Update ReadMe
[d2df-sdl.git] / src / game / sdl2 / system.inc
blobbe089fd5a37536b32ceba52c3aa1ad809cd468f6
1 (* Copyright (C) 2016 - The Doom2D.org team & involved community members <http://www.doom2d.org>.
2  * This file is part of Doom2D Forever.
3  *
4  * This program is free software: you can redistribute it and/or modify it under the terms of
5  * the GNU General Public License as published by the Free Software Foundation, version 3 of
6  * the License ONLY.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10  * See the GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License along with this program.
13  * If not, see <http://www.gnu.org/licenses/>.
14  *)
16 implementation
18 uses
19   SysUtils, Math, ctypes,
20   SDL2, {$INCLUDE ../../nogl/noGLuses.inc}
21   e_log, e_graphics, e_input, {$IFDEF ENABLE_SOUND}e_sound,{$ENDIF}
22 {$IFDEF ENABLE_HOLMES}
23   g_holmes, sdlcarcass, fui_ctls,
24 {$ENDIF}
25   g_main, g_basic, g_options, g_language,
26   g_window, g_console, g_game, g_menu, g_gui, g_touch;
28 var
29   window: PSDL_Window;
30   context: TSDL_GLContext;
31   display: Integer;
32   wx: Integer = SDL_WINDOWPOS_CENTERED;
33   wy: Integer = SDL_WINDOWPOS_CENTERED;
34   wc: Boolean;
36   JoystickHandle: array [0..e_MaxJoys - 1] of PSDL_Joystick;
37   JoystickHatState: array [0..e_MaxJoys - 1, 0..e_MaxJoyHats - 1, HAT_LEFT..HAT_DOWN] of Boolean;
38   JoystickZeroAxes: array [0..e_MaxJoys - 1, 0..e_MaxJoyAxes - 1] of Integer;
40 ////////////////////////////////////////////////////////////////////////////////////////////////////
41 ////////////////////////////////////////////////////////////////////////////////////////////////////
43 function LoadGL (): Boolean;
44 var
45   fbo: Boolean;
46   ltmp: Integer;
47 begin
48   Result := True;
50 {$IFNDEF NOGL_INIT}
51   fbo := Load_GL_ARB_framebuffer_object();
52 {$ELSE}
53   nogl_Init();
54   fbo := nogl_ExtensionSupported('GL_OES_framebuffer_object');
55 {$ENDIF}
57   if glRenderToFBO and not fbo then
58   begin
59     e_LogWriteln('GL: framebuffer objects not supported; disabling FBO rendering');
60     glRenderToFBO := False;
61   end;
63   if SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, @ltmp) = 0 then
64   begin
65     e_LogWritefln('stencil buffer size: %s', [ltmp]);
66     gwin_has_stencil := ltmp > 0;
67   end;
68 end;
70 procedure FreeGL ();
71 begin
72 {$IFDEF NOGL_INIT}
73   nogl_Quit();
74 {$ENDIF}
75 end;
77 ////////////////////////////////////////////////////////////////////////////////////////////////////
79 function GetTitle (): AnsiString;
80 var
81   info: AnsiString;
82 begin
83   info := g_GetBuildHash(False);
84   if info = 'custom build' then
85     info += ' by ' + g_GetBuilderName() + ' ' + GAME_BUILDDATE + ' ' + GAME_BUILDTIME;
87   Result := Format(GAME_NAME+' (SDL2, %s)', [info]);
88 end;
90 procedure UpdateSize (w, h: Integer);
91 begin
92   gWinSizeX := w;
93   gWinSizeY := h;
94   gRC_Width := w;
95   gRC_Height := h;
97   if glRenderToFBO then
98   begin
99     // store real window size in gWinSize, downscale resolution now
100     w := Round(w / r_pixel_scale);
101     h := Round(h / r_pixel_scale);
102     if not e_ResizeFramebuffer(w, h) then
103     begin
104       e_LogWriteln('GL: could not create framebuffer, falling back to --no-fbo');
105       glRenderToFBO := False;
106       w := gWinSizeX;
107       h := gWinSizeY;
108     end;
109   end;
111   gScreenWidth := w;
112   gScreenHeight := h;
114 {$IFDEF ENABLE_HOLMES}
115   fuiScrWdt := w;
116   fuiScrHgt := h;
117 {$ENDIF}
119   e_ResizeWindow(w, h);
120   e_InitGL();
121   g_Game_SetupScreenSize();
123 {$IFNDEF ANDROID}
124   // This will fix menu reset on keyboard showing
125   g_Menu_Reset();
126 {$ENDIF}
128   g_Game_ClearLoading();
130 {$IFDEF ENABLE_HOLMES}
131   if Assigned(oglInitCB) then oglInitCB();
132 {$ENDIF}
133 end;
135 function InitWindow (w, h, bpp: Integer; fullScreen, maximized: Boolean): Boolean;
137   flags: UInt32;
138   x, y: cint;
139 begin
140   // NB: On window close, do this for FlexUI: if @oglDeinitCB <> nil then oglDeinitCB();
141   Result := False;
142   e_LogWritefln('InitWindow %s %s %s %s', [w, h, bpp, fullScreen]);
144   if window = nil then
145   begin
146   {$IFNDEF USE_GLES1}
147     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
148     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
149     SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
150     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
151     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
152     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
153     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
155     // lights; it is enough to have 1-bit stencil buffer for lighting, but...
156     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
157   {$ELSE}
158     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
159     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
160     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
161   {$ENDIF}
163     flags := SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE;
164     if fullScreen then flags := flags or SDL_WINDOW_FULLSCREEN;
165     if maximized then flags := flags or SDL_WINDOW_MAXIMIZED;
167     if wc then
168     begin
169       x := SDL_WINDOWPOS_CENTERED;
170       y := SDL_WINDOWPOS_CENTERED;
171     end
172     else
173     begin
174       x := wx;
175       y := wy;
176     end;
178     window := SDL_CreateWindow(PChar(GetTitle()), x, y, w, h, flags);
179     if window = nil then
180     begin
181       e_LogWritefln('SDL2: unable to create window: %s', [SDL_GetError()]);
182       Exit;
183     end;
185     context := SDL_GL_CreateContext(window);
186     if context = nil then
187     begin
188       // SDL_DestroyWindow(window);
189       e_LogWritefln('SDL2: unable to create OpenGL context: %s', [SDL_GetError()]);
190       Exit;
191     end;
193     if not LoadGL() then
194     begin
195       e_LogWriteln('GL: unable to load OpenGL functions', TMsgType.Fatal);
196       SDL_GL_DeleteContext(context);
197       context := nil;
198       Exit;
199     end;
200   end
201   else
202   begin
203     if fullScreen
204       then flags := SDL_WINDOW_FULLSCREEN
205       else flags := 0;
207     SDL_SetWindowFullscreen(window, flags);
208     SDL_SetWindowSize(window, w, h);
209     if maximized then SDL_MaximizeWindow(window);
211     // always reset to center when changing fullscreen->windowed for safety purposes
212     if wc or (gFullscreen and not fullscreen) or (gWinMaximized and not maximized) then
213     begin
214       x := SDL_WINDOWPOS_CENTERED;
215       y := SDL_WINDOWPOS_CENTERED;
216     end
217     else
218     begin
219       x := wx;
220       y := wy;
221     end;
223     SDL_SetWindowPosition(window, x, y);
224   end;
226   if not fullscreen and not maximized and not wc then
227   begin
228     SDL_GetWindowPosition(window, @x, @y);
229     wx := x;
230     wy := y;
231   end;
233   gFullScreen := fullscreen;
234   gWinMaximized := maximized;
235   gRC_FullScreen := fullscreen;
236   gRC_Maximized := maximized;
238   UpdateSize(w, h);
239   Result := True;
240 end;
242 ////////////////////////////////////////////////////////////////////////////////////////////////////
243 ////////////////////////////////////////////////////////////////////////////////////////////////////
245 procedure HandleJoyButton (var ev: TSDL_JoyButtonEvent);
247   down: Boolean;
248   key: Integer;
249 begin
250   if (ev.which < e_MaxJoys) and (ev.button < e_MaxJoyBtns) then
251   begin
252     key := e_JoyButtonToKey(ev.which, ev.button);
253     down := ev.type_ = SDL_JOYBUTTONDOWN;
254     if g_dbg_input then
255       e_LogWritefln('Input Debug: jbutton, joy=%s, button=%s, keycode=%s, press=%s', [ev.which, ev.button, key, down]);
256     e_KeyUpDown(key, down);
257     g_Console_ProcessBind(key, down);
258   end
259   else
260   begin
261     if g_dbg_input then
262     begin
263       down := ev.type_ = SDL_JOYBUTTONDOWN;
264       e_LogWritefln('Input Debug: NOT IN RANGE! jbutton, joy=%s, button=%s, press=%s', [ev.which, ev.button, down]);
265     end;
266   end;
267 end;
269 procedure HandleJoyAxis (var ev: TSDL_JoyAxisEvent);
271   key, minuskey: Integer;
272 begin
273   if (ev.which < e_MaxJoys) and (ev.axis < e_MaxJoyAxes) then
274   begin
275     key := e_JoyAxisToKey(ev.which, ev.axis, AX_PLUS);
276     minuskey := e_JoyAxisToKey(ev.which, ev.axis, AX_MINUS);
278     if g_dbg_input then
279       e_LogWritefln('Input Debug: jaxis, joy=%s, axis=%s, value=%s, zeroaxes=%s, deadzone=%s',
280         [ev.which, ev.axis, ev.value, JoystickZeroAxes[ev.which, ev.axis], e_JoystickDeadzones[ev.which]]);
282     if ev.value < (JoystickZeroAxes[ev.which, ev.axis] - e_JoystickDeadzones[ev.which]) then
283     begin
284       if e_KeyPressed(key) then
285       begin
286         e_KeyUpDown(key, False);
287         g_Console_ProcessBind(key, False);
288       end;
289       e_KeyUpDown(minuskey, True);
290       g_Console_ProcessBind(minuskey, True);
291     end
292     else if ev.value > (JoystickZeroAxes[ev.which, ev.axis] + e_JoystickDeadzones[ev.which]) then
293     begin
294       if e_KeyPressed(minuskey) then
295       begin
296         e_KeyUpDown(minuskey, False);
297         g_Console_ProcessBind(minuskey, False);
298       end;
299       e_KeyUpDown(key, True);
300       g_Console_ProcessBind(key, True);
301     end
302     else
303     begin
304       if e_KeyPressed(minuskey) then
305       begin
306         e_KeyUpDown(minuskey, False);
307         g_Console_ProcessBind(minuskey, False);
308       end;
309       if e_KeyPressed(key) then
310       begin
311         e_KeyUpDown(key, False);
312         g_Console_ProcessBind(key, False);
313       end;
314     end;
315   end
316   else
317   begin
318     if g_dbg_input then
319       e_LogWritefln('Input Debug: NOT IN RANGE! jaxis, joy=%s, axis=%s, value=%s, zeroaxes=%s, deadzone=%s',
320         [ev.which, ev.axis, ev.value, JoystickZeroAxes[ev.which, ev.axis], e_JoystickDeadzones[ev.which]]);
321   end;
322 end;
324 procedure HandleJoyHat (var ev: TSDL_JoyHatEvent);
326   down: Boolean;
327   i, key: Integer;
328   hat: array [HAT_LEFT..HAT_DOWN] of Boolean;
329 begin
330   if (ev.which < e_MaxJoys) and (ev.hat < e_MaxJoyHats) then
331   begin
332     if g_dbg_input then
333       e_LogWritefln('Input Debug: jhat, joy=%s, hat=%s, value=%s', [ev.which, ev.hat, ev.value]);
335     hat[HAT_UP] := LongBool(ev.value and SDL_HAT_UP);
336     hat[HAT_DOWN] := LongBool(ev.value and SDL_HAT_DOWN);
337     hat[HAT_LEFT] := LongBool(ev.value and SDL_HAT_LEFT);
338     hat[HAT_RIGHT] := LongBool(ev.value and SDL_HAT_RIGHT);
340     for i := HAT_LEFT to HAT_DOWN do
341     begin
342       if JoystickHatState[ev.which, ev.hat, i] <> hat[i] then
343       begin
344         down := hat[i];
345         key := e_JoyHatToKey(ev.which, ev.hat, i);
346         e_KeyUpDown(key, down);
347         g_Console_ProcessBind(key, down);
348       end;
349     end;
350     JoystickHatState[ev.which, ev.hat] := hat;
351   end
352   else
353   begin
354     if g_dbg_input then
355       e_LogWritefln('Input Debug: NOT IN RANGE! jhat, joy=%s, hat=%s, value=%s', [ev.which, ev.hat, ev.value]);
356   end;
357 end;
359 procedure HandleJoyAdd (var ev: TSDL_JoyDeviceEvent);
361   i: Integer;
362 begin
363   if ev.which < e_MaxJoys then
364   begin
365     JoystickHandle[ev.which] := SDL_JoystickOpen(ev.which);
366     if JoystickHandle[ev.which] <> nil then
367     begin
368       e_LogWritefln('Added Joystick %s', [ev.which]);
369       e_JoystickAvailable[ev.which] := True;
370       for i := 0 to Min(SDL_JoystickNumAxes(JoystickHandle[ev.which]), e_MaxJoyAxes) - 1 do
371         JoystickZeroAxes[ev.which, i] := SDL_JoystickGetAxis(JoystickHandle[ev.which], i);
372     end
373     else
374     begin
375       e_LogWritefln('Warning! Failed to open Joystick %s', [ev.which]);
376     end;
377   end
378   else
379   begin
380     e_LogWritefln('Warning! Added Joystick %s, but we support only <= %s', [ev.which, e_MaxJoys]);
381   end;
382 end;
384 procedure HandleJoyRemove (var ev: TSDL_JoyDeviceEvent);
385 begin
386   e_LogWritefln('Removed Joystick %s', [ev.which]);
387   if ev.which < e_MaxJoys then
388   begin
389     e_JoystickAvailable[ev.which] := False;
390     if JoystickHandle[ev.which] <> nil then
391     begin
392       SDL_JoystickClose(JoystickHandle[ev.which]);
393       JoystickHandle[ev.which] := nil;
394     end;
395   end;
396 end;
398 ////////////////////////////////////////////////////////////////////////////////////////////////////
400 function HandleWindow (var ev: TSDL_WindowEvent): Boolean;
401 begin
402   Result := False;
403   if g_dbg_input then
404     e_LogWritefln('Window Event: event = %s, data1 = %s, data2 = %s', [ev.event, ev.data1, ev.data2]);
406   case ev.event of
407     SDL_WINDOWEVENT_RESIZED: UpdateSize(ev.data1, ev.data2);
408     SDL_WINDOWEVENT_EXPOSED: sys_Repaint();
409     SDL_WINDOWEVENT_CLOSE: Result := True;
411     SDL_WINDOWEVENT_MOVED:
412     begin
413       wx := ev.data1;
414       wy := ev.data2;
415     end;
417     SDL_WINDOWEVENT_FOCUS_LOST, SDL_WINDOWEVENT_MINIMIZED:
418     begin
419       e_UnpressAllKeys();
420     {$IFDEF ENABLE_SOUND}
421       if gMuteWhenInactive then e_MuteChannels(True);
422     {$ENDIF}
423     {$IFDEF ENABLE_HOLMES}
424       if Assigned(winBlurCB) then winBlurCB();
425     {$ENDIF}
426     end;
428     SDL_WINDOWEVENT_FOCUS_GAINED, SDL_WINDOWEVENT_MAXIMIZED, SDL_WINDOWEVENT_RESTORED:
429     begin
430       if ev.event = SDL_WINDOWEVENT_MAXIMIZED then
431       begin
432         gWinMaximized := True;
433         gRC_Maximized := True;
434       end
435       else if ev.event = SDL_WINDOWEVENT_RESTORED then
436       begin
437         gWinMaximized := False;
438         gRC_Maximized := False;
439       end;
440     {$IFDEF ENABLE_SOUND}
441       e_MuteChannels(False);
442     {$ENDIF}
443     {$IFDEF ENABLE_HOLMES}
444       if Assigned(winFocusCB) then winFocusCB();
445     {$ENDIF}
446     end;
447   end;
448 end;
450 procedure HandleKeyboard (var ev: TSDL_KeyboardEvent);
452   down: Boolean;
453   key: Integer;
454 begin
455   key := ev.keysym.scancode;
456   down := ev.type_ = SDL_KEYDOWN;
457   if key = SDL_SCANCODE_AC_BACK then
458     key := SDL_SCANCODE_ESCAPE;
460 {$IFDEF ENABLE_HOLMES}
461   if fuiOnSDLEvent(PSDL_Event(@ev)^) then
462   begin
463     // event eaten, but...
464     if not down then e_KeyUpDown(key, False);
465     Exit;
466   end;
467 {$ENDIF}
469   if ev._repeat = 0 then
470   begin
471     if g_dbg_input then
472       e_LogWritefln('Input Debug: keysym, press=%s, scancode=%s', [down, key]);
473     e_KeyUpDown(key, down);
474     g_Console_ProcessBind(key, down);
475   end
476   else
477   begin
478     if g_dbg_input then
479       e_LogWritefln('Input Debug: keyrep, scancode=%s', [key]);
480     g_Console_ProcessBindRepeat(key);
481   end;
482 end;
484 procedure HandleTextInput (var ev: TSDL_TextInputEvent);
486   ch: UnicodeChar;
487   sch: AnsiChar;
488 begin
489   Utf8ToUnicode(@ch, PChar(ev.text), 1);
490   sch := AnsiChar(wchar2win(ch));
492   if g_dbg_input then
493     e_LogWritefln('Input Debug: text, text="%s", ch = %s, sch = %s', [ev.text, Ord(ch), Ord(sch)]);
495   if IsValid1251(Word(ch)) and IsPrintable1251(ch) then
496     CharPress(sch);
497 end;
499 ////////////////////////////////////////////////////////////////////////////////////////////////////
500 ////////////////////////////////////////////////////////////////////////////////////////////////////
502 function sys_HandleEvents (): Boolean;
504   ev: TSDL_Event;
505 begin
506   Result := False;
507   ZeroMemory(@ev, SizeOf(ev));
509   // NB: Non-zero timeout is required here by the engine because this prevents abnormal CPU load.
510   while SDL_WaitEventTimeout(@ev, 1) <> 0 do
511   begin
512     case ev.type_ of
513       SDL_QUITEV: Result := True;
515       SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP: HandleJoyButton(ev.jbutton);
516       SDL_JOYAXISMOTION: HandleJoyAxis(ev.jaxis);
517       SDL_JOYHATMOTION: HandleJoyHat(ev.jhat);
518       SDL_JOYDEVICEADDED: HandleJoyAdd(ev.jdevice);
519       SDL_JOYDEVICEREMOVED: HandleJoyRemove(ev.jdevice);
521       SDL_WINDOWEVENT: Result := HandleWindow(ev.window);
522       SDL_KEYUP, SDL_KEYDOWN: HandleKeyboard(ev.key);
523       SDL_TEXTINPUT: HandleTextInput(ev.text);
524       SDL_FINGERMOTION, SDL_FINGERDOWN, SDL_FINGERUP: g_Touch_HandleEvent(ev.tfinger);
525     {$IFDEF ENABLE_HOLMES}
526       SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, SDL_MOUSEWHEEL, SDL_MOUSEMOTION: fuiOnSDLEvent(ev);
527     {$ENDIF}
528     end;
529   end;
530 end;
532 procedure sys_RequestQuit ();
534   ev: TSDL_Event;
535 begin
536   ev.type_ := SDL_QUITEV;
537   SDL_PushEvent(@ev);
538 end;
540 procedure sys_YieldTimeSlice ();
541 begin
542   // NB: At the moment, this is just a hint.
543   // https://www.freepascal.org/docs-html/3.2.2/rtl/system/threadswitch.html
544   ThreadSwitch();
545 end;
547 procedure sys_Delay (ms: Integer);
548 begin
549   SDL_Delay(ms);
550 end;
552 function sys_GetTicks (): Int64;
553 begin
554   Result := SDL_GetTicks64();
555 end;
557 ////////////////////////////////////////////////////////////////////////////////////////////////////
559 procedure sys_EnableVSync (yes: Boolean);
560 begin
561   SDL_GL_SetSwapInterval(Ord(yes));
562 end;
564 function sys_GetDisplayModes (bpp: Integer): SSArray;
566   m: TSDL_DisplayMode;
567   i, count, num, pw, ph: Integer;
568   c: AnsiChar;
569 begin
570   Result := nil;
571   num := SDL_GetNumDisplayModes(display);
572   if num < 0 then
573   begin
574     e_LogWritefln('SDL2: unable to get number of available display modes: %s', [SDL_GetError()]);
575     Exit;
576   end;
578   if num = 0 then
579     Exit;
581   e_LogWritefln('Video modes for display %s:', [display]);
582   SetLength(Result, num);
584   count := 0;
585   pw := 0;
586   ph := 0;
588   for i := 0 to num do
589   begin
590     SDL_GetDisplayMode(display, i, @m);
591     if (pw <> m.w) or (ph <> m.h) then
592     begin
593       c := '*';
594       pw := m.w;
595       ph := m.h;
596       Result[count] := IntToStr(m.w) + 'x' + IntToStr(m.h);
597       count += 1;
598     end
599     else
600       c := '-';
602     e_LogWritefln(c+' %sx%sx%s@%s', [m.w, m.h, SDL_BITSPERPIXEL(m.format), m.refresh_rate]);
603   end;
605   SetLength(Result, count);
606 end;
608 function sys_SetDisplayMode (w, h, bpp: Integer; fullScreen, maximized: Boolean): Boolean;
609 begin
610   Result := InitWindow(w, h, bpp, fullScreen, maximized);
611 end;
613 procedure sys_Repaint ();
614 begin
615   SDL_GL_SwapWindow(window);
616 end;
618 ////////////////////////////////////////////////////////////////////////////////////////////////////
620 procedure sys_Init ();
622   flags: UInt32;
623 begin
624   e_WriteLog('Init SDL2', TMsgType.Notify);
626   // NOTE: No explicit SDL_INIT_EVENTS here as it will be involved by other subsystems if needed.
627   flags := SDL_INIT_TIMER
628     {$IFNDEF HEADLESS} or SDL_INIT_VIDEO {$ENDIF};
630   if SDL_Init(flags) <> 0 then
631     raise Exception.Create('SDL2: Initialization failed: ' + SDL_GetError());
633 {$IFDEF USE_SDLMIXER}
634   if SDL_InitSubSystem(SDL_INIT_AUDIO) <> 0 then
635     e_LogWritefln('SDL2: Failed to initialize audio subsystem: %s', [SDL_GetError()]);
636 {$ENDIF}
638 {$IFNDEF HEADLESS}
639   SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, '0');
640   if SDL_InitSubSystem(SDL_INIT_JOYSTICK) <> 0 then
641     e_LogWritefln('SDL2: Failed to initialize joystick subsystem: %s', [SDL_GetError()]);
642 {$ENDIF}
644   SDL_ShowCursor(SDL_DISABLE);
645 end;
647 procedure sys_Final ();
648 begin
649   e_WriteLog('Releasing SDL2', TMsgType.Notify);
651   if context <> nil then
652   begin
653     FreeGL();
654     SDL_GL_DeleteContext(context);
655     context := nil;
656   end;
658   if window <> nil then
659   begin
660     SDL_DestroyWindow(window);
661     window := nil;
662   end;
664   SDL_Quit();
665 end;
667 ////////////////////////////////////////////////////////////////////////////////////////////////////
668 initialization
669   conRegVar('sdl2_display_index', @display, 'use display index as base', '');
670   conRegVar('sdl2_window_x', @wx, 'window position x', '');
671   conRegVar('sdl2_window_y', @wy, 'window position y', '');
672   conRegVar('sdl2_window_center', @wc, 'force window creation at center', '');