2 * Simple Framebuffer Gfx/GUI lib
4 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
5 * Understanding is not required. Only obedience.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 module iv
.egra
/*is aliced*/;
21 private import arsd
.simpledisplay
;
23 public import iv
.egra
.gfx
;
24 public import iv
.egra
.gui
;
26 // so rdmd will know that we need it
27 static if (EGfxOpenGLBackend
) {
28 private import iv
.glbinds
;
34 // ////////////////////////////////////////////////////////////////////////// //
35 // set this to `true` if you have fullscreen window
36 public __gshared
bool egraSkipScreenClear
= false;
37 public __gshared
bool egraX11Direct
= false;
39 private __gshared
ubyte vbufNewScale
= 1; // new window scale
40 private __gshared
bool vbufVSync
= false;
41 private __gshared
bool vbufEffVSync
= false;
43 private __gshared
int lastWinWidth
, lastWinHeight
;
44 private __gshared
bool firstTimeInited
= false;
46 public class EgraDoConsoleCommandsEvent
{}
47 private __gshared EgraDoConsoleCommandsEvent evDoConCommands
;
49 private __gshared QuitEvent evQuitEvent
;
52 public void egraPostDoConCommands () {
54 if (vbwin
!is null && !vbwin
.eventQueued
!EgraDoConsoleCommandsEvent
) vbwin
.postEvent(evDoConCommands
);
57 public void postQuitEvent () {
59 if (vbwin
!is null && !vbwin
.eventQueued
!QuitEvent
) vbwin
.postEvent(evQuitEvent
);
62 //public int egraGetScale () nothrow @trusted @nogc { pragma(inline, true); return vbufNewScale; }
65 // ////////////////////////////////////////////////////////////////////////// //
66 shared static this () {
67 evDoConCommands
= new EgraDoConsoleCommandsEvent();
68 evQuitEvent
= new QuitEvent();
70 conRegVar
!vbufNewScale(1, 4, "v_scale", "window scale: [1..3]");
72 conRegVar
!bool("v_vsync", "sync to video refresh rate?",
73 (ConVarBase self
) => vbufVSync
,
74 (ConVarBase self
, bool nv
) {
75 static if (EGfxOpenGLBackend
) {
76 if (vbufVSync
!= nv
) {
84 vbufNewScale
= cast(ubyte)screenEffScale
;
85 vbufEffVSync
= vbufVSync
;
89 // ////////////////////////////////////////////////////////////////////////// //
90 public void egraProcessConsole () {
91 scope(exit
) if (!conQueueEmpty()) egraPostDoConCommands();
93 scope(exit
) consoleUnlock();
98 // ////////////////////////////////////////////////////////////////////////// //
99 public bool egraOnKey (KeyEvent event
) {
100 if (vbwin
is null) return false;
101 scope(exit
) if (!conQueueEmpty()) egraPostDoConCommands();
102 if (vbwin
.closed
) return false;
103 if (isQuitRequested()) postQuitEvent();
104 if (glconKeyEvent(event
)) {
108 if ((event
.modifierState
&ModifierState
.numLock
) == 0) {
110 case Key
.Pad0
: event
.key
= Key
.Insert
; break;
111 case Key
.Pad1
: event
.key
= Key
.End
; break;
112 case Key
.Pad2
: event
.key
= Key
.Down
; break;
113 case Key
.Pad3
: event
.key
= Key
.PageDown
; break;
114 case Key
.Pad4
: event
.key
= Key
.Left
; break;
115 //case Key.Pad5: event.key = Key.Insert; break;
116 case Key
.Pad6
: event
.key
= Key
.Right
; break;
117 case Key
.Pad7
: event
.key
= Key
.Home
; break;
118 case Key
.Pad8
: event
.key
= Key
.Up
; break;
119 case Key
.Pad9
: event
.key
= Key
.PageUp
; break;
120 case Key
.PadEnter
: event
.key
= Key
.Enter
; break;
121 case Key
.PadDot
: event
.key
= Key
.Delete
; break;
125 if (event
.key
== Key
.PadEnter
) event
.key
= Key
.Enter
;
127 if (dispatchEvent(event
)) return true;
128 //postScreenRepaint(); // just in case
133 // ////////////////////////////////////////////////////////////////////////// //
134 public bool egraOnMouse (MouseEvent event
) {
135 if (vbwin
is null) return false;
136 scope(exit
) if (!conQueueEmpty()) egraPostDoConCommands();
137 if (vbwin
.closed
) return false;
138 lastMouseXUnscaled
= event
.x
;
139 lastMouseYUnscaled
= event
.y
;
140 if (event
.type
== MouseEventType
.buttonPressed
) lastMouseButton |
= event
.button
;
141 else if (event
.type
== MouseEventType
.buttonReleased
) lastMouseButton
&= ~event
.button
;
143 if (dispatchEvent(event
)) return true;
148 // ////////////////////////////////////////////////////////////////////////// //
149 public bool egraOnChar (dchar ch
) {
150 if (vbwin
is null) return false;
151 scope(exit
) if (!conQueueEmpty()) egraPostDoConCommands();
152 if (vbwin
.closed
) return false;
153 if (glconCharEvent(ch
)) {
157 if (dispatchEvent(ch
)) return true;
162 // ////////////////////////////////////////////////////////////////////////// //
163 // vbwin.onFocusChange = &egraSdpyOnFocusChange;
164 public void egraSdpyOnFocusChange (bool focused
) {
168 eguiLostGlobalFocus();
176 //vbwin.windowResized = &egraSdpyOnWindowResized;
177 public void egraSdpyOnWindowResized (int wdt
, int hgt
) {
178 // TODO: fix gui sizes
179 if (vbwin
.closed
) return;
181 if (lastWinWidth
== wdt
&& lastWinHeight
== hgt
) return;
182 glconResize(wdt
, hgt
);
184 if (wdt
< vbufNewScale
*32) wdt
= vbufNewScale
;
185 if (hgt
< vbufNewScale
*32) hgt
= vbufNewScale
;
186 int newwdt
= (wdt
+vbufNewScale
-1)/vbufNewScale
;
187 int newhgt
= (hgt
+vbufNewScale
-1)/vbufNewScale
;
192 vglResizeBuffer(newwdt
, newhgt
, vbufNewScale
);
200 // return `true` to prevent backbuffer copying by sdpy
201 public bool egraX11Expose (int x
, int y
, int width
, int height
, int eventsLeft
) {
202 //conwriteln("EXPOSE! x=", x, "; y=", y, "; width=", width, "; height=", height, "; eventsLeft=", eventsLeft);
203 if (eventsLeft
== 0) egraRepaintScreen();
204 //return egraX11Direct;
209 // ////////////////////////////////////////////////////////////////////////// //
210 public SimpleWindow
egraCreateSystemWindow (string wintitle
, bool allowResize
=false) {
211 assert(vbwin
is null || vbwin
.closed
);
213 firstTimeInited
= false;
215 static if (EGfxOpenGLBackend
) {
216 vbwin
= new SimpleWindow(screenWidthScaled
, screenHeightScaled
, wintitle
, OpenGlOptions
.yes
, (allowResize ? Resizability
.allowResizing
: Resizability
.fixedSize
));
218 glconAllowOpenGLRender
= true;
220 vbwin
= new SimpleWindow(screenWidthScaled
, screenHeightScaled
, wintitle
, OpenGlOptions
.no
, (allowResize ? Resizability
.allowResizing
: Resizability
.fixedSize
));
223 version(egfx_opengl_backend
) {
225 vbwin
.handleExpose
= delegate (int x
, int y
, int width
, int height
, int eventsLeft
) {
226 return egraX11Expose(x
, y
, width
, height
, eventsLeft
);
228 XSetWindowBackground(vbwin
.impl
.display
, vbwin
.impl
.window
, 0);
231 vbwin
.visibleForTheFirstTime
= delegate () {
235 vbwin
.addEventListener((EgraDoConsoleCommandsEvent evt
) {
236 bool sendAnother
= false;
237 bool prevVisible
= isConsoleVisible
;
240 scope(exit
) consoleUnlock();
242 sendAnother
= !conQueueEmpty();
244 if (sendAnother
) egraPostDoConCommands();
245 if (vbwin
.closed
) return;
246 if (isQuitRequested()) postQuitEvent();
247 if (prevVisible || isConsoleVisible
) postScreenRepaintDelayed();
250 vbwin
.onFocusChange
= (bool focused
) { egraSdpyOnFocusChange(focused
); };
252 vbwin
.windowResized
= (int wdt
, int hgt
) { egraSdpyOnWindowResized(wdt
, hgt
); };
254 vbwin
.addEventListener((HideMouseEvent evt
) {
255 if (vbwin
.closed
) return;
256 if (isQuitRequested()) postQuitEvent();
257 if (repostHideMouse
) egraRepaintScreen();
260 vbwin
.addEventListener((ScreenRebuildEvent evt
) {
261 if (vbwin
.closed
) return;
262 if (isQuitRequested()) postQuitEvent();
264 if (isConsoleVisible
) postScreenRepaintDelayed();
267 vbwin
.addEventListener((ScreenRepaintEvent evt
) {
268 if (vbwin
.closed
) return;
269 if (isQuitRequested()) postQuitEvent();
271 if (isConsoleVisible
) postScreenRepaintDelayed();
274 vbwin
.addEventListener((CursorBlinkEvent evt
) {
275 if (vbwin
.closed
) return;
279 vbwin
.redrawOpenGlScene
= delegate () {
280 if (vbwin
.closed
) return;
288 // ////////////////////////////////////////////////////////////////////////// //
289 // call this in `vbwin.visibleForTheFirstTime()` to initialise GUI
290 // note that you cannot use EGUI with more than one window yet!
291 public void egraFirstTimeInit () {
292 if (firstTimeInited
) return;
293 firstTimeInited
= true;
295 vbufEffVSync
= vbufVSync
;
296 static if (EGfxOpenGLBackend
) {
297 assert(vbwin
!is null);
299 vbwin
.setAsCurrentOpenGlContext();
300 vbwin
.vsync
= vbufEffVSync
;
303 lastWinWidth
= screenWidthScaled
;
304 lastWinHeight
= screenHeightScaled
;
306 vglResizeBuffer(screenWidth
, screenHeight
);
307 vglCreateArrowTexture();
309 glconInit(screenWidthScaled
, screenHeightScaled
);
315 // ////////////////////////////////////////////////////////////////////////// //
316 // call this from `win.redrawOpenGlScene()`
317 public void egraOnGLRepaint () {
318 pragma(inline
, true);
319 egraDoRepaint(fromGLHandler
:true, doRebuild
:false);
323 // ////////////////////////////////////////////////////////////////////////// //
324 public void egraDoRepaint (immutable bool fromGLHandler
, bool doRebuild
) {
325 scope(exit
) if (isQuitRequested()) postQuitEvent();
326 if (vbwin
is null || vbwin
.closed
) return;
328 bool resizeWin
= false;
332 scope(exit
) consoleUnlock();
334 if (!conQueueEmpty()) egraPostDoConCommands();
336 if (vbufNewScale
!= screenEffScale
) {
337 // window scale changed
340 if (vbufEffVSync
!= vbufVSync
) {
341 vbufEffVSync
= vbufVSync
;
342 vbwin
.vsync
= vbufEffVSync
;
347 vbwin
.resize(screenWidthScaled
, screenHeightScaled
);
348 glconResize(screenWidthScaled
, screenHeightScaled
);
349 //vglResizeBuffer(screenWidth, screenHeight, vbufNewScale);
355 if (!egraSkipScreenClear
) gxClearScreen(0);
357 vglUpdateTexture(); // this does nothing for X11, but required for OpenGL
360 static if (EGfxOpenGLBackend
) {
361 if (!fromGLHandler
) vbwin
.setAsCurrentOpenGlContext();
364 static if (EGfxOpenGLBackend
) {
365 if (!fromGLHandler
) vbwin
.releaseCurrentOpenGlContext();
369 static if (EGfxOpenGLBackend
) {
372 //vglBlitTexture(vbwin, egraX11Direct);
375 if (vArrowTextureId
) {
376 if (isMouseVisible
) {
379 static if (EGfxOpenGLBackend
) {
380 glColor4f(1, 1, 1, mouseAlpha
);
382 vglBlitArrow(px
, py
);
386 static if (EGfxOpenGLBackend
) {
387 glconDrawWindow
= null;
388 glconDrawDirect
= false;
392 vglBlitTexture(vbwin
, true);
393 glconDrawWindow
= vbwin
;
394 glconDrawDirect
= true;
396 glconDrawWindow
= null;
398 auto painter
= vbwin
.draw();
399 vglBlitTexture(vbwin
, false);
400 glconDrawWindow
= vbwin
;
401 glconDrawDirect
= false;
403 glconDrawWindow
= null;
409 // ////////////////////////////////////////////////////////////////////////// //
410 public void egraRebuildScreen () {
411 if (vbwin
!is null && !vbwin
.closed
&& !vbwin
.hidden
) {
412 static if (EGfxOpenGLBackend
) {
415 if (!egraSkipScreenClear
) gxClearScreen(0);
417 vglUpdateTexture(); // this does nothing for X11, but required for OpenGL
419 vbwin
.redrawOpenGlSceneNow();
421 egraDoRepaint(fromGLHandler
:false, doRebuild
:true);
427 // ////////////////////////////////////////////////////////////////////////// //
428 public void egraRepaintScreen () {
429 __gshared
bool lastvisible
= false;
430 if (vbwin
!is null && !vbwin
.closed
&& !vbwin
.hidden
) {
431 bool curvisible
= isConsoleVisible
;
432 if (lastvisible
!= curvisible || curvisible
) {
433 lastvisible
= curvisible
;
437 static if (EGfxOpenGLBackend
) {
438 vbwin
.redrawOpenGlSceneNow();
440 egraDoRepaint(fromGLHandler
:false, doRebuild
:false);