egra: added delegate to check widget hotkeys
[iv.d.git] / egra / package.d
blob00571efb8c4531d0d7e7882f188200c710b263f8
1 /*
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;
30 import iv.cmdcon;
31 import iv.cmdcongl;
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 () {
53 pragma(inline, true);
54 if (vbwin !is null && !vbwin.eventQueued!EgraDoConsoleCommandsEvent) vbwin.postEvent(evDoConCommands);
57 public void postQuitEvent () {
58 pragma(inline, true);
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) {
77 vbufVSync = nv;
78 postScreenRepaint();
84 vbufNewScale = cast(ubyte)screenEffScale;
85 vbufEffVSync = vbufVSync;
89 // ////////////////////////////////////////////////////////////////////////// //
90 public void egraProcessConsole () {
91 scope(exit) if (!conQueueEmpty()) egraPostDoConCommands();
92 consoleLock();
93 scope(exit) consoleUnlock();
94 conProcessQueue();
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)) {
105 postScreenRepaint();
106 return true;
108 if ((event.modifierState&ModifierState.numLock) == 0) {
109 switch (event.key) {
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;
122 default: break;
124 } else {
125 if (event.key == Key.PadEnter) event.key = Key.Enter;
127 if (dispatchEvent(event)) return true;
128 //postScreenRepaint(); // just in case
129 return false;
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;
142 egraMouseMoved();
143 if (dispatchEvent(event)) return true;
144 return false;
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)) {
154 postScreenRepaint();
155 return true;
157 if (dispatchEvent(ch)) return true;
158 return false;
162 // ////////////////////////////////////////////////////////////////////////// //
163 // vbwin.onFocusChange = &egraSdpyOnFocusChange;
164 public void egraSdpyOnFocusChange (bool focused) {
165 vbfocused = focused;
166 if (!focused) {
167 lastMouseButton = 0;
168 eguiLostGlobalFocus();
169 } else {
170 lastMouseButton = 0;
172 postScreenRebuild();
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;
189 lastWinWidth = wdt;
190 lastWinHeight = hgt;
192 vglResizeBuffer(newwdt, newhgt, vbufNewScale);
194 egraMouseMoved();
196 egraRebuildScreen();
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;
205 return true;
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));
217 vbwin.hideCursor();
218 glconAllowOpenGLRender = true;
219 } else {
220 vbwin = new SimpleWindow(screenWidthScaled, screenHeightScaled, wintitle, OpenGlOptions.no, (allowResize ? Resizability.allowResizing : Resizability.fixedSize));
223 version(egfx_opengl_backend) {
224 } else {
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 () {
232 egraFirstTimeInit();
235 vbwin.addEventListener((EgraDoConsoleCommandsEvent evt) {
236 bool sendAnother = false;
237 bool prevVisible = isConsoleVisible;
239 consoleLock();
240 scope(exit) consoleUnlock();
241 conProcessQueue();
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();
263 egraRebuildScreen();
264 if (isConsoleVisible) postScreenRepaintDelayed();
267 vbwin.addEventListener((ScreenRepaintEvent evt) {
268 if (vbwin.closed) return;
269 if (isQuitRequested()) postQuitEvent();
270 egraRepaintScreen();
271 if (isConsoleVisible) postScreenRepaintDelayed();
274 vbwin.addEventListener((CursorBlinkEvent evt) {
275 if (vbwin.closed) return;
276 egraRebuildScreen();
279 vbwin.redrawOpenGlScene = delegate () {
280 if (vbwin.closed) return;
281 egraOnGLRepaint();
284 return vbwin;
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);
298 import iv.glbinds;
299 vbwin.setAsCurrentOpenGlContext();
300 vbwin.vsync = vbufEffVSync;
303 lastWinWidth = screenWidthScaled;
304 lastWinHeight = screenHeightScaled;
306 vglResizeBuffer(screenWidth, screenHeight);
307 vglCreateArrowTexture();
309 glconInit(screenWidthScaled, screenHeightScaled);
311 egraRebuildScreen();
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;
331 consoleLock();
332 scope(exit) consoleUnlock();
334 if (!conQueueEmpty()) egraPostDoConCommands();
336 if (vbufNewScale != screenEffScale) {
337 // window scale changed
338 resizeWin = true;
340 if (vbufEffVSync != vbufVSync) {
341 vbufEffVSync = vbufVSync;
342 vbwin.vsync = vbufEffVSync;
346 if (resizeWin) {
347 vbwin.resize(screenWidthScaled, screenHeightScaled);
348 glconResize(screenWidthScaled, screenHeightScaled);
349 //vglResizeBuffer(screenWidth, screenHeight, vbufNewScale);
350 doRebuild = true;
353 if (doRebuild) {
354 gxClipReset();
355 if (!egraSkipScreenClear) gxClearScreen(0);
356 paintSubWindows();
357 vglUpdateTexture(); // this does nothing for X11, but required for OpenGL
360 static if (EGfxOpenGLBackend) {
361 if (!fromGLHandler) vbwin.setAsCurrentOpenGlContext();
363 scope(exit) {
364 static if (EGfxOpenGLBackend) {
365 if (!fromGLHandler) vbwin.releaseCurrentOpenGlContext();
369 static if (EGfxOpenGLBackend) {
370 vglBlitTexture();
371 } else {
372 //vglBlitTexture(vbwin, egraX11Direct);
375 if (vArrowTextureId) {
376 if (isMouseVisible) {
377 int px = lastMouseX;
378 int py = lastMouseY;
379 static if (EGfxOpenGLBackend) {
380 glColor4f(1, 1, 1, mouseAlpha);
382 vglBlitArrow(px, py);
386 static if (EGfxOpenGLBackend) {
387 glconDrawWindow = null;
388 glconDrawDirect = false;
389 glconDraw();
390 } else {
391 if (egraX11Direct) {
392 vglBlitTexture(vbwin, true);
393 glconDrawWindow = vbwin;
394 glconDrawDirect = true;
395 glconDraw();
396 glconDrawWindow = null;
397 } else {
398 auto painter = vbwin.draw();
399 vglBlitTexture(vbwin, false);
400 glconDrawWindow = vbwin;
401 glconDrawDirect = false;
402 glconDraw();
403 glconDrawWindow = null;
409 // ////////////////////////////////////////////////////////////////////////// //
410 public void egraRebuildScreen () {
411 if (vbwin !is null && !vbwin.closed && !vbwin.hidden) {
412 static if (EGfxOpenGLBackend) {
413 // rebuild
414 gxClipReset();
415 if (!egraSkipScreenClear) gxClearScreen(0);
416 paintSubWindows();
417 vglUpdateTexture(); // this does nothing for X11, but required for OpenGL
418 // and redraw
419 vbwin.redrawOpenGlSceneNow();
420 } else {
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;
434 egraRebuildScreen();
435 return;
437 static if (EGfxOpenGLBackend) {
438 vbwin.redrawOpenGlSceneNow();
439 } else {
440 egraDoRepaint(fromGLHandler:false, doRebuild:false);