DEADSOFTWARE

don't kill and recreate game window on resolution change in windowed mode
[d2df-sdl.git] / src / game / g_window.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
16 {$INCLUDE ../shared/a_modes.inc}
17 unit g_window;
19 interface
21 uses
22 utils;
24 function SDLMain (): Integer;
25 function GetTimer (): Int64;
26 procedure ResetTimer ();
27 procedure PushExitEvent ();
28 function ProcessMessage (): Boolean;
29 procedure ReDrawWindow ();
30 procedure SwapBuffers ();
31 procedure Sleep (ms: LongWord);
32 function GetDisplayModes (dbpp: LongWord; var selres: LongWord): SSArray;
33 function g_Window_SetDisplay (preserveGL: Boolean=false): Boolean;
34 function g_Window_SetSize (w, h: Word; fullscreen: Boolean): Boolean;
36 procedure ProcessLoading (forceUpdate: Boolean=false);
38 // returns `true` if quit event was received
39 function g_ProcessMessages (): Boolean;
42 var
43 gwin_dump_extensions: Boolean = false;
44 gwin_has_stencil: Boolean = false;
45 gwin_k8_enable_light_experiments: Boolean = false;
46 g_dbg_aimline_on: Boolean = false;
49 implementation
51 uses
52 {$IFDEF WINDOWS}Windows,{$ENDIF}
53 SysUtils, Classes, MAPDEF,
54 SDL2, GL, GLExt, e_graphics, e_log, e_texture, g_main,
55 g_console, e_input, g_options, g_game,
56 g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net,
57 g_map, g_gfx, g_monsters, g_holmes, xprofiler,
58 sdlcarcass, fui_ctls;
61 const
62 ProgressUpdateMSecs = 1;//100;
64 var
65 h_Wnd: PSDL_Window = nil;
66 h_GL: TSDL_GLContext = nil;
67 Time, Time_Delta, Time_Old: Int64;
68 flag: Boolean;
69 {$IF not DEFINED(HEADLESS)}
70 wTitle: PChar = nil;
71 wasFullscreen: Boolean = true; // so we need to recreate the window
72 wMaximized: Boolean = false;
73 {$ENDIF}
74 wNeedTimeReset: Boolean = false;
75 wMinimized: Boolean = false;
76 wLoadingProgress: Boolean = false;
77 wLoadingQuit: Boolean = false;
78 {$IFNDEF WINDOWS}
79 ticksOverflow: Int64 = -1;
80 lastTicks: Uint32 = 0; // to detect overflow
81 {$ENDIF}
84 procedure KillGLWindow (preserveGL: Boolean);
85 begin
86 if (h_GL <> nil) and (not preserveGL) then begin if (assigned(oglDeinitCB)) then oglDeinitCB(); end;
87 if (h_Wnd <> nil) then SDL_DestroyWindow(h_Wnd);
88 if (h_GL <> nil) and (not preserveGL) then SDL_GL_DeleteContext(h_GL);
89 h_Wnd := nil;
90 if (not preserveGL) then h_GL := nil;
91 end;
94 function g_Window_SetDisplay (preserveGL: Boolean = false): Boolean;
95 {$IF not DEFINED(HEADLESS)}
96 var
97 mode, cmode: TSDL_DisplayMode;
98 wFlags: LongWord = 0;
99 nw, nh: Integer;
100 {$ENDIF}
101 begin
102 {$IF not DEFINED(HEADLESS)}
103 result := false;
105 e_WriteLog('Setting display mode...', TMsgType.Notify);
107 wFlags := SDL_WINDOW_OPENGL {or SDL_WINDOW_RESIZABLE};
108 if gFullscreen then wFlags := wFlags or SDL_WINDOW_FULLSCREEN else wFlags := wFlags or SDL_WINDOW_RESIZABLE;
109 if (not gFullscreen) and (not preserveGL) and gWinMaximized then wFlags := wFlags or SDL_WINDOW_MAXIMIZED else gWinMaximized := false;
111 if gFullscreen then
112 begin
113 mode.w := gScreenWidth;
114 mode.h := gScreenHeight;
115 mode.format := 0;
116 mode.refresh_rate := 0;
117 mode.driverdata := nil;
118 if (SDL_GetClosestDisplayMode(0, @mode, @cmode) = nil) then
119 begin
120 e_WriteLog('SDL: cannot find display mode for '+IntToStr(gScreenWidth), TMsgType.Notify);
121 gScreenWidth := 800;
122 gScreenHeight := 600;
123 end
124 else
125 begin
126 e_WriteLog('SDL: found display mode for '+IntToStr(gScreenWidth)+'x'+IntToStr(gScreenHeight)+': '+IntToStr(cmode.w)+'x'+IntToStr(cmode.h), TMsgType.Notify);
127 gScreenWidth := cmode.w;
128 gScreenHeight := cmode.h;
129 end;
130 end;
132 if (preserveGL) and (h_Wnd <> nil) and (not gFullscreen) and (not wasFullscreen) then
133 begin
134 //SDL_SetWindowMaximumSize(h_Wnd, gScreenWidth, gScreenHeight);
135 //SDL_SetWindowDisplayMode(h_Wnd, @cmode);
136 if (wMaximized) then SDL_RestoreWindow(h_Wnd);
137 wMaximized := false;
138 gWinMaximized := false;
139 SDL_SetWindowSize(h_Wnd, gScreenWidth, gScreenHeight);
140 //SDL_SetWindowFullscreen(h_Wnd, SDL_WINDOW_FULLSCREEN);
141 //SDL_SetWindowFullscreen(h_Wnd, 0);
142 end
143 else
144 begin
145 KillGLWindow(preserveGL);
146 h_Wnd := SDL_CreateWindow(PChar(wTitle), gWinRealPosX, gWinRealPosY, gScreenWidth, gScreenHeight, wFlags);
147 if (h_Wnd = nil) then exit;
148 end;
149 wasFullscreen := gFullscreen;
151 SDL_GL_MakeCurrent(h_Wnd, h_GL);
152 SDL_ShowCursor(SDL_DISABLE);
153 if (gFullscreen) then
154 begin
155 nw := 0;
156 nh := 0;
157 SDL_GetWindowSize(h_Wnd, @nw, @nh);
158 if (nw > 128) and (nh > 128) then
159 begin
160 e_WriteLog('SDL: fullscreen window got size '+IntToStr(nw)+'x'+IntToStr(nh)+': '+IntToStr(gScreenWidth)+'x'+IntToStr(gScreenHeight), TMsgType.Notify);
161 gScreenWidth := nw;
162 gScreenHeight := nh;
163 end
164 else
165 begin
166 e_WriteLog('SDL: fullscreen window got invalid size: '+IntToStr(nw)+'x'+IntToStr(nh), TMsgType.Notify);
167 end;
168 end;
169 fuiScrWdt := gScreenWidth;
170 fuiScrHgt := gScreenHeight;
171 if (h_GL <> nil) and (not preserveGL) then begin if (assigned(oglInitCB)) then oglInitCB(); end;
172 {$ENDIF}
174 result := true;
175 end;
178 function GetDisplayModes (dbpp: LongWord; var selres: LongWord): SSArray;
179 var
180 mode: TSDL_DisplayMode;
181 res, i, k, n, pw, ph: Integer;
182 begin
183 SetLength(result, 0);
184 {$IFDEF HEADLESS}exit;{$ENDIF}
185 k := 0; selres := 0;
186 n := SDL_GetNumDisplayModes(0);
187 pw := 0; ph := 0;
188 for i := 0 to n do
189 begin
190 res := SDL_GetDisplayMode(0, i, @mode);
191 if res < 0 then continue;
192 if SDL_BITSPERPIXEL(mode.format) = gBPP then continue;
193 if (mode.w = pw) and (mode.h = ph) then continue;
194 if (mode.w = gScreenWidth) and (mode.h = gScreenHeight) then
195 selres := k;
196 Inc(k);
197 SetLength(result, k);
198 result[k-1] := IntToStr(mode.w) + 'x' + IntToStr(mode.h);
199 pw := mode.w; ph := mode.h
200 end;
202 e_WriteLog('SDL: Got ' + IntToStr(k) + ' resolutions.', TMsgType.Notify);
203 end;
206 procedure Sleep (ms: LongWord);
207 begin
208 SDL_Delay(ms);
209 end;
212 procedure ChangeWindowSize ();
213 begin
214 e_LogWritefln(' ChangeWindowSize: (ws=%dx%d) (ss=%dx%d)', [gWinSizeX, gWinSizeY, gScreenWidth, gScreenHeight]);
215 gWinSizeX := gScreenWidth;
216 gWinSizeY := gScreenHeight;
217 {$IF not DEFINED(HEADLESS)}
218 fuiScrWdt := gScreenWidth;
219 fuiScrHgt := gScreenHeight;
220 e_ResizeWindow(gScreenWidth, gScreenHeight);
221 g_Game_SetupScreenSize();
222 g_Menu_Reset();
223 g_Game_ClearLoading();
224 {$ENDIF}
225 end;
228 function g_Window_SetSize (w, h: Word; fullscreen: Boolean): Boolean;
229 {$IF not DEFINED(HEADLESS)}
230 var
231 preserve: Boolean;
232 {$ENDIF}
233 begin
234 result := false;
235 {$IF not DEFINED(HEADLESS)}
236 preserve := false;
238 if (gScreenWidth <> w) or (gScreenHeight <> h) then
239 begin
240 result := true;
241 preserve := true;
242 gScreenWidth := w;
243 gScreenHeight := h;
244 end;
246 if (gFullscreen <> fullscreen) then
247 begin
248 result := true;
249 preserve := true;
250 gFullscreen := fullscreen;
251 preserve := true;
252 end;
254 if result then
255 begin
256 g_Window_SetDisplay(preserve);
257 ChangeWindowSize();
258 end;
259 {$ENDIF}
260 end;
263 function WindowEventHandler (constref ev: TSDL_WindowEvent): Boolean;
264 var
265 wActivate, wDeactivate: Boolean;
266 begin
267 result := false;
268 wActivate := false;
269 wDeactivate := false;
271 case ev.event of
272 SDL_WINDOWEVENT_MOVED:
273 begin
274 if not (gFullscreen or gWinMaximized) then
275 begin
276 gWinRealPosX := ev.data1;
277 gWinRealPosY := ev.data2;
278 end;
279 end;
281 SDL_WINDOWEVENT_MINIMIZED:
282 begin
283 e_UnpressAllKeys();
284 if not wMinimized then
285 begin
286 e_ResizeWindow(0, 0);
287 wMinimized := true;
288 if g_debug_WinMsgs then
289 begin
290 g_Console_Add('Now minimized');
291 e_WriteLog('[DEBUG] WinMsgs: Now minimized', TMsgType.Notify);
292 end;
293 wDeactivate := true;
294 end;
295 end;
297 SDL_WINDOWEVENT_RESIZED:
298 begin
299 e_LogWritefln('Resize: (os=%dx%d) (ns=%dx%d)', [gScreenWidth, gScreenHeight, Integer(ev.data1), Integer(ev.data2)]);
300 {if (gFullscreen) then
301 begin
302 e_LogWriteln(' fullscreen fix applied.');
303 if (gScreenWidth <> ev.data1) or (gScreenHeight <> ev.data2) then
304 begin
305 SDL_SetWindowSize(h_Wnd, gScreenWidth, gScreenHeight);
306 end;
307 end
308 else}
309 begin
310 gScreenWidth := ev.data1;
311 gScreenHeight := ev.data2;
312 end;
313 ChangeWindowSize();
314 SwapBuffers();
315 if g_debug_WinMsgs then
316 begin
317 g_Console_Add('Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2));
318 e_WriteLog('[DEBUG] WinMsgs: Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2), TMsgType.Notify);
319 end;
320 end;
322 SDL_WINDOWEVENT_EXPOSED:
323 SwapBuffers();
325 SDL_WINDOWEVENT_MAXIMIZED:
326 begin
327 wMaximized := true;
328 if wMinimized then
329 begin
330 e_ResizeWindow(gScreenWidth, gScreenHeight);
331 wMinimized := false;
332 wActivate := true;
333 end;
334 if (not gWinMaximized) and (not gFullscreen) then
335 begin
336 gWinMaximized := true;
337 if g_debug_WinMsgs then
338 begin
339 g_Console_Add('Now maximized');
340 e_WriteLog('[DEBUG] WinMsgs: Now maximized', TMsgType.Notify);
341 end;
342 end;
343 end;
345 SDL_WINDOWEVENT_RESTORED:
346 begin
347 wMaximized := false;
348 if wMinimized then
349 begin
350 e_ResizeWindow(gScreenWidth, gScreenHeight);
351 wMinimized := false;
352 wActivate := true;
353 end;
354 gWinMaximized := false;
355 if g_debug_WinMsgs then
356 begin
357 g_Console_Add('Now restored');
358 e_WriteLog('[DEBUG] WinMsgs: Now restored', TMsgType.Notify);
359 end;
360 end;
362 SDL_WINDOWEVENT_FOCUS_GAINED:
363 begin
364 wActivate := true;
365 //e_WriteLog('window gained focus!', MSG_NOTIFY);
366 end;
368 SDL_WINDOWEVENT_FOCUS_LOST:
369 begin
370 wDeactivate := true;
371 e_UnpressAllKeys();
372 //e_WriteLog('window lost focus!', MSG_NOTIFY);
373 end;
374 end;
376 if wDeactivate then
377 begin
378 if gWinActive then
379 begin
380 e_WriteLog('deactivating window', TMsgType.Notify);
381 e_EnableInput := false;
382 e_ClearInputBuffer();
384 if gMuteWhenInactive then
385 begin
386 //e_WriteLog('deactivating sounds', MSG_NOTIFY);
387 e_MuteChannels(true);
388 end;
390 if g_debug_WinMsgs then
391 begin
392 g_Console_Add('Now inactive');
393 e_WriteLog('[DEBUG] WinMsgs: Now inactive', TMsgType.Notify);
394 end;
396 gWinActive := false;
398 if assigned(winBlurCB) then winBlurCB();
399 end;
400 end
401 else if wActivate then
402 begin
403 if not gWinActive then
404 begin
405 //e_WriteLog('activating window', MSG_NOTIFY);
406 e_EnableInput := true;
408 if gMuteWhenInactive then
409 begin
410 //e_WriteLog('activating sounds', MSG_NOTIFY);
411 e_MuteChannels(false);
412 end;
414 if g_debug_WinMsgs then
415 begin
416 g_Console_Add('Now active');
417 e_WriteLog('[DEBUG] WinMsgs: Now active', TMsgType.Notify);
418 end;
420 gWinActive := true;
421 if assigned(winFocusCB) then winFocusCB();
422 end;
423 end;
424 end;
427 function EventHandler (var ev: TSDL_Event): Boolean;
428 var
429 key, keychr: Word;
430 uc: UnicodeChar;
431 down: Boolean;
432 begin
433 result := false;
435 case ev.type_ of
436 SDL_WINDOWEVENT:
437 result := WindowEventHandler(ev.window);
439 SDL_QUITEV:
440 begin
441 if (gExit <> EXIT_QUIT) then
442 begin
443 if not wLoadingProgress then
444 begin
445 g_Game_Free();
446 g_Game_Quit();
447 end
448 else
449 begin
450 wLoadingQuit := true;
451 end;
452 end;
453 result := true;
454 end;
456 SDL_KEYDOWN, SDL_KEYUP:
457 begin
458 key := ev.key.keysym.scancode;
459 down := (ev.type_ = SDL_KEYDOWN);
460 {$IF not DEFINED(HEADLESS)}
461 if fuiOnSDLEvent(ev) then
462 begin
463 // event eaten, but...
464 if not down then e_KeyUpDown(key, false);
465 exit;
466 end;
467 {$ENDIF}
468 if down then KeyPress(key);
469 e_KeyUpDown(key, down);
470 end;
472 {$IF not DEFINED(HEADLESS)}
473 SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, SDL_MOUSEWHEEL, SDL_MOUSEMOTION:
474 fuiOnSDLEvent(ev);
475 {$ENDIF}
477 SDL_TEXTINPUT:
478 begin
479 Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
480 keychr := Word(uc);
481 if (keychr > 127) then keychr := Word(wchar2win(WideChar(keychr)));
482 if (keychr > 0) and (keychr <= 255) then CharPress(AnsiChar(keychr));
483 end;
485 // other key presses and joysticks are handled in e_input
486 end;
487 end;
490 procedure SwapBuffers ();
491 begin
492 {$IF not DEFINED(HEADLESS)}
493 SDL_GL_SwapWindow(h_Wnd);
494 {$ENDIF}
495 end;
498 function CreateGLWindow (Title: PChar): Boolean;
499 begin
500 result := false;
502 gWinSizeX := gScreenWidth;
503 gWinSizeY := gScreenHeight;
505 {$IF not DEFINED(HEADLESS)}
506 wTitle := Title;
507 {$ENDIF}
508 e_WriteLog('Creating window', TMsgType.Notify);
510 if not g_Window_SetDisplay() then
511 begin
512 KillGLWindow(false);
513 e_WriteLog('Window creation error (resolution not supported?)', TMsgType.Fatal);
514 exit;
515 end;
517 {$IF not DEFINED(HEADLESS)}
518 h_GL := SDL_GL_CreateContext(h_Wnd);
519 if (h_GL = nil) then exit;
520 fuiScrWdt := gScreenWidth;
521 fuiScrHgt := gScreenHeight;
522 if (assigned(oglInitCB)) then oglInitCB();
523 {$ENDIF}
525 e_ResizeWindow(gScreenWidth, gScreenHeight);
526 e_InitGL();
528 result := true;
529 end;
532 {$IFDEF WINDOWS}
533 // windoze sux; in headless mode `GetTickCount()` (and SDL) returns shit
534 function GetTimer (): Int64;
535 var
536 F, C: Int64;
537 begin
538 QueryPerformanceFrequency(F);
539 QueryPerformanceCounter(C);
540 result := Round(C/F*1000{000});
541 end;
542 {$ELSE}
543 function GetTimer (): Int64;
544 var
545 t: Uint32;
546 tt: Int64;
547 begin
548 t := SDL_GetTicks();
549 if (ticksOverflow = -1) then
550 begin
551 ticksOverflow := 0;
552 lastTicks := t;
553 end
554 else
555 begin
556 if (lastTicks > t) then
557 begin
558 // overflow, increment overflow ;-)
559 ticksOverflow := ticksOverflow+(Int64($ffffffff)+Int64(1));
560 tt := (Int64($ffffffff)+Int64(1))+Int64(t);
561 t := Uint32(tt-lastTicks);
562 end;
563 end;
564 lastTicks := t;
565 result := ticksOverflow+Int64(t);
566 end;
567 {$ENDIF}
570 procedure ResetTimer ();
571 begin
572 wNeedTimeReset := true;
573 end;
576 procedure PushExitEvent ();
577 var
578 ev: TSDL_Event;
579 begin
580 ev.type_ := SDL_QUITEV;
581 SDL_PushEvent(@ev);
582 end;
585 var
586 prevLoadingUpdateTime: UInt64 = 0;
588 procedure ProcessLoading (forceUpdate: Boolean=false);
589 var
590 ev: TSDL_Event;
591 ID: LongWord;
592 stt: UInt64;
593 begin
594 FillChar(ev, sizeof(ev), 0);
595 wLoadingProgress := true;
597 while (SDL_PollEvent(@ev) > 0) do
598 begin
599 EventHandler(ev);
600 if (ev.type_ = SDL_QUITEV) then break;
601 end;
602 e_PollJoysticks();
604 if (ev.type_ = SDL_QUITEV) or (gExit = EXIT_QUIT) then
605 begin
606 wLoadingProgress := false;
607 exit;
608 end;
610 if not wMinimized then
611 begin
612 if forceUpdate then
613 begin
614 prevLoadingUpdateTime := getTimeMilli();
615 end
616 else
617 begin
618 stt := getTimeMilli();
619 if (stt < prevLoadingUpdateTime) or (stt-prevLoadingUpdateTime >= ProgressUpdateMSecs) then
620 begin
621 prevLoadingUpdateTime := stt;
622 forceUpdate := true;
623 end;
624 end;
626 if forceUpdate then
627 begin
628 if g_Texture_Get('INTER', ID) then
629 begin
630 e_DrawSize(ID, 0, 0, 0, false, false, gScreenWidth, gScreenHeight);
631 e_DarkenQuadWH(0, 0, gScreenWidth, gScreenHeight, 150);
632 end
633 else
634 begin
635 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
636 end;
638 DrawLoadingStat();
639 SwapBuffers();
640 end;
641 end;
643 e_SoundUpdate();
645 if NetMode = NET_SERVER then
646 begin
647 g_Net_Host_Update();
648 end
649 else
650 begin
651 if (NetMode = NET_CLIENT) and (NetState <> NET_STATE_AUTH) then g_Net_Client_UpdateWhileLoading();
652 end;
654 wLoadingProgress := false;
655 end;
658 function g_ProcessMessages (): Boolean;
659 var
660 ev: TSDL_Event;
661 begin
662 result := false;
663 FillChar(ev, SizeOf(ev), 0);
664 while (SDL_PollEvent(@ev) > 0) do
665 begin
666 result := EventHandler(ev);
667 if (ev.type_ = SDL_QUITEV) then exit;
668 end;
669 e_PollJoysticks();
670 end;
673 function ProcessMessage (): Boolean;
674 var
675 i, t: Integer;
676 begin
677 result := g_ProcessMessages();
679 Time := GetTimer();
680 Time_Delta := Time-Time_Old;
682 flag := false;
684 if wNeedTimeReset then
685 begin
686 Time_Delta := 28;
687 wNeedTimeReset := false;
688 end;
690 g_Map_ProfilersBegin();
691 g_Mons_ProfilersBegin();
693 t := Time_Delta div 28;
694 if (t > 0) then
695 begin
696 flag := true;
697 for i := 1 to t do
698 begin
699 if (NetMode = NET_SERVER) then g_Net_Host_Update()
700 else if (NetMode = NET_CLIENT) then g_Net_Client_Update();
701 Update();
702 end;
703 end
704 else
705 begin
706 if (NetMode = NET_SERVER) then g_Net_Host_Update()
707 else if (NetMode = NET_CLIENT) then g_Net_Client_Update();
708 end;
710 g_Map_ProfilersEnd();
711 g_Mons_ProfilersEnd();
713 if wLoadingQuit then
714 begin
715 g_Game_Free();
716 g_Game_Quit();
717 end;
719 if (gExit = EXIT_QUIT) then
720 begin
721 result := true;
722 exit;
723 end;
725 // Âðåìÿ ïðåäûäóùåãî îáíîâëåíèÿ
726 if flag then
727 begin
728 Time_Old := Time-(Time_Delta mod 28);
729 if (not wMinimized) then
730 begin
731 Draw();
732 SwapBuffers();
733 end;
734 end
735 else
736 begin
737 Sleep(1); // release time slice, so we won't eat 100% CPU
738 end;
740 e_SoundUpdate();
741 end;
744 procedure ReDrawWindow ();
745 begin
746 SwapBuffers();
747 end;
750 procedure InitOpenGL (vsync: Boolean);
751 {$IF not DEFINED(HEADLESS)}
752 var
753 v: Byte;
754 {$ENDIF}
755 begin
756 {$IF not DEFINED(HEADLESS)}
757 if vsync then v := 1 else v := 0;
758 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
759 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
760 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
761 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
762 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
763 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
764 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
765 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); // lights; it is enough to have 1-bit stencil buffer for lighting, but...
766 SDL_GL_SetSwapInterval(v);
767 {$ENDIF}
768 end;
771 function glHasExtension (const name: AnsiString): Boolean;
772 var
773 exts: PChar;
774 i: Integer;
775 found: Boolean;
776 extName: ShortString;
777 begin
778 result := false;
779 if (Length(name) = 0) then exit;
780 exts := glGetString(GL_EXTENSIONS);
781 if (exts = nil) then exit;
782 while (exts[0] <> #0) and (exts[0] = ' ') do Inc(exts);
783 while (exts[0] <> #0) do
784 begin
785 if gwin_dump_extensions then
786 begin
787 i := 0;
788 while (exts[i] <> #0) and (exts[i] <> ' ') do Inc(i);
789 if i > 255 then
790 begin
791 e_WriteLog('FUUUUUUUUUUUUU', TMsgType.Warning);
792 end
793 else
794 begin
795 Move(exts^, extName[1], i);
796 extName[0] := Char(i);
797 e_WriteLog(Format('EXT: %s', [extName]), TMsgType.Notify);
798 end;
799 end;
800 found := true;
801 for i := 0 to length(name)-1 do
802 begin
803 if (exts[i] = #0) then begin found := false; break; end;
804 if (exts[i] <> name[i+1]) then begin found := false; break; end;
805 end;
806 if found and ((exts[Length(name)] = #0) or (exts[Length(name)] = ' ')) then begin result := true; exit; end;
807 while (exts[0] <> #0) and (exts[0] <> ' ') do Inc(exts);
808 while (exts[0] <> #0) and (exts[0] = ' ') do Inc(exts);
809 end;
810 end;
813 function SDLMain (): Integer;
814 var
815 idx: Integer;
816 {$IF not DEFINED(HEADLESS)}
817 ltmp: Integer;
818 {$ENDIF}
819 arg: AnsiString;
820 mdfo: TStream;
821 itmp: Integer;
822 valres: Word;
823 begin
824 {$IFDEF HEADLESS}
825 e_NoGraphics := true;
826 {$ELSE}
827 if (not g_holmes_imfunctional) then
828 begin
829 uiInitialize();
830 uiContext.font := 'win14';
831 end;
832 {$ENDIF}
834 idx := 1;
835 while (idx <= ParamCount) do
836 begin
837 arg := ParamStr(idx);
838 Inc(idx);
839 if arg = '--opengl-dump-exts' then gwin_dump_extensions := true;
840 //if arg = '--twinkletwinkle' then gwin_k8_enable_light_experiments := true;
841 if arg = '--jah' then g_profile_history_size := 100;
842 if arg = '--no-particles' then gpart_dbg_enabled := false;
843 if arg = '--no-los' then gmon_dbg_los_enabled := false;
845 if arg = '--profile-render' then g_profile_frame_draw := true;
846 if arg = '--profile-coldet' then g_profile_collision := true;
847 if arg = '--profile-los' then g_profile_los := true;
849 if arg = '--no-part-phys' then gpart_dbg_phys_enabled := false;
850 if arg = '--no-part-physics' then gpart_dbg_phys_enabled := false;
851 if arg = '--no-particles-phys' then gpart_dbg_phys_enabled := false;
852 if arg = '--no-particles-physics' then gpart_dbg_phys_enabled := false;
853 if arg = '--no-particle-phys' then gpart_dbg_phys_enabled := false;
854 if arg = '--no-particle-physics' then gpart_dbg_phys_enabled := false;
856 {.$IF DEFINED(D2F_DEBUG)}
857 if arg = '--aimline' then g_dbg_aimline_on := true;
858 {.$ENDIF}
860 if arg = '--holmes' then begin g_holmes_enabled := true; g_Game_SetDebugMode(); end;
862 if (arg = '--holmes-ui-scale') or (arg = '-holmes-ui-scale') then
863 begin
864 if (idx <= ParamCount) then
865 begin
866 if not conParseFloat(fuiRenderScale, ParamStr(idx)) then fuiRenderScale := 1.0;
867 Inc(idx);
868 end;
869 end;
871 if (arg = '--holmes-font') or (arg = '-holmes-font') then
872 begin
873 if (idx <= ParamCount) then
874 begin
875 itmp := 0;
876 val(ParamStr(idx), itmp, valres);
877 {$IFNDEF HEADLESS}
878 if (valres = 0) and (not g_holmes_imfunctional) then
879 begin
880 case itmp of
881 8: uiContext.font := 'win8';
882 14: uiContext.font := 'win14';
883 16: uiContext.font := 'win16';
884 end;
885 end;
886 {$ELSE}
887 // fuck off, fpc!
888 itmp := itmp;
889 valres := valres;
890 {$ENDIF}
891 Inc(idx);
892 end;
893 end;
895 if (arg = '--game-scale') or (arg = '-game-scale') then
896 begin
897 if (idx <= ParamCount) then
898 begin
899 if not conParseFloat(g_dbg_scale, ParamStr(idx)) then g_dbg_scale := 1.0;
900 Inc(idx);
901 end;
902 end;
904 if (arg = '--write-mapdef') or (arg = '-write-mapdef') then
905 begin
906 mdfo := createDiskFile('mapdef.txt');
907 mdfo.WriteBuffer(defaultMapDef[1], Length(defaultMapDef));
908 mdfo.Free();
909 Halt(0);
910 end;
911 end;
913 e_WriteLog('Initializing OpenGL', TMsgType.Notify);
914 InitOpenGL(gVSync);
916 e_WriteLog('Creating GL window', TMsgType.Notify);
917 if not CreateGLWindow(PChar(Format('Doom 2D: Forever %s', [GAME_VERSION]))) then
918 begin
919 result := 0;
920 exit;
921 end;
923 {EnumDisplayModes();}
925 {$IFDEF HEADLESS}
926 //gwin_k8_enable_light_experiments := false;
927 gwin_has_stencil := false;
928 glLegacyNPOT := false;
929 gwin_dump_extensions := false;
930 {$ELSE}
931 SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, @ltmp);
932 e_LogWritefln('stencil buffer size: %s', [ltmp]);
933 gwin_has_stencil := (ltmp > 0);
935 if not glHasExtension('GL_ARB_texture_non_power_of_two') then
936 begin
937 e_WriteLog('NPOT textures: NO', TMsgType.Warning);
938 glLegacyNPOT := true;
939 end
940 else
941 begin
942 e_WriteLog('NPOT textures: YES', TMsgType.Notify);
943 glLegacyNPOT := false;
944 end;
945 gwin_dump_extensions := false;
946 {$ENDIF}
948 Init();
949 Time_Old := GetTimer();
951 // Êîìàíäíàÿ ñòðîêà
952 if (ParamCount > 0) then g_Game_Process_Params();
954 // Çàïðîñ ÿçûêà
955 if (not gGameOn) and gAskLanguage then g_Menu_AskLanguage();
957 e_WriteLog('Entering the main loop', TMsgType.Notify);
959 // main loop
960 while not ProcessMessage() do begin end;
962 Release();
963 KillGLWindow(false);
965 result := 0;
966 end;
969 end.