DEADSOFTWARE

FlexUI: button control; slightly changed event consuming logic
[d2df-sdl.git] / src / gx / glgfx.pas
1 (* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *)
17 {$INCLUDE ../shared/a_modes.inc}
18 unit glgfx;
20 interface
22 uses
23 SysUtils, Classes,
24 GL, GLExt, SDL2,
25 sdlcarcass;
28 // ////////////////////////////////////////////////////////////////////////// //
29 type
30 TGxRGBA = packed record
31 public
32 r, g, b, a: Byte;
34 public
35 constructor Create (ar, ag, ab: Integer; aa: Integer=255);
37 function asUInt (): LongWord; inline;
38 function isOpaque (): Boolean; inline;
39 function isTransparent (): Boolean; inline;
41 // WARNING! This function does blending in RGB space, and RGB space is not linear!
42 // alpha value of `self` doesn't matter
43 // `aa` means: 255 for replace color, 0 for keep `self`
44 function blend (ar, ag, ab, aa: Integer): TGxRGBA; inline;
45 end;
48 // ////////////////////////////////////////////////////////////////////////// //
49 type
50 THMouseEvent = record
51 public
52 const
53 // both for but and for bstate
54 None = 0;
55 Left = $0001;
56 Right = $0002;
57 Middle = $0004;
58 WheelUp = $0008;
59 WheelDown = $0010;
61 // event types
62 type
63 TKind = (Release, Press, Motion);
65 private
66 mEaten: Boolean;
67 mCancelled: Boolean;
69 public
70 kind: TKind; // motion, press, release
71 x, y: Integer; // current mouse position
72 dx, dy: Integer; // for wheel this is wheel motion, otherwise this is relative mouse motion
73 but: Word; // current pressed/released button, or 0 for motion
74 bstate: Word; // button state BEFORE event (i.e. press/release modifications aren't done yet)
75 kstate: Word; // keyboard state (see THKeyEvent);
77 public
78 procedure intrInit (); inline; // init hidden fields
80 function press (): Boolean; inline;
81 function release (): Boolean; inline;
82 function motion (): Boolean; inline;
83 procedure eat (); inline;
84 procedure cancel (); inline;
86 public
87 property eaten: Boolean read mEaten;
88 property cancelled: Boolean read mCancelled;
89 end;
91 THKeyEvent = record
92 public
93 const
94 // modifiers
95 ModCtrl = $0001;
96 ModAlt = $0002;
97 ModShift = $0004;
98 ModHyper = $0008;
100 // event types
101 type
102 TKind = (Release, Press);
104 private
105 mEaten: Boolean;
106 mCancelled: Boolean;
108 public
109 kind: TKind;
110 scan: Word; // SDL_SCANCODE_XXX
111 sym: LongWord; // SDLK_XXX
112 x, y: Integer; // current mouse position
113 bstate: Word; // button state
114 kstate: Word; // keyboard state BEFORE event (i.e. press/release modifications aren't done yet)
116 public
117 procedure intrInit (); inline; // init hidden fields
119 function press (): Boolean; inline;
120 function release (): Boolean; inline;
121 procedure eat (); inline;
122 procedure cancel (); inline;
124 public
125 property eaten: Boolean read mEaten;
126 property cancelled: Boolean read mCancelled;
127 end;
131 // ////////////////////////////////////////////////////////////////////////// //
132 // setup 2D OpenGL mode; will be called automatically in `glInit()`
133 procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
135 type
136 TScissorSave = record
137 public
138 wassc: Boolean;
139 scxywh: packed array[0..3] of GLint;
141 public
143 public
144 procedure save (enableScissoring: Boolean);
145 procedure restore ();
147 // set new scissor rect, bounded by the saved scissor rect
148 procedure combineRect (x, y, w, h: Integer);
149 end;
152 procedure oglDrawCursor ();
153 procedure oglDrawCursorAt (msX, msY: Integer);
155 // return `false` if destination rect is empty
156 // modifies rect0
157 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean;
159 procedure normRGBA (var r, g, b, a: Integer); inline;
160 function setupGLColor (r, g, b, a: Integer): Boolean;
161 function setupGLColor (constref clr: TGxRGBA): Boolean;
162 function isScaled (): Boolean;
164 function textWidth6 (const s: AnsiString): Integer;
165 function textWidth8 (const s: AnsiString): Integer;
166 // return width (including last empty pixel)
167 function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; constref clr: TGxRGBA; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
168 procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
169 procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
170 procedure drawHLine (x, y, len: Integer; constref clr: TGxRGBA);
171 procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
172 procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
173 procedure darkenRect (x, y, w, h: Integer; a: Integer);
174 procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
175 function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
176 function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
177 function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
178 function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
179 // x-centered at `x`
180 function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
181 function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
182 function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
183 function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
186 // ////////////////////////////////////////////////////////////////////////// //
187 // event handlers
188 var
189 evMouseCB: procedure (var ev: THMouseEvent) = nil;
190 evKeyCB: procedure (var ev: THKeyEvent) = nil;
193 // ////////////////////////////////////////////////////////////////////////// //
194 function getMouseX (): Integer; inline;
195 function getMouseY (): Integer; inline;
196 function getButState (): Word; inline;
197 function getModState (): Word; inline;
200 // ////////////////////////////////////////////////////////////////////////// //
201 property
202 gMouseX: Integer read getMouseX;
203 gMouseY: Integer read getMouseY;
204 gButState: Word read getButState;
205 gModState: Word read getModState;
207 var
208 gGfxDoClear: Boolean = true;
211 // ////////////////////////////////////////////////////////////////////////// //
212 // any mods = 255: nothing was defined
213 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
215 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
216 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
218 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
219 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
222 implementation
225 var
226 curButState: Word = 0;
227 curModState: Word = 0;
228 curMsX: Integer = 0;
229 curMsY: Integer = 0;
232 // ////////////////////////////////////////////////////////////////////////// //
233 function strEquCI (const s0, s1: AnsiString): Boolean;
234 var
235 f: Integer;
236 c0, c1: AnsiChar;
237 begin
238 result := (Length(s0) = Length(s1));
239 if result then
240 begin
241 for f := 1 to Length(s0) do
242 begin
243 c0 := s0[f];
244 if (c0 >= 'a') and (c0 <= 'z') then Dec(c0, 32); // poor man's `toupper()`
245 c1 := s1[f];
246 if (c1 >= 'a') and (c1 <= 'z') then Dec(c1, 32); // poor man's `toupper()`
247 if (c0 <> c1) then begin result := false; exit; end;
248 end;
249 end;
250 end;
253 // ////////////////////////////////////////////////////////////////////////// //
254 function getMouseX (): Integer; inline; begin result := curMsX; end;
255 function getMouseY (): Integer; inline; begin result := curMsY; end;
256 function getButState (): Word; inline; begin result := curButState; end;
257 function getModState (): Word; inline; begin result := curModState; end;
260 // ////////////////////////////////////////////////////////////////////////// //
261 procedure THMouseEvent.intrInit (); inline; begin mEaten := false; mCancelled := false; end;
262 function THMouseEvent.press (): Boolean; inline; begin result := (kind = TKind.Press); end;
263 function THMouseEvent.release (): Boolean; inline; begin result := (kind = TKind.Release); end;
264 function THMouseEvent.motion (): Boolean; inline; begin result := (kind = TKind.Motion); end;
265 procedure THMouseEvent.eat (); inline; begin mEaten := true; end;
266 procedure THMouseEvent.cancel (); inline; begin mCancelled := true; end;
268 procedure THKeyEvent.intrInit (); inline; begin mEaten := false; mCancelled := false; end;
269 function THKeyEvent.press (): Boolean; inline; begin result := (kind = TKind.Press); end;
270 function THKeyEvent.release (): Boolean; inline; begin result := (kind = TKind.Release); end;
271 procedure THKeyEvent.eat (); inline; begin mEaten := true; end;
272 procedure THKeyEvent.cancel (); inline; begin mCancelled := true; end;
275 // ////////////////////////////////////////////////////////////////////////// //
276 constructor TGxRGBA.Create (ar, ag, ab: Integer; aa: Integer=255);
277 begin
278 if (ar < 0) then r := 0 else if (ar > 255) then r := 255 else r := Byte(ar);
279 if (ag < 0) then g := 0 else if (ag > 255) then g := 255 else g := Byte(ag);
280 if (ab < 0) then b := 0 else if (ab > 255) then b := 255 else b := Byte(ab);
281 if (aa < 0) then a := 0 else if (aa > 255) then a := 255 else a := Byte(aa);
282 end;
284 function TGxRGBA.asUInt (): LongWord; inline; begin result := LongWord(r) or (LongWord(g) shl 8) or (LongWord(b) shl 16) or (LongWord(a) shl 24); end;
286 function TGxRGBA.isOpaque (): Boolean; inline; begin result := (a = 255); end;
287 function TGxRGBA.isTransparent (): Boolean; inline; begin result := (a = 0); end;
289 function TGxRGBA.blend (ar, ag, ab, aa: Integer): TGxRGBA; inline;
290 var
291 me, it, a_tmp_, dc_tmp_, srb_tmp_, sg_tmp_, drb_tmp_, dg_tmp_, orb_tmp_, og_tmp_: LongWord;
292 begin
293 if (aa <= 0) then begin result := self; exit; end;
294 result := TGxRGBA.Create(ar, ag, ab, aa);
295 if (aa >= 255) then begin result.a := a; exit; end;
296 me := asUInt;
297 it := result.asUInt;
298 a_tmp_ := (256-(255-(it shr 24))) and (-(1-(((255-(it shr 24))+1) shr 8))); // to not loose bits, but 255 should become 0
299 dc_tmp_ := me and $ffffff;
300 srb_tmp_ := (it and $ff00ff);
301 sg_tmp_ := (it and $00ff00);
302 drb_tmp_ := (dc_tmp_ and $ff00ff);
303 dg_tmp_ := (dc_tmp_ and $00ff00);
304 orb_tmp_ := (drb_tmp_+(((srb_tmp_-drb_tmp_)*a_tmp_+$800080) shr 8)) and $ff00ff;
305 og_tmp_ := (dg_tmp_+(((sg_tmp_-dg_tmp_)*a_tmp_+$008000) shr 8)) and $00ff00;
306 me := (orb_tmp_ or og_tmp_); // or $ff000000; /* and $ffffff;*/
307 result.r := Byte(me and $ff);
308 result.g := Byte((me shr 8) and $ff);
309 result.b := Byte((me shr 16) and $ff);
310 result.a := a;
311 end;
314 // ////////////////////////////////////////////////////////////////////////// //
315 // any mods = 255: nothing was defined
316 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
317 var
318 pos, epos: Integer;
319 begin
320 kmods := 255;
321 mbuts := 255;
322 pos := 1;
323 //while (pos <= Length(s)) and (s[pos] <= ' ') do Inc(pos);
324 if (pos < Length(s)) and ((s[pos] = '+') or (s[pos] = '-') or (s[pos] = '*')) then Inc(pos);
325 while (pos <= Length(s)) do
326 begin
327 if (Length(s)-pos >= 1) and (s[pos+1] = '-') then
328 begin
329 case s[pos] of
330 'C', 'c': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModCtrl; Inc(pos, 2); continue; end;
331 'M', 'm': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModAlt; Inc(pos, 2); continue; end;
332 'S', 's': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModShift; Inc(pos, 2); continue; end;
333 end;
334 break;
335 end;
336 if (Length(s)-pos >= 3) and (s[pos+3] = '-') and ((s[pos+1] = 'M') or (s[pos+1] = 'm')) and ((s[pos+2] = 'B') or (s[pos+2] = 'b')) then
337 begin
338 case s[pos] of
339 'L', 'l': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Left; Inc(pos, 4); continue; end;
340 'R', 'r': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Right; Inc(pos, 4); continue; end;
341 'M', 'm': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Middle; Inc(pos, 4); continue; end;
342 end;
343 break;
344 end;
345 break;
346 end;
347 epos := Length(s)+1;
348 while (epos > pos) and (s[epos-1] <= ' ') do Dec(epos);
349 if (epos > pos) then result := Copy(s, pos, epos-pos) else result := '';
350 end;
353 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
354 var
355 f: Integer;
356 kmods: Byte = 255;
357 mbuts: Byte = 255;
358 kname: AnsiString;
359 begin
360 result := false;
361 if (Length(s) > 0) then
362 begin
363 if (s[1] = '+') then begin if (not ev.press) then exit; end
364 else if (s[1] = '-') then begin if (not ev.release) then exit; end
365 else if (s[1] = '*') then begin end
366 else if (not ev.press) then exit;
367 end;
368 kname := parseModKeys(s, kmods, mbuts);
369 if (kmods = 255) then kmods := 0;
370 if (ev.kstate <> kmods) then exit;
371 if (mbuts <> 255) and (ev.bstate <> mbuts) then exit;
373 if (strEquCI(kname, 'Enter')) then kname := 'RETURN';
375 for f := 0 to SDL_NUM_SCANCODES-1 do
376 begin
377 if strEquCI(kname, SDL_GetScancodeName(f)) then
378 begin
379 result := (ev.scan = f);
380 exit;
381 end;
382 end;
383 end;
386 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
387 begin
388 result := (ev = s);
389 end;
392 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
393 var
394 kmods: Byte = 255;
395 mbuts: Byte = 255;
396 kname: AnsiString;
397 but: Integer = -1;
398 modch: AnsiChar = ' ';
399 begin
400 result := false;
402 if (Length(s) > 0) then
403 begin
404 if (s[1] = '+') then begin if (not ev.press) then exit; modch := '+'; end
405 else if (s[1] = '-') then begin if (not ev.release) then exit; modch := '-'; end
406 else if (s[1] = '*') then begin if (not ev.motion) then exit; end
407 else if (not ev.press) then exit;
408 end;
410 kname := parseModKeys(s, kmods, mbuts);
411 if strEquCI(kname, 'LMB') then but := THMouseEvent.Left
412 else if strEquCI(kname, 'RMB') then but := THMouseEvent.Right
413 else if strEquCI(kname, 'MMB') then but := THMouseEvent.Middle
414 else if strEquCI(kname, 'WheelUp') or strEquCI(kname, 'WUP') then but := THMouseEvent.WheelUp
415 else if strEquCI(kname, 'WheelDown') or strEquCI(kname, 'WDN') or strEquCI(kname, 'WDOWN') then but := THMouseEvent.WheelDown
416 else if strEquCI(kname, 'None') then but := 0
417 else exit;
419 if (mbuts = 255) then mbuts := 0;
420 if (kmods = 255) then kmods := 0;
421 if (ev.kstate <> kmods) then exit;
422 if (modch = '-') then mbuts := mbuts or but else if (modch = '+') then mbuts := mbuts and (not but);
424 result := (ev.bstate = mbuts) and (ev.but = but);
425 end;
428 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
429 begin
430 result := (ev = s);
431 end;
434 // ////////////////////////////////////////////////////////////////////////// //
435 procedure resetKMState (sendEvents: Boolean=true);
436 var
437 mask: Word;
438 mev: THMouseEvent;
439 kev: THKeyEvent;
440 begin
441 // generate mouse release events
442 if (curButState <> 0) then
443 begin
444 if sendEvents then
445 begin
446 mask := 1;
447 while (mask <> 0) do
448 begin
449 // checked each time, 'cause `evMouseCB` can be changed from the handler
450 if ((curButState and mask) <> 0) and assigned(evMouseCB) then
451 begin
452 FillChar(mev, sizeof(mev), 0);
453 mev.intrInit();
454 mev.kind := mev.TKind.Release;
455 mev.x := curMsX;
456 mev.y := curMsY;
457 mev.dx := 0;
458 mev.dy := 0;
459 mev.but := mask;
460 mev.bstate := curButState;
461 mev.kstate := curModState;
462 curButState := curButState and (not mask);
463 evMouseCB(mev);
464 end;
465 mask := mask shl 1;
466 end;
467 end;
468 curButState := 0;
469 end;
471 // generate modifier release events
472 if (curModState <> 0) then
473 begin
474 if sendEvents then
475 begin
476 mask := 1;
477 while (mask <= 8) do
478 begin
479 // checked each time, 'cause `evMouseCB` can be changed from the handler
480 if ((curModState and mask) <> 0) and assigned(evKeyCB) then
481 begin
482 FillChar(kev, sizeof(kev), 0);
483 kev.intrInit();
484 kev.kind := kev.TKind.Release;
485 case mask of
486 THKeyEvent.ModCtrl: begin kev.scan := SDL_SCANCODE_LCTRL; kev.sym := SDLK_LCTRL;{arbitrary} end;
487 THKeyEvent.ModAlt: begin kev.scan := SDL_SCANCODE_LALT; kev.sym := SDLK_LALT;{arbitrary} end;
488 THKeyEvent.ModShift: begin kev.scan := SDL_SCANCODE_LSHIFT; kev.sym := SDLK_LSHIFT;{arbitrary} end;
489 THKeyEvent.ModHyper: begin kev.scan := SDL_SCANCODE_LGUI; kev.sym := SDLK_LGUI;{arbitrary} end;
490 else assert(false);
491 end;
492 kev.x := curMsX;
493 kev.y := curMsY;
494 mev.bstate := 0{curMsButState}; // anyway
495 mev.kstate := curModState;
496 curModState := curModState and (not mask);
497 evKeyCB(kev);
498 end;
499 mask := mask shl 1;
500 end;
501 end;
502 curModState := 0;
503 end;
504 end;
507 function onSDLEvent (var ev: TSDL_Event): Boolean;
508 var
509 mev: THMouseEvent;
510 kev: THKeyEvent;
512 function buildBut (b: Byte): Word;
513 begin
514 result := 0;
515 case b of
516 SDL_BUTTON_LEFT: result := result or THMouseEvent.Left;
517 SDL_BUTTON_MIDDLE: result := result or THMouseEvent.Middle;
518 SDL_BUTTON_RIGHT: result := result or THMouseEvent.Right;
519 end;
520 end;
522 begin
523 result := false;
525 case ev.type_ of
526 SDL_KEYDOWN, SDL_KEYUP:
527 begin
528 // fix left/right modifiers
529 FillChar(kev, sizeof(kev), 0);
530 kev.intrInit();
531 if (ev.type_ = SDL_KEYDOWN) then kev.kind := THKeyEvent.TKind.Press else kev.kind := THKeyEvent.TKind.Release;
532 kev.scan := ev.key.keysym.scancode;
533 kev.sym := ev.key.keysym.sym;
535 if (kev.scan = SDL_SCANCODE_RCTRL) then kev.scan := SDL_SCANCODE_LCTRL;
536 if (kev.scan = SDL_SCANCODE_RALT) then kev.scan := SDL_SCANCODE_LALT;
537 if (kev.scan = SDL_SCANCODE_RSHIFT) then kev.scan := SDL_SCANCODE_LSHIFT;
538 if (kev.scan = SDL_SCANCODE_RGUI) then kev.scan := SDL_SCANCODE_LGUI;
540 if (kev.sym = SDLK_RCTRL) then kev.sym := SDLK_LCTRL;
541 if (kev.sym = SDLK_RALT) then kev.sym := SDLK_LALT;
542 if (kev.sym = SDLK_RSHIFT) then kev.sym := SDLK_LSHIFT;
543 if (kev.sym = SDLK_RGUI) then kev.sym := SDLK_LGUI;
545 kev.x := curMsX;
546 kev.y := curMsY;
547 kev.bstate := curButState;
548 kev.kstate := curModState;
550 case kev.scan of
551 SDL_SCANCODE_LCTRL: if (kev.press) then curModState := curModState or THKeyEvent.ModCtrl else curModState := curModState and (not THKeyEvent.ModCtrl);
552 SDL_SCANCODE_LALT: if (kev.press) then curModState := curModState or THKeyEvent.ModAlt else curModState := curModState and (not THKeyEvent.ModAlt);
553 SDL_SCANCODE_LSHIFT: if (kev.press) then curModState := curModState or THKeyEvent.ModShift else curModState := curModState and (not THKeyEvent.ModShift);
554 end;
556 if assigned(evKeyCB) then
557 begin
558 evKeyCB(kev);
559 result := kev.eaten;
560 end;
561 end;
563 SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP:
564 begin
565 FillChar(mev, sizeof(mev), 0);
566 mev.intrInit();
567 if (ev.type_ = SDL_MOUSEBUTTONDOWN) then mev.kind := THMouseEvent.TKind.Press else mev.kind := THMouseEvent.TKind.Release;
568 mev.dx := ev.button.x-curMsX;
569 mev.dy := ev.button.y-curMsY;
570 curMsX := ev.button.x;
571 curMsY := ev.button.y;
572 mev.but := buildBut(ev.button.button);
573 mev.x := curMsX;
574 mev.y := curMsY;
575 mev.bstate := curButState;
576 mev.kstate := curModState;
577 if (mev.but <> 0) then
578 begin
579 // ev.button.clicks: Byte
580 if (ev.type_ = SDL_MOUSEBUTTONDOWN) then curButState := curButState or mev.but else curButState := curButState and (not mev.but);
581 if assigned(evMouseCB) then
582 begin
583 evMouseCB(mev);
584 result := mev.eaten;
585 end;
586 end;
587 end;
588 SDL_MOUSEWHEEL:
589 begin
590 if (ev.wheel.y <> 0) then
591 begin
592 FillChar(mev, sizeof(mev), 0);
593 mev.intrInit();
594 mev.kind := THMouseEvent.TKind.Press;
595 mev.dx := 0;
596 mev.dy := ev.wheel.y;
597 if (ev.wheel.y < 0) then mev.but := THMouseEvent.WheelUp else mev.but := THMouseEvent.WheelDown;
598 mev.x := curMsX;
599 mev.y := curMsY;
600 mev.bstate := curButState;
601 mev.kstate := curModState;
602 if assigned(evMouseCB) then
603 begin
604 evMouseCB(mev);
605 result := mev.eaten;
606 end;
607 end;
608 end;
609 SDL_MOUSEMOTION:
610 begin
611 FillChar(mev, sizeof(mev), 0);
612 mev.intrInit();
613 mev.kind := THMouseEvent.TKind.Motion;
614 mev.dx := ev.button.x-curMsX;
615 mev.dy := ev.button.y-curMsY;
616 curMsX := ev.button.x;
617 curMsY := ev.button.y;
618 mev.but := 0;
619 mev.x := curMsX;
620 mev.y := curMsY;
621 mev.bstate := curButState;
622 mev.kstate := curModState;
623 if assigned(evMouseCB) then
624 begin
625 evMouseCB(mev);
626 result := mev.eaten;
627 end;
628 end;
631 SDL_TEXTINPUT:
632 begin
633 Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
634 keychr := Word(uc);
635 if (keychr > 127) then keychr := Word(wchar2win(WideChar(keychr)));
636 CharPress(AnsiChar(keychr));
637 end;
639 end;
640 end;
643 // ////////////////////////////////////////////////////////////////////////// //
644 procedure oglSetup2D (winWidth, winHeight: Integer; upsideDown: Boolean=false);
645 begin
646 glViewport(0, 0, winWidth, winHeight);
648 glDisable(GL_BLEND);
649 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
650 glDisable(GL_LINE_SMOOTH);
651 glDisable(GL_POINT_SMOOTH);
652 glDisable(GL_DEPTH_TEST);
653 glDisable(GL_TEXTURE_2D);
654 glDisable(GL_LIGHTING);
655 glDisable(GL_DITHER);
656 glDisable(GL_STENCIL_TEST);
657 glDisable(GL_SCISSOR_TEST);
658 glDisable(GL_CULL_FACE);
660 glMatrixMode(GL_PROJECTION);
661 glLoadIdentity();
662 if (upsideDown) then
663 begin
664 glOrtho(0, winWidth, 0, winHeight, -1, 1); // set origin to bottom left
665 end
666 else
667 begin
668 glOrtho(0, winWidth, winHeight, 0, -1, 1); // set origin to top left
669 end;
671 glMatrixMode(GL_MODELVIEW);
672 glLoadIdentity();
674 glClearColor(0, 0, 0, 0);
675 glColor4f(1, 1, 1, 1);
676 end;
679 // ////////////////////////////////////////////////////////////////////////// //
680 // cursor (hi, Death Track!)
681 const curTexWidth = 32;
682 const curTexHeight = 32;
683 const curWidth = 17;
684 const curHeight = 23;
686 const cursorImg: array[0..curWidth*curHeight-1] of Byte = (
687 2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
688 3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
689 3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
690 3,3,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,
691 3,3,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,
692 3,3,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,
693 3,3,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,
694 3,3,4,4,4,4,2,2,0,0,0,0,0,0,0,0,0,
695 3,3,4,4,4,5,6,2,2,0,0,0,0,0,0,0,0,
696 3,3,4,4,5,6,7,5,2,2,0,0,0,0,0,0,0,
697 3,3,4,5,6,7,5,4,5,2,2,0,0,0,0,0,0,
698 3,3,5,6,7,5,4,5,6,7,2,2,0,0,0,0,0,
699 3,3,6,7,5,4,5,6,7,7,7,2,2,0,0,0,0,
700 3,3,7,5,4,5,6,7,7,7,7,7,2,2,0,0,0,
701 3,3,5,4,5,6,8,8,8,8,8,8,8,8,2,0,0,
702 3,3,4,5,6,3,8,8,8,8,8,8,8,8,8,0,0,
703 3,3,5,6,3,3,0,0,0,0,0,0,0,0,0,0,0,
704 3,3,6,3,3,0,0,0,0,0,0,0,0,0,0,0,0,
705 3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
706 3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
707 3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
708 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
709 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
710 );
711 const cursorPal: array[0..9*4-1] of Byte = (
712 0, 0, 0, 0,
713 0, 0, 0,163,
714 85,255,255,255,
715 85, 85,255,255,
716 255, 85, 85,255,
717 170, 0,170,255,
718 85, 85, 85,255,
719 0, 0, 0,255,
720 0, 0,170,255
721 );
724 var
725 curtexid: GLuint = 0;
727 procedure createCursorTexture ();
728 var
729 tex, tpp: PByte;
730 c: Integer;
731 x, y: Integer;
732 begin
733 if (curtexid <> 0) then exit; //begin glDeleteTextures(1, @curtexid); curtexid := 0; end;
735 GetMem(tex, curTexWidth*curTexHeight*4);
736 try
737 FillChar(tex^, curTexWidth*curTexHeight*4, 0);
739 // draw shadow
740 for y := 0 to curHeight-1 do
741 begin
742 for x := 0 to curWidth-1 do
743 begin
744 if (cursorImg[y*curWidth+x] <> 0) then
745 begin
746 c := 1*4;
747 tpp := tex+((y+1)*(curTexWidth*4)+(x+3)*4);
748 tpp^ := cursorPal[c+0]; Inc(tpp);
749 tpp^ := cursorPal[c+1]; Inc(tpp);
750 tpp^ := cursorPal[c+2]; Inc(tpp);
751 tpp^ := cursorPal[c+3]; Inc(tpp);
752 tpp^ := cursorPal[c+0]; Inc(tpp);
753 tpp^ := cursorPal[c+1]; Inc(tpp);
754 tpp^ := cursorPal[c+2]; Inc(tpp);
755 tpp^ := cursorPal[c+3]; Inc(tpp);
756 end;
757 end;
758 end;
760 // draw cursor
761 for y := 0 to curHeight-1 do
762 begin
763 for x := 0 to curWidth-1 do
764 begin
765 c := cursorImg[y*curWidth+x]*4;
766 if (c <> 0) then
767 begin
768 tpp := tex+(y*(curTexWidth*4)+x*4);
769 tpp^ := cursorPal[c+0]; Inc(tpp);
770 tpp^ := cursorPal[c+1]; Inc(tpp);
771 tpp^ := cursorPal[c+2]; Inc(tpp);
772 tpp^ := cursorPal[c+3]; Inc(tpp);
773 end;
774 end;
775 end;
777 glGenTextures(1, @curtexid);
778 if (curtexid = 0) then raise Exception.Create('can''t create cursor texture');
780 glBindTexture(GL_TEXTURE_2D, curtexid);
781 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
782 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
783 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
784 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
786 //GLfloat[4] bclr = 0.0;
787 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
789 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, curTexWidth, curTexHeight, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
790 glFlush();
791 finally
792 FreeMem(tex);
793 end;
794 end;
796 procedure oglDrawCursorAt (msX, msY: Integer);
797 begin
798 //if (curtexid = 0) then createCursorTexture() else glBindTexture(GL_TEXTURE_2D, curtexid);
799 glBindTexture(GL_TEXTURE_2D, curtexid);
800 // blend it
801 glEnable(GL_BLEND);
802 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
803 glEnable(GL_TEXTURE_2D);
804 glDisable(GL_STENCIL_TEST);
805 glDisable(GL_SCISSOR_TEST);
806 glDisable(GL_LIGHTING);
807 glDisable(GL_DEPTH_TEST);
808 glDisable(GL_CULL_FACE);
809 // color and opacity
810 glColor4f(1, 1, 1, 0.9);
811 //Dec(msX, 2);
812 glBegin(GL_QUADS);
813 glTexCoord2f(0.0, 0.0); glVertex2i(msX, msY); // top-left
814 glTexCoord2f(1.0, 0.0); glVertex2i(msX+curTexWidth, msY); // top-right
815 glTexCoord2f(1.0, 1.0); glVertex2i(msX+curTexWidth, msY+curTexHeight); // bottom-right
816 glTexCoord2f(0.0, 1.0); glVertex2i(msX, msY+curTexHeight); // bottom-left
817 glEnd();
818 //Inc(msX, 2);
819 glDisable(GL_BLEND);
820 glDisable(GL_TEXTURE_2D);
821 glColor4f(1, 1, 1, 1);
822 glBindTexture(GL_TEXTURE_2D, 0);
823 end;
825 procedure oglDrawCursor (); begin oglDrawCursorAt(curMsX, curMsY); end;
828 // ////////////////////////////////////////////////////////////////////////// //
829 // fonts
830 const kgiFont6: array[0..256*8-1] of Byte = (
831 $00,$00,$00,$00,$00,$00,$00,$00,$3c,$42,$a5,$81,$a5,$99,$42,$3c,$3c,$7e,$db,$ff,$ff,$db,$66,$3c,$6c,$fe,
832 $fe,$fe,$7c,$38,$10,$00,$10,$38,$7c,$fe,$7c,$38,$10,$00,$10,$38,$54,$fe,$54,$10,$38,$00,$10,$38,$7c,$fe,
833 $fe,$10,$38,$00,$00,$00,$00,$30,$30,$00,$00,$00,$ff,$ff,$ff,$e7,$e7,$ff,$ff,$ff,$38,$44,$82,$82,$82,$44,
834 $38,$00,$c7,$bb,$7d,$7d,$7d,$bb,$c7,$ff,$0f,$03,$05,$79,$88,$88,$88,$70,$38,$44,$44,$44,$38,$10,$7c,$10,
835 $30,$28,$24,$24,$28,$20,$e0,$c0,$3c,$24,$3c,$24,$24,$e4,$dc,$18,$10,$54,$38,$ee,$38,$54,$10,$00,$10,$10,
836 $10,$7c,$10,$10,$10,$10,$10,$10,$10,$ff,$00,$00,$00,$00,$00,$00,$00,$ff,$10,$10,$10,$10,$10,$10,$10,$f0,
837 $10,$10,$10,$10,$10,$10,$10,$1f,$10,$10,$10,$10,$10,$10,$10,$ff,$10,$10,$10,$10,$10,$10,$10,$10,$10,$10,
838 $10,$10,$00,$00,$00,$ff,$00,$00,$00,$00,$00,$00,$00,$1f,$10,$10,$10,$10,$00,$00,$00,$f0,$10,$10,$10,$10,
839 $10,$10,$10,$1f,$00,$00,$00,$00,$10,$10,$10,$f0,$00,$00,$00,$00,$81,$42,$24,$18,$18,$24,$42,$81,$01,$02,
840 $04,$08,$10,$20,$40,$80,$80,$40,$20,$10,$08,$04,$02,$01,$00,$10,$10,$ff,$10,$10,$00,$00,$00,$00,$00,$00,
841 $00,$00,$00,$00,$20,$20,$20,$20,$00,$00,$20,$00,$50,$50,$50,$00,$00,$00,$00,$00,$50,$50,$f8,$50,$f8,$50,
842 $50,$00,$20,$78,$a0,$70,$28,$f0,$20,$00,$c0,$c8,$10,$20,$40,$98,$18,$00,$40,$a0,$40,$a8,$90,$98,$60,$00,
843 $10,$20,$40,$00,$00,$00,$00,$00,$10,$20,$40,$40,$40,$20,$10,$00,$40,$20,$10,$10,$10,$20,$40,$00,$88,$50,
844 $20,$f8,$20,$50,$88,$00,$00,$20,$20,$f8,$20,$20,$00,$00,$00,$00,$00,$00,$00,$20,$20,$40,$00,$00,$00,$78,
845 $00,$00,$00,$00,$00,$00,$00,$00,$00,$60,$60,$00,$00,$00,$08,$10,$20,$40,$80,$00,$70,$88,$98,$a8,$c8,$88,
846 $70,$00,$20,$60,$a0,$20,$20,$20,$f8,$00,$70,$88,$08,$10,$60,$80,$f8,$00,$70,$88,$08,$30,$08,$88,$70,$00,
847 $10,$30,$50,$90,$f8,$10,$10,$00,$f8,$80,$e0,$10,$08,$10,$e0,$00,$30,$40,$80,$f0,$88,$88,$70,$00,$f8,$88,
848 $10,$20,$20,$20,$20,$00,$70,$88,$88,$70,$88,$88,$70,$00,$70,$88,$88,$78,$08,$10,$60,$00,$00,$00,$20,$00,
849 $00,$20,$00,$00,$00,$00,$20,$00,$00,$20,$20,$40,$18,$30,$60,$c0,$60,$30,$18,$00,$00,$00,$f8,$00,$f8,$00,
850 $00,$00,$c0,$60,$30,$18,$30,$60,$c0,$00,$70,$88,$08,$10,$20,$00,$20,$00,$70,$88,$08,$68,$a8,$a8,$70,$00,
851 $20,$50,$88,$88,$f8,$88,$88,$00,$f0,$48,$48,$70,$48,$48,$f0,$00,$30,$48,$80,$80,$80,$48,$30,$00,$e0,$50,
852 $48,$48,$48,$50,$e0,$00,$f8,$80,$80,$f0,$80,$80,$f8,$00,$f8,$80,$80,$f0,$80,$80,$80,$00,$70,$88,$80,$b8,
853 $88,$88,$70,$00,$88,$88,$88,$f8,$88,$88,$88,$00,$70,$20,$20,$20,$20,$20,$70,$00,$38,$10,$10,$10,$90,$90,
854 $60,$00,$88,$90,$a0,$c0,$a0,$90,$88,$00,$80,$80,$80,$80,$80,$80,$f8,$00,$88,$d8,$a8,$a8,$88,$88,$88,$00,
855 $88,$c8,$c8,$a8,$98,$98,$88,$00,$70,$88,$88,$88,$88,$88,$70,$00,$f0,$88,$88,$f0,$80,$80,$80,$00,$70,$88,
856 $88,$88,$a8,$90,$68,$00,$f0,$88,$88,$f0,$a0,$90,$88,$00,$70,$88,$80,$70,$08,$88,$70,$00,$f8,$20,$20,$20,
857 $20,$20,$20,$00,$88,$88,$88,$88,$88,$88,$70,$00,$88,$88,$88,$88,$50,$50,$20,$00,$88,$88,$88,$a8,$a8,$d8,
858 $88,$00,$88,$88,$50,$20,$50,$88,$88,$00,$88,$88,$88,$70,$20,$20,$20,$00,$f8,$08,$10,$20,$40,$80,$f8,$00,
859 $70,$40,$40,$40,$40,$40,$70,$00,$00,$00,$80,$40,$20,$10,$08,$00,$70,$10,$10,$10,$10,$10,$70,$00,$20,$50,
860 $88,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$f8,$00,$40,$20,$10,$00,$00,$00,$00,$00,$00,$00,$70,$08,
861 $78,$88,$78,$00,$80,$80,$b0,$c8,$88,$c8,$b0,$00,$00,$00,$70,$88,$80,$88,$70,$00,$08,$08,$68,$98,$88,$98,
862 $68,$00,$00,$00,$70,$88,$f8,$80,$70,$00,$10,$28,$20,$f8,$20,$20,$20,$00,$00,$00,$68,$98,$98,$68,$08,$70,
863 $80,$80,$f0,$88,$88,$88,$88,$00,$20,$00,$60,$20,$20,$20,$70,$00,$10,$00,$30,$10,$10,$10,$90,$60,$40,$40,
864 $48,$50,$60,$50,$48,$00,$60,$20,$20,$20,$20,$20,$70,$00,$00,$00,$d0,$a8,$a8,$a8,$a8,$00,$00,$00,$b0,$c8,
865 $88,$88,$88,$00,$00,$00,$70,$88,$88,$88,$70,$00,$00,$00,$b0,$c8,$c8,$b0,$80,$80,$00,$00,$68,$98,$98,$68,
866 $08,$08,$00,$00,$b0,$c8,$80,$80,$80,$00,$00,$00,$78,$80,$f0,$08,$f0,$00,$40,$40,$f0,$40,$40,$48,$30,$00,
867 $00,$00,$90,$90,$90,$90,$68,$00,$00,$00,$88,$88,$88,$50,$20,$00,$00,$00,$88,$a8,$a8,$a8,$50,$00,$00,$00,
868 $88,$50,$20,$50,$88,$00,$00,$00,$88,$88,$98,$68,$08,$70,$00,$00,$f8,$10,$20,$40,$f8,$00,$18,$20,$20,$40,
869 $20,$20,$18,$00,$20,$20,$20,$00,$20,$20,$20,$00,$c0,$20,$20,$10,$20,$20,$c0,$00,$40,$a8,$10,$00,$00,$00,
870 $00,$00,$00,$00,$20,$50,$f8,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$ff,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,
871 $00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$00,$00,$00,$00,$00,$3c,$3c,$00,$00,$00,$ff,$ff,
872 $ff,$ff,$ff,$ff,$00,$00,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$c0,$0f,$0f,$0f,$0f,$f0,$f0,$f0,$f0,$fc,$fc,$fc,$fc,
873 $fc,$fc,$fc,$fc,$03,$03,$03,$03,$03,$03,$03,$03,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$3f,$11,$22,$44,$88,$11,$22,
874 $44,$88,$88,$44,$22,$11,$88,$44,$22,$11,$fe,$7c,$38,$10,$00,$00,$00,$00,$00,$00,$00,$00,$10,$38,$7c,$fe,
875 $80,$c0,$e0,$f0,$e0,$c0,$80,$00,$01,$03,$07,$0f,$07,$03,$01,$00,$ff,$7e,$3c,$18,$18,$3c,$7e,$ff,$81,$c3,
876 $e7,$ff,$ff,$e7,$c3,$81,$f0,$f0,$f0,$f0,$00,$00,$00,$00,$00,$00,$00,$00,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,
877 $00,$00,$00,$00,$00,$00,$00,$00,$f0,$f0,$f0,$f0,$33,$33,$cc,$cc,$33,$33,$cc,$cc,$00,$20,$20,$50,$50,$88,
878 $f8,$00,$20,$20,$70,$20,$70,$20,$20,$00,$00,$00,$00,$50,$88,$a8,$50,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,
879 $00,$00,$00,$00,$ff,$ff,$ff,$ff,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$ff,$ff,
880 $ff,$ff,$00,$00,$00,$00,$00,$00,$68,$90,$90,$90,$68,$00,$30,$48,$48,$70,$48,$48,$70,$c0,$f8,$88,$80,$80,
881 $80,$80,$80,$00,$00,$50,$70,$88,$f8,$80,$70,$00,$00,$00,$78,$80,$f0,$80,$78,$00,$00,$00,$78,$90,$90,$90,
882 $60,$00,$20,$00,$60,$20,$20,$20,$70,$00,$50,$00,$70,$20,$20,$20,$70,$00,$f8,$20,$70,$a8,$a8,$70,$20,$f8,
883 $20,$50,$88,$f8,$88,$50,$20,$00,$70,$88,$88,$88,$50,$50,$d8,$00,$30,$40,$40,$20,$50,$50,$50,$20,$00,$00,
884 $00,$50,$a8,$a8,$50,$00,$08,$70,$a8,$a8,$a8,$70,$80,$00,$38,$40,$80,$f8,$80,$40,$38,$00,$70,$88,$88,$88,
885 $88,$88,$88,$00,$00,$f8,$00,$f8,$00,$f8,$00,$00,$20,$20,$f8,$20,$20,$00,$f8,$00,$c0,$30,$08,$30,$c0,$00,
886 $f8,$00,$50,$f8,$80,$f0,$80,$80,$f8,$00,$78,$80,$80,$f0,$80,$80,$78,$00,$20,$20,$20,$20,$20,$20,$a0,$40,
887 $70,$20,$20,$20,$20,$20,$70,$00,$50,$70,$20,$20,$20,$20,$70,$00,$00,$18,$24,$24,$18,$00,$00,$00,$00,$30,
888 $78,$78,$30,$00,$00,$00,$00,$00,$00,$00,$30,$00,$00,$00,$3e,$20,$20,$20,$a0,$60,$20,$00,$a0,$50,$50,$50,
889 $00,$00,$00,$00,$40,$a0,$20,$40,$e0,$00,$00,$00,$00,$38,$38,$38,$38,$38,$38,$00,$3c,$42,$99,$a1,$a1,$99,
890 $42,$3c,$00,$00,$90,$a8,$e8,$a8,$90,$00,$00,$00,$60,$10,$70,$90,$68,$00,$00,$00,$f0,$80,$f0,$88,$f0,$00,
891 $00,$00,$90,$90,$90,$f8,$08,$00,$00,$00,$30,$50,$50,$70,$88,$00,$00,$00,$70,$88,$f8,$80,$70,$00,$00,$20,
892 $70,$a8,$a8,$70,$20,$00,$00,$00,$78,$48,$40,$40,$40,$00,$00,$00,$88,$50,$20,$50,$88,$00,$00,$00,$88,$98,
893 $a8,$c8,$88,$00,$00,$50,$20,$00,$98,$a8,$c8,$00,$00,$00,$90,$a0,$c0,$a0,$90,$00,$00,$00,$38,$28,$28,$48,
894 $88,$00,$00,$00,$88,$d8,$a8,$88,$88,$00,$00,$00,$88,$88,$f8,$88,$88,$00,$00,$00,$70,$88,$88,$88,$70,$00,
895 $00,$00,$78,$48,$48,$48,$48,$00,$00,$00,$78,$88,$78,$28,$48,$00,$00,$00,$f0,$88,$f0,$80,$80,$00,$00,$00,
896 $78,$80,$80,$80,$78,$00,$00,$00,$f8,$20,$20,$20,$20,$00,$00,$00,$88,$50,$20,$40,$80,$00,$00,$00,$a8,$70,
897 $20,$70,$a8,$00,$00,$00,$f0,$48,$70,$48,$f0,$00,$00,$00,$40,$40,$70,$48,$70,$00,$00,$00,$88,$88,$c8,$a8,
898 $c8,$00,$00,$00,$f0,$08,$70,$08,$f0,$00,$00,$00,$a8,$a8,$a8,$a8,$f8,$00,$00,$00,$70,$88,$38,$88,$70,$00,
899 $00,$00,$a8,$a8,$a8,$f8,$08,$00,$00,$00,$48,$48,$78,$08,$08,$00,$00,$00,$c0,$40,$70,$48,$70,$00,$90,$a8,
900 $a8,$e8,$a8,$a8,$90,$00,$20,$50,$88,$88,$f8,$88,$88,$00,$f8,$88,$80,$f0,$88,$88,$f0,$00,$90,$90,$90,$90,
901 $90,$f8,$08,$00,$38,$28,$28,$48,$48,$f8,$88,$00,$f8,$80,$80,$f0,$80,$80,$f8,$00,$20,$70,$a8,$a8,$a8,$70,
902 $20,$00,$f8,$88,$88,$80,$80,$80,$80,$00,$88,$88,$50,$20,$50,$88,$88,$00,$88,$88,$98,$a8,$c8,$88,$88,$00,
903 $50,$20,$88,$98,$a8,$c8,$88,$00,$88,$90,$a0,$c0,$a0,$90,$88,$00,$18,$28,$48,$48,$48,$48,$88,$00,$88,$d8,
904 $a8,$a8,$88,$88,$88,$00,$88,$88,$88,$f8,$88,$88,$88,$00,$70,$88,$88,$88,$88,$88,$70,$00,$f8,$88,$88,$88,
905 $88,$88,$88,$00,$78,$88,$88,$78,$28,$48,$88,$00,$f0,$88,$88,$f0,$80,$80,$80,$00,$70,$88,$80,$80,$80,$88,
906 $70,$00,$f8,$20,$20,$20,$20,$20,$20,$00,$88,$88,$88,$50,$20,$40,$80,$00,$a8,$a8,$70,$20,$70,$a8,$a8,$00,
907 $f0,$48,$48,$70,$48,$48,$f0,$00,$80,$80,$80,$f0,$88,$88,$f0,$00,$88,$88,$88,$c8,$a8,$a8,$c8,$00,$f0,$08,
908 $08,$30,$08,$08,$f0,$00,$a8,$a8,$a8,$a8,$a8,$a8,$f8,$00,$70,$88,$08,$78,$08,$88,$70,$00,$a8,$a8,$a8,$a8,
909 $a8,$f8,$08,$00,$88,$88,$88,$88,$78,$08,$08,$00,$c0,$40,$40,$70,$48,$48,$70,$00
910 );
912 const kgiFont8: array[0..256*8-1] of Byte = (
913 $00,$00,$00,$00,$00,$00,$00,$00,$7e,$81,$a5,$81,$bd,$99,$81,$7e,$7e,$ff,$db,$ff,$c3,$e7,$ff,$7e,$6c,$fe,
914 $fe,$fe,$7c,$38,$10,$00,$10,$38,$7c,$fe,$7c,$38,$10,$00,$38,$7c,$38,$fe,$fe,$d6,$10,$38,$10,$10,$38,$7c,
915 $fe,$7c,$10,$38,$00,$00,$18,$3c,$3c,$18,$00,$00,$ff,$ff,$e7,$c3,$c3,$e7,$ff,$ff,$00,$3c,$66,$42,$42,$66,
916 $3c,$00,$ff,$c3,$99,$bd,$bd,$99,$c3,$ff,$0f,$07,$0f,$7d,$cc,$cc,$cc,$78,$3c,$66,$66,$66,$3c,$18,$7e,$18,
917 $3f,$33,$3f,$30,$30,$70,$f0,$e0,$7f,$63,$7f,$63,$63,$67,$e6,$c0,$99,$5a,$3c,$e7,$e7,$3c,$5a,$99,$80,$e0,
918 $f8,$fe,$f8,$e0,$80,$00,$02,$0e,$3e,$fe,$3e,$0e,$02,$00,$18,$3c,$7e,$18,$18,$7e,$3c,$18,$66,$66,$66,$66,
919 $66,$00,$66,$00,$7f,$db,$db,$7b,$1b,$1b,$1b,$00,$7e,$c3,$78,$cc,$cc,$78,$8c,$f8,$00,$00,$00,$00,$7e,$7e,
920 $7e,$00,$18,$3c,$7e,$18,$7e,$3c,$18,$ff,$18,$3c,$7e,$18,$18,$18,$18,$00,$18,$18,$18,$18,$7e,$3c,$18,$00,
921 $00,$18,$0c,$fe,$0c,$18,$00,$00,$00,$30,$60,$fe,$60,$30,$00,$00,$00,$00,$c0,$c0,$c0,$fe,$00,$00,$00,$24,
922 $66,$ff,$66,$24,$00,$00,$00,$18,$3c,$7e,$ff,$ff,$00,$00,$00,$ff,$ff,$7e,$3c,$18,$00,$00,$00,$00,$00,$00,
923 $00,$00,$00,$00,$30,$78,$78,$30,$30,$00,$30,$00,$6c,$6c,$6c,$00,$00,$00,$00,$00,$6c,$6c,$fe,$6c,$fe,$6c,
924 $6c,$00,$30,$7c,$c0,$78,$0c,$f8,$30,$00,$00,$c6,$cc,$18,$30,$66,$c6,$00,$38,$6c,$38,$76,$dc,$cc,$76,$00,
925 $60,$60,$c0,$00,$00,$00,$00,$00,$18,$30,$60,$60,$60,$30,$18,$00,$60,$30,$18,$18,$18,$30,$60,$00,$00,$66,
926 $3c,$ff,$3c,$66,$00,$00,$00,$30,$30,$fc,$30,$30,$00,$00,$00,$00,$00,$00,$00,$70,$30,$60,$00,$00,$00,$fc,
927 $00,$00,$00,$00,$00,$00,$00,$00,$00,$30,$30,$00,$06,$0c,$18,$30,$60,$c0,$80,$00,$78,$cc,$dc,$fc,$ec,$cc,
928 $78,$00,$30,$f0,$30,$30,$30,$30,$fc,$00,$78,$cc,$0c,$38,$60,$cc,$fc,$00,$78,$cc,$0c,$38,$0c,$cc,$78,$00,
929 $1c,$3c,$6c,$cc,$fe,$0c,$0c,$00,$fc,$c0,$f8,$0c,$0c,$cc,$78,$00,$38,$60,$c0,$f8,$cc,$cc,$78,$00,$fc,$cc,
930 $0c,$18,$30,$60,$60,$00,$78,$cc,$cc,$78,$cc,$cc,$78,$00,$78,$cc,$cc,$7c,$0c,$18,$70,$00,$00,$00,$30,$30,
931 $00,$30,$30,$00,$00,$00,$30,$30,$00,$70,$30,$60,$18,$30,$60,$c0,$60,$30,$18,$00,$00,$00,$fc,$00,$fc,$00,
932 $00,$00,$60,$30,$18,$0c,$18,$30,$60,$00,$78,$cc,$0c,$18,$30,$00,$30,$00,$7c,$c6,$de,$de,$de,$c0,$78,$00,
933 $30,$78,$cc,$cc,$fc,$cc,$cc,$00,$fc,$66,$66,$7c,$66,$66,$fc,$00,$3c,$66,$c0,$c0,$c0,$66,$3c,$00,$fc,$6c,
934 $66,$66,$66,$6c,$fc,$00,$fe,$62,$68,$78,$68,$62,$fe,$00,$fe,$62,$68,$78,$68,$60,$f0,$00,$3c,$66,$c0,$c0,
935 $ce,$66,$3e,$00,$cc,$cc,$cc,$fc,$cc,$cc,$cc,$00,$78,$30,$30,$30,$30,$30,$78,$00,$1e,$0c,$0c,$0c,$cc,$cc,
936 $78,$00,$e6,$66,$6c,$78,$6c,$66,$e6,$00,$f0,$60,$60,$60,$62,$66,$fe,$00,$c6,$ee,$fe,$d6,$c6,$c6,$c6,$00,
937 $c6,$e6,$f6,$de,$ce,$c6,$c6,$00,$38,$6c,$c6,$c6,$c6,$6c,$38,$00,$fc,$66,$66,$7c,$60,$60,$f0,$00,$78,$cc,
938 $cc,$cc,$dc,$78,$1c,$00,$fc,$66,$66,$7c,$78,$6c,$e6,$00,$78,$cc,$e0,$38,$1c,$cc,$78,$00,$fc,$b4,$30,$30,
939 $30,$30,$78,$00,$cc,$cc,$cc,$cc,$cc,$cc,$fc,$00,$cc,$cc,$cc,$cc,$cc,$78,$30,$00,$c6,$c6,$c6,$d6,$fe,$ee,
940 $c6,$00,$c6,$c6,$6c,$38,$6c,$c6,$c6,$00,$cc,$cc,$cc,$78,$30,$30,$78,$00,$fe,$cc,$98,$30,$62,$c6,$fe,$00,
941 $78,$60,$60,$60,$60,$60,$78,$00,$c0,$60,$30,$18,$0c,$06,$02,$00,$78,$18,$18,$18,$18,$18,$78,$00,$10,$38,
942 $6c,$c6,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$ff,$30,$30,$18,$00,$00,$00,$00,$00,$00,$00,$78,$0c,
943 $7c,$cc,$76,$00,$e0,$60,$7c,$66,$66,$66,$bc,$00,$00,$00,$78,$cc,$c0,$cc,$78,$00,$1c,$0c,$0c,$7c,$cc,$cc,
944 $76,$00,$00,$00,$78,$cc,$fc,$c0,$78,$00,$38,$6c,$60,$f0,$60,$60,$f0,$00,$00,$00,$76,$cc,$cc,$7c,$0c,$f8,
945 $e0,$60,$6c,$76,$66,$66,$e6,$00,$30,$00,$70,$30,$30,$30,$78,$00,$18,$00,$78,$18,$18,$18,$d8,$70,$e0,$60,
946 $66,$6c,$78,$6c,$e6,$00,$70,$30,$30,$30,$30,$30,$78,$00,$00,$00,$ec,$fe,$d6,$c6,$c6,$00,$00,$00,$f8,$cc,
947 $cc,$cc,$cc,$00,$00,$00,$78,$cc,$cc,$cc,$78,$00,$00,$00,$dc,$66,$66,$7c,$60,$f0,$00,$00,$76,$cc,$cc,$7c,
948 $0c,$1e,$00,$00,$d8,$6c,$6c,$60,$f0,$00,$00,$00,$7c,$c0,$78,$0c,$f8,$00,$10,$30,$7c,$30,$30,$34,$18,$00,
949 $00,$00,$cc,$cc,$cc,$cc,$76,$00,$00,$00,$cc,$cc,$cc,$78,$30,$00,$00,$00,$c6,$c6,$d6,$fe,$6c,$00,$00,$00,
950 $c6,$6c,$38,$6c,$c6,$00,$00,$00,$cc,$cc,$cc,$7c,$0c,$f8,$00,$00,$fc,$98,$30,$64,$fc,$00,$1c,$30,$30,$e0,
951 $30,$30,$1c,$00,$18,$18,$18,$00,$18,$18,$18,$00,$e0,$30,$30,$1c,$30,$30,$e0,$00,$76,$dc,$00,$00,$00,$00,
952 $00,$00,$10,$38,$6c,$c6,$c6,$c6,$fe,$00,$78,$cc,$c0,$cc,$78,$18,$0c,$78,$00,$cc,$00,$cc,$cc,$cc,$7e,$00,
953 $1c,$00,$78,$cc,$fc,$c0,$78,$00,$7e,$c3,$3c,$06,$3e,$66,$3f,$00,$cc,$00,$78,$0c,$7c,$cc,$7e,$00,$e0,$00,
954 $78,$0c,$7c,$cc,$7e,$00,$30,$30,$78,$0c,$7c,$cc,$7e,$00,$00,$00,$7c,$c0,$c0,$7c,$06,$3c,$7e,$c3,$3c,$66,
955 $7e,$60,$3c,$00,$cc,$00,$78,$cc,$fc,$c0,$78,$00,$e0,$00,$78,$cc,$fc,$c0,$78,$00,$cc,$00,$70,$30,$30,$30,
956 $78,$00,$7c,$c6,$38,$18,$18,$18,$3c,$00,$e0,$00,$70,$30,$30,$30,$78,$00,$cc,$30,$78,$cc,$cc,$fc,$cc,$00,
957 $30,$30,$00,$78,$cc,$fc,$cc,$00,$1c,$00,$fc,$60,$78,$60,$fc,$00,$00,$00,$7f,$0c,$7f,$cc,$7f,$00,$3e,$6c,
958 $cc,$fe,$cc,$cc,$ce,$00,$78,$cc,$00,$78,$cc,$cc,$78,$00,$00,$cc,$00,$78,$cc,$cc,$78,$00,$00,$e0,$00,$78,
959 $cc,$cc,$78,$00,$78,$cc,$00,$cc,$cc,$cc,$7e,$00,$00,$e0,$00,$cc,$cc,$cc,$7e,$00,$00,$cc,$00,$cc,$cc,$fc,
960 $0c,$f8,$c6,$38,$7c,$c6,$c6,$7c,$38,$00,$cc,$00,$cc,$cc,$cc,$cc,$78,$00,$18,$18,$7e,$c0,$c0,$7e,$18,$18,
961 $38,$6c,$64,$f0,$60,$e6,$fc,$00,$cc,$cc,$78,$fc,$30,$fc,$30,$00,$f0,$d8,$d8,$f4,$cc,$de,$cc,$0e,$0e,$1b,
962 $18,$7e,$18,$18,$d8,$70,$1c,$00,$78,$0c,$7c,$cc,$7e,$00,$38,$00,$70,$30,$30,$30,$78,$00,$00,$1c,$00,$78,
963 $cc,$cc,$78,$00,$00,$1c,$00,$cc,$cc,$cc,$7e,$00,$00,$f8,$00,$f8,$cc,$cc,$cc,$00,$fc,$00,$cc,$ec,$fc,$dc,
964 $cc,$00,$3c,$6c,$6c,$3e,$00,$7e,$00,$00,$3c,$66,$66,$3c,$00,$7e,$00,$00,$30,$00,$30,$60,$c0,$cc,$78,$00,
965 $00,$00,$00,$fc,$c0,$c0,$00,$00,$00,$00,$00,$fc,$0c,$0c,$00,$00,$c6,$cc,$d8,$3e,$63,$ce,$98,$1f,$c6,$cc,
966 $d8,$f3,$67,$cf,$9f,$03,$00,$18,$00,$18,$18,$3c,$3c,$18,$00,$33,$66,$cc,$66,$33,$00,$00,$00,$cc,$66,$33,
967 $66,$cc,$00,$00,$22,$88,$22,$88,$22,$88,$22,$88,$55,$aa,$55,$aa,$55,$aa,$55,$aa,$dc,$76,$dc,$76,$dc,$76,
968 $dc,$76,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$f8,$18,$18,$18,$18,$18,$f8,$18,$f8,$18,$18,$18,
969 $36,$36,$36,$36,$f6,$36,$36,$36,$00,$00,$00,$00,$fe,$36,$36,$36,$00,$00,$f8,$18,$f8,$18,$18,$18,$36,$36,
970 $f6,$06,$f6,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$36,$00,$00,$fe,$06,$f6,$36,$36,$36,$36,$36,$f6,$06,
971 $fe,$00,$00,$00,$36,$36,$36,$36,$fe,$00,$00,$00,$18,$18,$f8,$18,$f8,$00,$00,$00,$00,$00,$00,$00,$f8,$18,
972 $18,$18,$18,$18,$18,$18,$1f,$00,$00,$00,$18,$18,$18,$18,$ff,$00,$00,$00,$00,$00,$00,$00,$ff,$18,$18,$18,
973 $18,$18,$18,$18,$1f,$18,$18,$18,$00,$00,$00,$00,$ff,$00,$00,$00,$18,$18,$18,$18,$ff,$18,$18,$18,$18,$18,
974 $1f,$18,$1f,$18,$18,$18,$36,$36,$36,$36,$37,$36,$36,$36,$36,$36,$37,$30,$3f,$00,$00,$00,$00,$00,$3f,$30,
975 $37,$36,$36,$36,$36,$36,$f7,$00,$ff,$00,$00,$00,$00,$00,$ff,$00,$f7,$36,$36,$36,$36,$36,$37,$30,$37,$36,
976 $36,$36,$00,$00,$ff,$00,$ff,$00,$00,$00,$36,$36,$f7,$00,$f7,$36,$36,$36,$18,$18,$ff,$00,$ff,$00,$00,$00,
977 $36,$36,$36,$36,$ff,$00,$00,$00,$00,$00,$ff,$00,$ff,$18,$18,$18,$00,$00,$00,$00,$ff,$36,$36,$36,$36,$36,
978 $36,$36,$3f,$00,$00,$00,$18,$18,$1f,$18,$1f,$00,$00,$00,$00,$00,$1f,$18,$1f,$18,$18,$18,$00,$00,$00,$00,
979 $3f,$36,$36,$36,$36,$36,$36,$36,$f7,$36,$36,$36,$18,$18,$ff,$00,$ff,$18,$18,$18,$18,$18,$18,$18,$f8,$00,
980 $00,$00,$00,$00,$00,$00,$1f,$18,$18,$18,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$ff,$ff,$ff,$ff,
981 $f0,$f0,$f0,$f0,$f0,$f0,$f0,$f0,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$0f,$ff,$ff,$ff,$ff,$00,$00,$00,$00,$00,$00,
982 $76,$dc,$c8,$dc,$76,$00,$00,$78,$cc,$f8,$cc,$f8,$c0,$c0,$00,$fe,$c6,$c0,$c0,$c0,$c0,$00,$00,$fe,$6c,$6c,
983 $6c,$6c,$6c,$00,$fe,$66,$30,$18,$30,$66,$fe,$00,$00,$00,$7e,$cc,$cc,$cc,$78,$00,$00,$66,$66,$66,$66,$7c,
984 $60,$c0,$00,$76,$dc,$18,$18,$18,$18,$00,$fc,$30,$78,$cc,$cc,$78,$30,$fc,$38,$6c,$c6,$fe,$c6,$6c,$38,$00,
985 $38,$6c,$c6,$c6,$6c,$6c,$ee,$00,$1c,$30,$18,$7c,$cc,$cc,$78,$00,$00,$00,$7e,$db,$db,$7e,$00,$00,$06,$0c,
986 $7e,$db,$db,$7e,$60,$c0,$3c,$60,$c0,$fc,$c0,$60,$3c,$00,$78,$cc,$cc,$cc,$cc,$cc,$cc,$00,$00,$fc,$00,$fc,
987 $00,$fc,$00,$00,$30,$30,$fc,$30,$30,$00,$fc,$00,$60,$30,$18,$30,$60,$00,$fc,$00,$18,$30,$60,$30,$18,$00,
988 $fc,$00,$0e,$1b,$1b,$18,$18,$18,$18,$18,$18,$18,$18,$18,$18,$d8,$d8,$70,$30,$30,$00,$fc,$00,$30,$30,$00,
989 $00,$72,$9c,$00,$72,$9c,$00,$00,$38,$6c,$6c,$38,$00,$00,$00,$00,$00,$00,$00,$18,$18,$00,$00,$00,$00,$00,
990 $00,$00,$18,$00,$00,$00,$0f,$0c,$0c,$0c,$ec,$6c,$3c,$1c,$78,$6c,$6c,$6c,$6c,$00,$00,$00,$78,$0c,$38,$60,
991 $7c,$00,$00,$00,$00,$00,$3c,$3c,$3c,$3c,$00,$00,$ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff
992 );
994 const kgiFont6PropWidth: array[0..256-1] of Byte = (
995 $08,$08,$08,$07,$07,$07,$07,$04,$08,$07,$08,$08,$06,$06,$06,$07,
996 $06,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
997 $85,$21,$13,$05,$05,$05,$05,$13,$13,$13,$05,$05,$12,$14,$12,$05,
998 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$21,$12,$05,$05,$05,$05,
999 $05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$05,$05,$05,$05,$05,
1000 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$13,$05,$13,$05,$05,
1001 $13,$05,$05,$05,$05,$05,$05,$05,$05,$13,$04,$14,$13,$05,$05,$05,
1002 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$14,$21,$04,$05,$08,
1003 $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$04,
1004 $44,$08,$08,$08,$08,$08,$08,$08,$05,$04,$05,$08,$08,$08,$08,$08,
1005 $05,$05,$05,$05,$05,$05,$13,$13,$05,$05,$05,$04,$05,$05,$05,$05,
1006 $05,$05,$05,$05,$05,$03,$04,$04,$06,$05,$04,$07,$04,$03,$05,$08,
1007 $05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$04,$05,$05,$05,$05,
1008 $14,$05,$05,$05,$05,$05,$05,$05,$14,$05,$05,$05,$05,$05,$14,$05,
1009 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,
1010 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05
1011 );
1013 const kgiFont8PropWidth: array[0..256-1] of Byte = (
1014 $08,$08,$08,$07,$07,$07,$07,$06,$08,$07,$08,$08,$07,$08,$08,$08,
1015 $07,$07,$07,$07,$08,$08,$07,$08,$07,$07,$07,$07,$07,$08,$08,$08,
1016 $85,$14,$15,$07,$06,$07,$07,$03,$14,$14,$08,$06,$13,$06,$22,$07,
1017 $05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$22,$13,$05,$06,$15,$06,
1018 $07,$06,$07,$07,$07,$07,$07,$07,$06,$14,$07,$07,$07,$07,$07,$07,
1019 $07,$06,$07,$06,$06,$06,$06,$07,$07,$06,$07,$14,$07,$14,$07,$08,
1020 $23,$07,$07,$06,$07,$06,$06,$07,$07,$14,$05,$07,$14,$07,$06,$06,
1021 $07,$07,$06,$06,$15,$07,$06,$07,$07,$06,$06,$06,$32,$06,$07,$07,
1022 $06,$07,$06,$08,$07,$07,$07,$07,$08,$06,$06,$06,$07,$05,$06,$06,
1023 $06,$08,$07,$06,$06,$06,$07,$07,$06,$07,$06,$07,$07,$06,$07,$08,
1024 $07,$05,$06,$07,$06,$06,$16,$16,$06,$06,$06,$08,$08,$06,$08,$08,
1025 $08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,
1026 $38,$08,$08,$38,$08,$08,$38,$28,$28,$28,$08,$08,$28,$08,$08,$08,
1027 $08,$08,$08,$28,$38,$38,$28,$08,$08,$08,$38,$08,$08,$08,$48,$08,
1028 $07,$06,$07,$07,$07,$07,$07,$07,$06,$07,$07,$06,$08,$08,$06,$06,
1029 $06,$06,$06,$06,$35,$05,$06,$07,$15,$32,$32,$08,$15,$15,$24,$08
1030 );
1033 function createFontTexture (constref font: array of Byte; constref fontwdt: array of Byte; prop: Boolean): GLuint;
1034 const
1035 Width = 16*8;
1036 Height = 16*8;
1037 var
1038 tex, tpp: PByte;
1039 b: Byte;
1040 cc: Integer;
1041 x, y, dx, dy: Integer;
1042 begin
1043 GetMem(tex, Width*Height*4);
1045 for cc := 0 to 255 do
1046 begin
1047 x := (cc mod 16)*8;
1048 y := (cc div 16)*8;
1049 for dy := 0 to 7 do
1050 begin
1051 b := font[cc*8+dy];
1052 if prop then b := b shl (fontwdt[cc] shr 4);
1053 tpp := tex+((y+dy)*(Width*4))+x*4;
1054 for dx := 0 to 7 do
1055 begin
1056 if ((b and $80) <> 0) then
1057 begin
1058 tpp^ := 255; Inc(tpp);
1059 tpp^ := 255; Inc(tpp);
1060 tpp^ := 255; Inc(tpp);
1061 tpp^ := 255; Inc(tpp);
1062 end
1063 else
1064 begin
1065 tpp^ := 0; Inc(tpp);
1066 tpp^ := 0; Inc(tpp);
1067 tpp^ := 0; Inc(tpp);
1068 tpp^ := 0; Inc(tpp);
1069 end;
1070 b := (b and $7f) shl 1;
1071 end;
1072 end;
1073 end;
1075 glGenTextures(1, @result);
1076 if (result = 0) then raise Exception.Create('can''t create Holmes font texture');
1078 glBindTexture(GL_TEXTURE_2D, result);
1079 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
1080 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
1081 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1082 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1084 //GLfloat[4] bclr = 0.0;
1085 //glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bclr.ptr);
1087 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA{gltt}, GL_UNSIGNED_BYTE, tex);
1088 glFlush();
1090 //FreeMem(tex);
1091 end;
1094 var
1095 font6texid: GLuint = 0;
1096 font8texid: GLuint = 0;
1097 prfont6texid: GLuint = 0;
1098 prfont8texid: GLuint = 0;
1101 procedure deleteFonts ();
1102 begin
1103 if (font6texid <> 0) then glDeleteTextures(1, @font6texid);
1104 if (font8texid <> 0) then glDeleteTextures(1, @font8texid);
1105 if (prfont6texid <> 0) then glDeleteTextures(1, @prfont6texid);
1106 if (prfont8texid <> 0) then glDeleteTextures(1, @prfont8texid);
1107 font6texid := 0;
1108 font8texid := 0;
1109 prfont6texid := 0;
1110 prfont8texid := 0;
1111 end;
1114 procedure createFonts ();
1115 begin
1116 if (font6texid = 0) then font6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, false);
1117 if (font8texid = 0) then font8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, false);
1118 if (prfont6texid = 0) then prfont6texid := createFontTexture(kgiFont6, kgiFont6PropWidth, true);
1119 if (prfont8texid = 0) then prfont8texid := createFontTexture(kgiFont8, kgiFont8PropWidth, true);
1120 end;
1123 // ////////////////////////////////////////////////////////////////////////// //
1124 procedure TScissorSave.save (enableScissoring: Boolean);
1125 begin
1126 wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0);
1127 if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]);
1128 //conwritefln('(%d,%d)-(%d,%d)', [scxywh[0], scxywh[1], scxywh[2], scxywh[3]]);
1129 if enableScissoring and (not wassc) then glEnable(GL_SCISSOR_TEST);
1130 end;
1132 procedure TScissorSave.restore ();
1133 begin
1134 glScissor(scxywh[0], scxywh[1], scxywh[2], scxywh[3]);
1135 if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST);
1136 end;
1138 procedure TScissorSave.combineRect (x, y, w, h: Integer);
1139 //var ox, oy, ow, oh: Integer;
1140 begin
1141 if (w < 1) or (h < 1) then begin glScissor(0, 0, 0, 0); exit; end;
1142 y := gScrHeight-(y+h);
1143 //ox := x; oy := y; ow := w; oh := h;
1144 if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then
1145 begin
1146 //writeln('oops: COMBINE: old=(', ox, ',', oy, ')-(', ox+ow-1, ',', oy+oh-1, '); sci: (', scxywh[0], ',', scxywh[1], ')-(', scxywh[0]+scxywh[2]-1, ',', scxywh[1]+scxywh[3]-1, ')');
1147 //writeln('oops: COMBINE: oldx=<', ox, '-', ox+ow-1, '>; oldy=<', oy, ',', oy+oh-1, '> : scix=<', scxywh[0], '-', scxywh[0]+scxywh[2]-1, '>; sciy=<', scxywh[1], '-', scxywh[1]+scxywh[3]-1, '>');
1148 glScissor(0, 0, 0, 0);
1149 end
1150 else
1151 begin
1152 glScissor(x, y, w, h);
1153 end;
1154 end;
1156 //TODO: overflow checks
1157 function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean;
1158 var
1159 ex0, ey0: Integer;
1160 ex1, ey1: Integer;
1161 begin
1162 result := false;
1163 if (w0 < 1) or (h0 < 1) or (w1 < 1) or (h1 < 1) then exit; // at least one rect is null
1164 // check for intersection
1165 ex0 := x0+w0;
1166 ey0 := y0+h0;
1167 ex1 := x1+w1;
1168 ey1 := y1+h1;
1169 if (ex0 <= x1) or (ey0 <= y1) or (ex1 <= x0) or (ey1 <= y0) then exit;
1170 if (x0 >= ex1) or (y0 >= ey1) or (x1 >= ex0) or (y1 >= ey0) then exit;
1171 // ok, intersects
1172 if (x0 < x1) then x0 := x1;
1173 if (y0 < y1) then y0 := y1;
1174 if (ex0 > ex1) then ex0 := ex1;
1175 if (ey0 > ey1) then ey0 := ey1;
1176 w0 := ex0-x0;
1177 h0 := ey0-y0;
1178 result := (w0 > 0) and (h0 > 0);
1179 end;
1182 // ////////////////////////////////////////////////////////////////////////// //
1183 procedure normRGBA (var r, g, b, a: Integer); inline;
1184 begin
1185 if (a < 0) then a := 0 else if (a > 255) then a := 255;
1186 if (r < 0) then r := 0 else if (r > 255) then r := 255;
1187 if (g < 0) then g := 0 else if (g > 255) then g := 255;
1188 if (b < 0) then b := 0 else if (b > 255) then b := 255;
1189 end;
1191 // returns `false` if the color is transparent
1192 function setupGLColor (r, g, b, a: Integer): Boolean;
1193 begin
1194 normRGBA(r, g, b, a);
1195 if (a < 255) then
1196 begin
1197 if (a = 0) then begin result := false; exit; end;
1198 glEnable(GL_BLEND);
1199 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1200 end
1201 else
1202 begin
1203 glDisable(GL_BLEND);
1204 end;
1205 glColor4ub(Byte(r), Byte(g), Byte(b), Byte(a));
1206 result := true;
1207 end;
1209 // returns `false` if the color is transparent
1210 function setupGLColor (constref clr: TGxRGBA): Boolean;
1211 begin
1212 if (clr.a < 255) then
1213 begin
1214 if (clr.a = 0) then begin result := false; exit; end;
1215 glEnable(GL_BLEND);
1216 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1217 end
1218 else
1219 begin
1220 glDisable(GL_BLEND);
1221 end;
1222 glColor4ub(clr.r, clr.g, clr.b, clr.a);
1223 result := true;
1224 end;
1226 function isScaled (): Boolean;
1227 var
1228 mt: packed array [0..15] of Double;
1229 begin
1230 glGetDoublev(GL_MODELVIEW_MATRIX, @mt[0]);
1231 result := (mt[0] <> 1.0) or (mt[1*4+1] <> 1.0);
1232 end;
1235 // ////////////////////////////////////////////////////////////////////////// //
1236 function textWidth6 (const s: AnsiString): Integer;
1237 var
1238 f: Integer;
1239 begin
1240 result := 0;
1241 for f := 1 to Length(s) do Inc(result, Integer(kgiFont6PropWidth[Integer(s[f])] and $0f)+1);
1242 if (result > 0) then Dec(result); // don't count last empty pixel
1243 end;
1246 function textWidth8 (const s: AnsiString): Integer;
1247 var
1248 f: Integer;
1249 begin
1250 result := 0;
1251 for f := 1 to Length(s) do Inc(result, Integer(kgiFont8PropWidth[Integer(s[f])] and $0f)+1);
1252 if (result > 0) then Dec(result); // don't count last empty pixel
1253 end;
1256 // return width (including last empty pixel)
1257 function drawTextInternal (wdt, x, y: Integer; const s: AnsiString; constref clr: TGxRGBA; tid: GLuint; constref fontwdt: array of Byte; prop: Boolean): Integer;
1258 var
1259 f, c: Integer;
1260 tx, ty: Integer;
1261 begin
1262 result := 0;
1263 if (Length(s) = 0) then exit;
1264 if not setupGLColor(clr) then exit;
1266 glEnable(GL_ALPHA_TEST);
1267 glAlphaFunc(GL_NOTEQUAL, 0.0);
1268 glEnable(GL_TEXTURE_2D);
1269 glBindTexture(GL_TEXTURE_2D, tid);
1271 for f := 1 to Length(s) do
1272 begin
1273 c := Integer(s[f]) and $ff;
1274 tx := (c mod 16)*8;
1275 ty := (c div 16)*8;
1276 glBegin(GL_QUADS);
1277 glTexCoord2f((tx+0)/128.0, (ty+0)/128.0); glVertex2i(x+0, y+0); // top-left
1278 glTexCoord2f((tx+8)/128.0, (ty+0)/128.0); glVertex2i(x+8, y+0); // top-right
1279 glTexCoord2f((tx+8)/128.0, (ty+8)/128.0); glVertex2i(x+8, y+8); // bottom-right
1280 glTexCoord2f((tx+0)/128.0, (ty+8)/128.0); glVertex2i(x+0, y+8); // bottom-left
1281 glEnd();
1282 if prop then
1283 begin
1284 x += Integer(fontwdt[c] and $0f)+1;
1285 result += Integer(fontwdt[c] and $0f)+1;
1286 end
1287 else
1288 begin
1289 x += wdt;
1290 result += wdt;
1291 end;
1292 end;
1294 glDisable(GL_ALPHA_TEST);
1295 glDisable(GL_BLEND);
1296 glDisable(GL_TEXTURE_2D);
1297 glColor4f(1, 1, 1, 1);
1298 glBindTexture(GL_TEXTURE_2D, 0);
1299 end;
1302 // ////////////////////////////////////////////////////////////////////////// //
1303 procedure drawHLine (x, y, len: Integer; constref clr: TGxRGBA);
1304 begin
1305 if (len < 1) then exit;
1306 if not setupGLColor(clr) then exit;
1307 glDisable(GL_TEXTURE_2D);
1308 if (not isScaled) then
1309 begin
1310 glLineWidth(1);
1311 glBegin(GL_LINES);
1312 glVertex2f(x+0.375, y+0.375);
1313 glVertex2f(x+len+0.375, y+0.375);
1314 glEnd();
1315 end
1316 else
1317 begin
1318 glBegin(GL_QUADS);
1319 glVertex2i(x, y);
1320 glVertex2i(x+len, y);
1321 glVertex2i(x+len, y+1);
1322 glVertex2i(x, y+1);
1323 glEnd();
1324 end;
1325 end;
1328 procedure drawVLine (x, y, len: Integer; constref clr: TGxRGBA);
1329 begin
1330 if (len < 1) then exit;
1331 if not setupGLColor(clr) then exit;
1332 glDisable(GL_TEXTURE_2D);
1333 if (not isScaled) then
1334 begin
1335 glLineWidth(1);
1336 glBegin(GL_LINES);
1337 glVertex2f(x+0.375, y+0.375);
1338 glVertex2f(x+0.375, y+len+0.375);
1339 glEnd();
1340 end
1341 else
1342 begin
1343 glBegin(GL_QUADS);
1344 glVertex2i(x, y);
1345 glVertex2i(x, y+len);
1346 glVertex2i(x+1, y+len);
1347 glVertex2i(x+1, y);
1348 glEnd();
1349 end;
1350 end;
1353 procedure drawLine (x1, y1, x2, y2: Integer; constref clr: TGxRGBA);
1354 begin
1355 if not setupGLColor(clr) then exit;
1357 glDisable(GL_TEXTURE_2D);
1359 glLineWidth(1);
1360 glPointSize(1);
1362 if (not isScaled) then
1363 begin
1364 glLineWidth(1);
1365 glBegin(GL_LINES);
1366 glVertex2f(x1+0.375, y1+0.375);
1367 glVertex2f(x2+0.375, y2+0.375);
1368 glEnd();
1370 if (x1 <> x2) or (y1 <> y2) then
1371 begin
1372 glBegin(GL_POINTS);
1373 glVertex2f(x2+0.375, y2+0.375);
1374 glEnd();
1375 end;
1376 end
1377 else
1378 begin
1379 glLineWidth(1);
1380 glBegin(GL_LINES);
1381 glVertex2i(x1, y1);
1382 glVertex2i(x2, y2);
1383 // draw last point
1384 glVertex2i(x2, y2);
1385 glVertex2i(x2+1, y2+1);
1386 glEnd();
1387 end;
1389 glColor4f(1, 1, 1, 1);
1390 glDisable(GL_BLEND);
1391 end;
1394 procedure drawRect (x, y, w, h: Integer; constref clr: TGxRGBA);
1395 begin
1396 if (w < 0) or (h < 0) then exit;
1397 if not setupGLColor(clr) then exit;
1398 glDisable(GL_TEXTURE_2D);
1399 glLineWidth(1);
1400 glDisable(GL_LINE_SMOOTH);
1401 glDisable(GL_POLYGON_SMOOTH);
1402 if (w = 1) and (h = 1) then
1403 begin
1404 glBegin(GL_POINTS);
1405 glVertex2f(x+0.375, y+0.375);
1406 glEnd();
1407 end
1408 else
1409 begin
1410 glLineWidth(1);
1411 glBegin(GL_LINES);
1412 glVertex2i(x, y); glVertex2i(x+w, y); // top
1413 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1414 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1415 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1416 glEnd();
1417 end;
1418 //glRect(x, y, x+w, y+h);
1419 glColor4f(1, 1, 1, 1);
1420 glDisable(GL_BLEND);
1421 end;
1424 procedure drawRectUI (x, y, w, h: Integer; constref clr: TGxRGBA);
1425 procedure hline (x, y, len: Integer);
1426 begin
1427 if (len < 1) then exit;
1428 glBegin(GL_QUADS);
1429 glVertex2i(x, y);
1430 glVertex2i(x+len, y);
1431 glVertex2i(x+len, y+1);
1432 glVertex2i(x, y+1);
1433 glEnd();
1434 end;
1436 procedure vline (x, y, len: Integer);
1437 begin
1438 if (len < 1) then exit;
1439 glBegin(GL_QUADS);
1440 glVertex2i(x, y);
1441 glVertex2i(x, y+len);
1442 glVertex2i(x+1, y+len);
1443 glVertex2i(x+1, y);
1444 glEnd();
1445 end;
1447 var
1448 scaled: Boolean;
1449 begin
1450 if (w < 0) or (h < 0) then exit;
1451 if not setupGLColor(clr) then exit;
1452 glDisable(GL_TEXTURE_2D);
1453 glLineWidth(1);
1454 glDisable(GL_LINE_SMOOTH);
1455 glDisable(GL_POLYGON_SMOOTH);
1456 scaled := isScaled();
1457 if (w = 1) and (h = 1) then
1458 begin
1459 glBegin(GL_POINTS);
1460 if scaled then glVertex2i(x, y) else glVertex2f(x+0.375, y+0.375);
1461 glEnd();
1462 end
1463 else
1464 begin
1465 if not scaled then
1466 begin
1467 glLineWidth(1);
1468 glBegin(GL_LINES);
1469 glVertex2i(x, y); glVertex2i(x+w, y); // top
1470 glVertex2i(x, y+h-1); glVertex2i(x+w, y+h-1); // bottom
1471 glVertex2f(x+0.375, y+1); glVertex2f(x+0.375, y+h-1); // left
1472 glVertex2f(x+w-1+0.375, y+1); glVertex2f(x+w-1+0.375, y+h-1); // right
1473 glEnd();
1474 end
1475 else
1476 begin
1477 hline(x, y, w);
1478 hline(x, y+h-1, w);
1479 vline(x, y+1, h-2);
1480 vline(x+w-1, y+1, h-2);
1481 end;
1482 end;
1483 //glRect(x, y, x+w, y+h);
1484 glColor4f(1, 1, 1, 1);
1485 glDisable(GL_BLEND);
1486 end;
1489 procedure darkenRect (x, y, w, h: Integer; a: Integer);
1490 begin
1491 if (w < 0) or (h < 0) then exit;
1492 if (a < 0) then a := 0;
1493 if (a >= 255) then exit;
1494 glEnable(GL_BLEND);
1495 glBlendFunc(GL_ZERO, GL_SRC_ALPHA);
1496 glDisable(GL_LINE_SMOOTH);
1497 glDisable(GL_POLYGON_SMOOTH);
1498 glDisable(GL_TEXTURE_2D);
1499 glColor4f(0.0, 0.0, 0.0, a/255.0);
1500 glBegin(GL_QUADS);
1501 glVertex2i(x, y);
1502 glVertex2i(x+w, y);
1503 glVertex2i(x+w, y+h);
1504 glVertex2i(x, y+h);
1505 glEnd();
1506 //glRect(x, y, x+w, y+h);
1507 glColor4f(1, 1, 1, 1);
1508 glDisable(GL_BLEND);
1509 //glBlendEquation(GL_FUNC_ADD);
1510 end;
1513 procedure fillRect (x, y, w, h: Integer; constref clr: TGxRGBA);
1514 begin
1515 if (w < 0) or (h < 0) then exit;
1516 if not setupGLColor(clr) then exit;
1517 glDisable(GL_LINE_SMOOTH);
1518 glDisable(GL_POLYGON_SMOOTH);
1519 glDisable(GL_TEXTURE_2D);
1520 glBegin(GL_QUADS);
1521 glVertex2f(x, y);
1522 glVertex2f(x+w, y);
1523 glVertex2f(x+w, y+h);
1524 glVertex2f(x, y+h);
1525 glEnd();
1526 glColor4f(1, 1, 1, 1);
1527 glDisable(GL_BLEND);
1528 end;
1531 // ////////////////////////////////////////////////////////////////////////// //
1532 function drawText6 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1533 begin
1534 if (font6texid = 0) then createFonts();
1535 drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
1536 result := Length(s)*6;
1537 end;
1539 function drawText8 (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1540 begin
1541 if (font8texid = 0) then createFonts();
1542 drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
1543 result := Length(s)*8;
1544 end;
1546 function drawText6Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1547 begin
1548 if (prfont6texid = 0) then createFonts();
1549 result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
1550 end;
1552 function drawText8Prop (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1553 begin
1554 if (prfont8texid = 0) then createFonts();
1555 result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
1556 end;
1559 // ////////////////////////////////////////////////////////////////////////// //
1560 // x-centered at `x`
1561 function drawText6XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1562 begin
1563 if (font6texid = 0) then createFonts();
1564 x -= Length(s)*6 div 2;
1565 drawTextInternal(6, x, y, s, clr, font6texid, kgiFont6PropWidth, false);
1566 result := Length(s)*6;
1567 end;
1569 function drawText8XC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1570 begin
1571 if (font8texid = 0) then createFonts();
1572 x -= Length(s)*8 div 2;
1573 drawTextInternal(8, x, y, s, clr, font8texid, kgiFont8PropWidth, false);
1574 result := Length(s)*8;
1575 end;
1577 function drawText6PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1578 begin
1579 if (prfont6texid = 0) then createFonts();
1580 x -= textWidth6(s) div 2;
1581 result := drawTextInternal(6, x, y, s, clr, prfont6texid, kgiFont6PropWidth, true);
1582 end;
1584 function drawText8PropXC (x, y: Integer; const s: AnsiString; constref clr: TGxRGBA): Integer;
1585 begin
1586 if (prfont8texid = 0) then createFonts();
1587 x -= textWidth8(s) div 2;
1588 result := drawTextInternal(8, x, y, s, clr, prfont8texid, kgiFont8PropWidth, true);
1589 end;
1592 // ////////////////////////////////////////////////////////////////////////// //
1593 procedure oglRestoreMode (doClear: Boolean);
1594 begin
1595 oglSetup2D(gScrWidth, gScrHeight);
1596 glScissor(0, 0, gScrWidth, gScrHeight);
1598 glBindTexture(GL_TEXTURE_2D, 0);
1599 glDisable(GL_BLEND);
1600 glDisable(GL_TEXTURE_2D);
1601 glDisable(GL_STENCIL_TEST);
1602 glDisable(GL_SCISSOR_TEST);
1603 glDisable(GL_LIGHTING);
1604 glDisable(GL_DEPTH_TEST);
1605 glDisable(GL_CULL_FACE);
1606 glDisable(GL_LINE_SMOOTH);
1607 glDisable(GL_POINT_SMOOTH);
1608 glLineWidth(1);
1609 glPointSize(1);
1610 glColor4f(1, 1, 1, 1);
1612 if doClear then
1613 begin
1614 glClearColor(0, 0, 0, 0);
1615 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT or GL_ACCUM_BUFFER_BIT or GL_STENCIL_BUFFER_BIT);
1616 end;
1618 // scale everything
1619 glMatrixMode(GL_MODELVIEW);
1620 glLoadIdentity();
1621 //glScalef(4, 4, 1);
1622 end;
1625 procedure onWinFocus (); begin end;
1627 procedure onWinBlur (); begin resetKMState(true); end;
1629 procedure onPreRender (); begin oglRestoreMode(gGfxDoClear); end;
1631 procedure onPostRender (); begin oglRestoreMode(false); oglDrawCursor(); end;
1633 procedure onInit ();
1634 begin
1635 oglSetup2D(gScrWidth, gScrHeight);
1637 createCursorTexture();
1638 createFonts();
1639 end;
1641 procedure onDeinit ();
1642 begin
1643 resetKMState(false);
1644 if (curtexid <> 0) then glDeleteTextures(1, @curtexid);
1645 curtexid := 0;
1646 deleteFonts();
1647 curButState := 0;
1648 curModState := 0;
1649 curMsX := 0;
1650 curMsY := 0;
1651 end;
1654 // ////////////////////////////////////////////////////////////////////////// //
1655 begin
1656 evSDLCB := onSDLEvent;
1657 winFocusCB := onWinFocus;
1658 winBlurCB := onWinBlur;
1659 prerenderFrameCB := onPreRender;
1660 postrenderFrameCB := onPostRender;
1661 oglInitCB := onInit;
1662 oglDeinitCB := onDeinit;
1663 end.