redraw fixes
[amper.git] / egfx / backgl.d
blob4b3727743e6b9bcca5f33ac844b8c0a4707ec0ba
1 /* E-Mail Client
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module egfx.backgl is aliced;
19 private:
21 import arsd.simpledisplay;
23 import iv.cmdcon;
24 import iv.cmdcongl;
25 //import iv.glbinds : glTexParameterfv; // rdmd hack
27 import egfx.base;
30 // ////////////////////////////////////////////////////////////////////////// //
31 /*public*/ enum GLTexType = GL_BGRA;
32 //public enum GLTexType = GL_RGBA;
35 // ////////////////////////////////////////////////////////////////////////// //
36 public __gshared uint vglTexId; // OpenGL texture id
37 public __gshared uint vArrowTextureId = 0;
40 // ////////////////////////////////////////////////////////////////////////// //
41 shared static this () {
42 import core.stdc.stdlib : malloc;
43 vglTexBuf = cast(uint*)malloc((VBufWidth*VBufHeight+4)*4);
44 if (vglTexBuf is null) assert(0, "out of memory!");
45 vglTexBuf[0..VBufWidth*VBufHeight+4] = 0;
49 // ////////////////////////////////////////////////////////////////////////// //
50 public void vglCreateArrowTexture () {
51 //import iv.glbinds;
53 enum wrapOpt = GL_REPEAT;
54 enum filterOpt = GL_NEAREST; //GL_LINEAR;
55 enum ttype = GL_UNSIGNED_BYTE;
57 if (vArrowTextureId) glDeleteTextures(1, &vArrowTextureId);
58 vArrowTextureId = 0;
59 glGenTextures(1, &vArrowTextureId);
60 if (vArrowTextureId == 0) assert(0, "can't create arrow texture");
62 //GLint gltextbinding;
63 //glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
64 //scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
66 glBindTexture(GL_TEXTURE_2D, vArrowTextureId);
67 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
68 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
69 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
70 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
71 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
72 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
74 //GLfloat[4] bclr = 0.0;
75 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
77 uint[16*8] pmap = 0x00_000000U;
78 // sprite,sprite,mask,mask
79 static immutable ushort[$] spx = [
80 0b11111111_10000000, 0b00000000_01111111,
81 0b01000000_10000000, 0b10000000_01111111,
82 0b00100000_10000000, 0b11000000_01111111,
83 0b00010000_01100000, 0b11100000_00011111,
84 0b00001001_10011000, 0b11110000_00000111,
85 0b00000110_01100110, 0b11111001_10000001,
86 0b00000000_00011001, 0b11111111_11100000,
87 0b00000000_00000110, 0b11111111_11111001,
90 foreach (immutable dy; 0..8) {
91 ushort spr = spx[dy*2+0];
92 ushort msk = spx[dy*2+1];
93 foreach (immutable dx; 0..16) {
94 if ((msk&0x8000) == 0) {
95 pmap[dy*16+dx] = (spr&0x8000 ? 0xff_ffffffU : 0xff_000000U);
97 msk <<= 1;
98 spr <<= 1;
101 //pmap = 0xff_ff0000U;
102 //pmap[0] = 0;
104 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 8, 0, GLTexType, GL_UNSIGNED_BYTE, pmap.ptr);
108 // ////////////////////////////////////////////////////////////////////////// //
109 public void vglBlitArrow (int px, int py) {
110 if (vArrowTextureId != 0) {
111 glMatrixMode(GL_PROJECTION); // for ortho camera
112 glLoadIdentity();
113 // left, right, bottom, top, near, far
114 //glViewport(0, 0, w*vbufEffScale, h*vbufEffScale);
115 //glOrtho(0, w, h, 0, -1, 1); // top-to-bottom
116 glViewport(0, 0, VBufWidth*vbufEffScale, VBufHeight*vbufEffScale);
117 glOrtho(0, VBufWidth, VBufHeight, 0, -1, 1); // top-to-bottom
118 glMatrixMode(GL_MODELVIEW);
119 glLoadIdentity();
121 glEnable(GL_TEXTURE_2D);
122 glDisable(GL_LIGHTING);
123 glDisable(GL_DITHER);
124 glDisable(GL_DEPTH_TEST);
126 glEnable(GL_BLEND);
127 //glBlendFunc(GL_SRC_ALPHA, GL_ONE);
128 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
129 //glColor4f(1, 1, 1, 1);
130 glBindTexture(GL_TEXTURE_2D, vArrowTextureId);
131 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
132 glBegin(GL_QUADS);
133 glTexCoord2f(0.0f, 0.0f); glVertex2i(px, py); // top-left
134 glTexCoord2f(1.0f, 0.0f); glVertex2i(px+16, py); // top-right
135 glTexCoord2f(1.0f, 1.0f); glVertex2i(px+16, py+8); // bottom-right
136 glTexCoord2f(0.0f, 1.0f); glVertex2i(px, py+8); // bottom-left
137 glEnd();
142 // ////////////////////////////////////////////////////////////////////////// //
143 // resize buffer, reinitialize OpenGL texture
144 public void vglResizeBuffer (int wdt, int hgt, int ascale=1) {
145 //import iv.glbinds;
147 if (wdt < 1) wdt = 1;
148 if (hgt < 1) hgt = 1;
150 if (wdt > 8192) wdt = 8192;
151 if (hgt > 8192) hgt = 8192;
153 bool sizeChanged = (wdt != VBufWidth || hgt != VBufHeight);
154 VBufWidth = wdt;
155 VBufHeight = hgt;
157 if (vglTexBuf is null || sizeChanged) {
158 import core.stdc.stdlib : realloc;
159 vglTexBuf = cast(uint*)realloc(vglTexBuf, (VBufWidth*VBufHeight+4)*vglTexBuf[0].sizeof);
160 if (vglTexBuf is null) assert(0, "out of memory!");
161 vglTexBuf[0..VBufWidth*VBufHeight+4] = 0;
164 if (vglTexId == 0 || sizeChanged) {
165 enum wrapOpt = GL_REPEAT;
166 enum filterOpt = GL_NEAREST; //GL_LINEAR;
167 enum ttype = GL_UNSIGNED_BYTE;
169 if (vglTexId) glDeleteTextures(1, &vglTexId);
170 vglTexId = 0;
171 glGenTextures(1, &vglTexId);
172 if (vglTexId == 0) assert(0, "can't create OpenGL texture");
174 //GLint gltextbinding;
175 //glGetIntegerv(GL_TEXTURE_BINDING_2D, &gltextbinding);
176 //scope(exit) glBindTexture(GL_TEXTURE_2D, gltextbinding);
178 glBindTexture(GL_TEXTURE_2D, vglTexId);
179 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapOpt);
180 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapOpt);
181 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterOpt);
182 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterOpt);
183 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
184 //glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
186 //GLfloat[4] bclr = 0.0;
187 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
189 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VBufWidth, VBufHeight, 0, GLTexType, GL_UNSIGNED_BYTE, vglTexBuf);
192 if (ascale < 1) ascale = 1;
193 if (ascale > 32) ascale = 32;
194 vbufEffScale = cast(ubyte)ascale;
198 // ////////////////////////////////////////////////////////////////////////// //
199 public void vglUpdateTexture () {
200 if (vglTexId != 0) {
201 glBindTexture(GL_TEXTURE_2D, vglTexId);
202 glTexSubImage2D(GL_TEXTURE_2D, 0, 0/*x*/, 0/*y*/, VBufWidth, VBufHeight, GLTexType, GL_UNSIGNED_BYTE, vglTexBuf);
203 //glBindTexture(GL_TEXTURE_2D, 0);
208 // ////////////////////////////////////////////////////////////////////////// //
209 public void vglBlitTexture () {
210 if (vglTexId != 0) {
211 glMatrixMode(GL_PROJECTION); // for ortho camera
212 glLoadIdentity();
213 // left, right, bottom, top, near, far
214 //glViewport(0, 0, w*vbufEffScale, h*vbufEffScale);
215 //glOrtho(0, w, h, 0, -1, 1); // top-to-bottom
216 glViewport(0, 0, VBufWidth*vbufEffScale, VBufHeight*vbufEffScale);
217 glOrtho(0, VBufWidth, VBufHeight, 0, -1, 1); // top-to-bottom
218 glMatrixMode(GL_MODELVIEW);
219 glLoadIdentity();
221 glEnable(GL_TEXTURE_2D);
222 glDisable(GL_LIGHTING);
223 glDisable(GL_DITHER);
224 //glDisable(GL_BLEND);
225 glDisable(GL_DEPTH_TEST);
226 //glEnable(GL_BLEND);
227 //glBlendFunc(GL_SRC_ALPHA, GL_ONE);
228 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
229 glDisable(GL_BLEND);
230 //glDisable(GL_STENCIL_TEST);
232 if (vglTexId) {
233 immutable w = VBufWidth;
234 immutable h = VBufHeight;
235 //conwriteln("w=", w, "; h=", h, "; scale=", vbufEffScale);
237 glColor4f(1, 1, 1, 1);
238 glBindTexture(GL_TEXTURE_2D, vglTexId);
239 //scope(exit) glBindTexture(GL_TEXTURE_2D, 0);
240 glBegin(GL_QUADS);
241 glTexCoord2f(0.0f, 0.0f); glVertex2i(0, 0); // top-left
242 glTexCoord2f(1.0f, 0.0f); glVertex2i(w, 0); // top-right
243 glTexCoord2f(1.0f, 1.0f); glVertex2i(w, h); // bottom-right
244 glTexCoord2f(0.0f, 1.0f); glVertex2i(0, h); // bottom-left
245 glEnd();
251 // ////////////////////////////////////////////////////////////////////////// //
252 struct EGfxWindowData {
253 uint vglTexIdSave;
254 int VBufWidthSave;
255 int VBufHeightSave;
256 uint* vglTexBufSave;
257 ubyte vbufEffScaleSave;
258 GxRect gxClipRectSave;
260 void clear () {
261 if (vglTexIdSave != 0) {
262 glDeleteTextures(1, &vglTexIdSave);
263 vglTexIdSave = 0;
265 if (vglTexBufSave !is null) {
266 import core.stdc.stdlib : free;
267 free(vglTexBufSave);
268 vglTexBufSave = null;
270 VBufWidthSave = VBufHeightSave = 0;
271 vbufEffScaleSave = 1;
274 static auto setup (SimpleWindow w) {
275 static struct WDRest {
276 private:
277 SimpleWindow w;
278 SimpleWindow oldcw;
279 public:
280 @property bool valid () const pure nothrow @safe @nogc { pragma(inline, true); return (w !is null); }
281 @property SimpleWindow sw () pure nothrow @safe @nogc { pragma(inline, true); return w; }
282 @property EGfxWindowData* wd () { pragma(inline, true); return (w is null ? null : w in wdata); }
283 this (SimpleWindow aw) {
284 if (aw !is null && !aw.closed) {
285 if (auto sw = aw in wdata) {
286 w = aw;
287 oldcw = curwin;
288 if (curwin !is aw) {
289 //conwriteln("saving old context");
290 curwin = aw;
292 EGfxWindowData* rd = (oldcw !is null ? oldcw in wdata : null);
293 if (rd is null) {
294 if (oldcw is null) {
295 savedData.vglTexIdSave = vglTexId;
296 savedData.VBufWidthSave = VBufWidth;
297 savedData.VBufHeightSave = VBufHeight;
298 savedData.vbufEffScaleSave = vbufEffScale;
299 savedData.vglTexBufSave = vglTexBuf;
300 savedData.gxClipRectSave = gxClipRect;
302 } else {
303 rd.vglTexIdSave = vglTexId;
304 rd.VBufWidthSave = VBufWidth;
305 rd.VBufHeightSave = VBufHeight;
306 rd.vbufEffScaleSave = vbufEffScale;
307 rd.vglTexBufSave = vglTexBuf;
308 rd.gxClipRectSave = gxClipRect;
311 vglTexId = sw.vglTexIdSave;
312 VBufWidth = sw.VBufWidthSave;
313 VBufHeight = sw.VBufHeightSave;
314 vbufEffScale = sw.vbufEffScaleSave;
315 vglTexBuf = sw.vglTexBufSave;
316 gxClipReset();
318 vglResizeBuffer(w.width/vbufEffScale, w.height/vbufEffScale, vbufEffScale);
319 } else {
320 //conwriteln("same context");
322 gxClipReset();
326 @disable this (this);
327 ~this () {
328 if (w !is null) {
329 if (auto sw = w in wdata) {
330 assert(curwin is w);
331 if (oldcw !is w) {
332 //conwriteln("restoring old context");
333 curwin = oldcw;
335 sw.vglTexIdSave = vglTexId;
336 sw.VBufWidthSave = VBufWidth;
337 sw.VBufHeightSave = VBufHeight;
338 sw.vbufEffScaleSave = vbufEffScale;
339 sw.vglTexBufSave = vglTexBuf;
341 if (w.closed) { sw.clear(); wdata.remove(w); }
343 EGfxWindowData* rd = (oldcw !is null ? oldcw in wdata : null);
344 if (rd is null) {
345 if (oldcw is null) {
346 vglTexId = savedData.vglTexIdSave;
347 VBufWidth = savedData.VBufWidthSave;
348 VBufHeight = savedData.VBufHeightSave;
349 vbufEffScale = savedData.vbufEffScaleSave;
350 vglTexBuf = savedData.vglTexBufSave;
351 gxClipRect = savedData.gxClipRectSave;
353 } else {
354 vglTexId = rd.vglTexIdSave;
355 VBufWidth = rd.VBufWidthSave;
356 VBufHeight = rd.VBufHeightSave;
357 vbufEffScale = rd.vbufEffScaleSave;
358 vglTexBuf = rd.vglTexBufSave;
360 } else {
361 //conwriteln("skip restoring same context");
367 assert(w !is null);
368 return WDRest(w);
371 // old event handlers
372 void delegate () redrawOpenGlSceneSave;
373 void delegate (KeyEvent ke) handleKeyEventSave;
374 void delegate (dchar c) handleCharEventSave;
375 void delegate () handlePulseSave;
376 void delegate (MouseEvent) handleMouseEventSave;
377 void delegate (int width, int height) windowResizedSave;
380 __gshared EGfxWindowData[SimpleWindow] wdata; // yes, this anchors
381 __gshared EGfxWindowData savedData;
382 __gshared SimpleWindow curwin;
385 // call this in `visibleForTheFirstTime()`, so it can capture handlers
386 public void vglRegisterWindow (SimpleWindow w) {
387 if (w is null || w.closed) return;
388 if (w in wdata) return;
390 EGfxWindowData svd;
392 w.setAsCurrentOpenGlContext();
393 if (glconMainWindow is w) glconInit(w.width, w.height);
394 //vglResizeBuffer(sdwin.width, sdwin.height);
395 //gxRebuildScreen();
397 svd.vglTexIdSave = 0;
398 svd.VBufWidthSave = 0;
399 svd.VBufHeightSave = 0;
400 svd.vglTexBufSave = null;
401 svd.vbufEffScaleSave = vbufEffScale;
403 // create texture and backbuffer
404 { auto save = EGfxWindowData.setup(w); }
406 svd.redrawOpenGlSceneSave = w.redrawOpenGlScene;
407 w.redrawOpenGlScene = delegate () {
408 w.setAsCurrentOpenGlContext();
409 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
411 auto save = EGfxWindowData.setup(w);
412 if (auto sw = save.wd) {
413 if (sw.redrawOpenGlSceneSave !is null) {
414 gxClipReset();
415 sw.redrawOpenGlSceneSave();
416 w.setAsCurrentOpenGlContext();
417 vglUpdateTexture();
418 vglBlitTexture();
422 if (glconMainWindow is w && !w.closed) {
423 //if (isConsoleVisible) conwriteln("VISIBLE");
424 glconResize(w.width, w.height);
425 glconDraw();
429 svd.handleKeyEventSave = w.handleKeyEvent;
430 w.handleKeyEvent = delegate (KeyEvent event) {
431 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
432 if (glconMainWindow is w && glconKeyEvent(event)) { glconPostScreenRepaint(); return; }
433 if (isQuitRequested) w.close();
435 auto save = EGfxWindowData.setup(w);
436 if (auto sw = save.wd) {
437 if (sw.handleKeyEventSave !is null) sw.handleKeyEventSave(event);
442 svd.handleMouseEventSave = w.handleMouseEvent;
443 w.handleMouseEvent = delegate (MouseEvent event) {
444 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
445 if (isQuitRequested) w.close();
447 auto save = EGfxWindowData.setup(w);
448 if (auto sw = save.wd) {
449 event.x /= vbufEffScale;
450 event.y /= vbufEffScale;
451 if (sw.handleMouseEventSave !is null) sw.handleMouseEventSave(event);
456 svd.handleCharEventSave = w.handleCharEvent;
457 w.handleCharEvent = delegate (dchar ch) {
458 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
459 if (glconMainWindow is w && glconCharEvent(ch)) { glconPostScreenRepaint(); return; }
460 if (isQuitRequested) w.close();
462 auto save = EGfxWindowData.setup(w);
463 if (auto sw = save.wd) {
464 if (sw.handleCharEventSave !is null) sw.handleCharEventSave(ch);
469 svd.handlePulseSave = w.handlePulse;
470 w.handlePulse = delegate () {
471 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
472 if (isQuitRequested) w.close();
474 auto save = EGfxWindowData.setup(w);
475 if (auto sw = save.wd) {
476 if (sw.handlePulseSave !is null) sw.handlePulseSave();
481 svd.windowResizedSave = w.windowResized;
482 w.windowResized = delegate (int wdt, int hgt) {
483 scope(exit) if (!conQueueEmpty()) glconPostDoConCommands();
485 auto save = EGfxWindowData.setup(w);
486 if (auto sw = save.wd) {
487 if (!w.closed) {
488 //w.setMinSize(wdt, hgt);
489 //w.setMaxSize(wdt, hgt);
490 if (glconMainWindow is w) glconResize(wdt, hgt);
491 vglResizeBuffer(wdt/vbufEffScale, hgt/vbufEffScale, vbufEffScale);
492 if (sw.windowResizedSave !is null) sw.windowResizedSave(wdt, hgt);
498 wdata[w] = svd;
499 if (glconMainWindow is w) glconResize(w.width, w.height);
500 w.redrawOpenGlSceneNow();
504 public void vglCloseWindow (SimpleWindow w) {
505 if (w is null) return;
506 auto sw = w in wdata;
507 if (sw !is null) {
508 if (!w.closed) w.close();
509 sw.clear();
510 wdata.remove(w);
515 public ubyte vglWindowScale (SimpleWindow w) {
516 if (w is null || w.closed) return 1;
517 auto sw = w in wdata;
518 if (sw !is null) return sw.vbufEffScaleSave;
519 return 1;
523 public ubyte vglScaleWindow (SimpleWindow w, int newscale) {
524 if (newscale < 1) newscale = 1;
525 if (newscale > 32) newscale = 32;
526 if (w is null || w.closed) return cast(ubyte)newscale;
527 auto sw = w in wdata;
528 if (sw !is null) {
529 if (sw.vbufEffScaleSave != newscale) {
530 sw.vbufEffScaleSave = cast(ubyte)newscale;
531 int wdt = sw.VBufWidthSave*newscale;
532 int hgt = sw.VBufHeightSave*newscale;
533 w.resize(wdt, hgt);
536 return cast(ubyte)newscale;
540 public void vglPaintWith (SimpleWindow w, scope void delegate () paintcb) {
541 if (w is null) return;
542 auto save = EGfxWindowData.setup(w);
543 if (auto sw = save.wd) {
544 if (!w.closed) {
545 if (paintcb !is null) paintcb();
546 if (!w.closed) w.redrawOpenGlSceneNow();