DEADSOFTWARE

use `QueryPerformanceCounter()` in shitdoze
[d2df-sdl.git] / src / game / g_window.pas
1 unit g_window;
3 interface
5 uses
6 WADEDITOR;
8 function SDLMain(): Integer;
9 function GetTimer(): Int64;
10 procedure ResetTimer();
11 function CreateGLWindow(Title: PChar): Boolean;
12 procedure KillGLWindow();
13 procedure PushExitEvent();
14 function ProcessMessage(): Boolean;
15 procedure ProcessLoading();
16 procedure ReDrawWindow();
17 procedure SwapBuffers();
18 procedure Sleep(ms: LongWord);
19 function GetDisplayModes(dBPP: DWORD; var SelRes: DWORD): SArray;
20 function g_Window_SetDisplay(PreserveGL: Boolean = False): Boolean;
21 function g_Window_SetSize(W, H: Word; FScreen: Boolean): Boolean;
23 implementation
25 uses
26 {$IFDEF WIN32}Windows,{$ENDIF}
27 SDL2, GL, GLExt, e_graphics, e_log, g_main,
28 g_console, SysUtils, e_input, g_options, g_game,
29 g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net;
31 var
32 h_Wnd: PSDL_Window;
33 h_GL: TSDL_GLContext;
34 wFlags: LongWord = 0;
35 Time, Time_Delta, Time_Old: Int64;
36 flag: Boolean;
37 wTitle: PChar = nil;
38 wNeedTimeReset: Boolean = False;
39 //wWindowCreated: Boolean = False;
40 //wCursorShown: Boolean = False;
41 wMinimized: Boolean = False;
42 //wNeedFree: Boolean = True;
43 wLoadingProgress: Boolean = False;
44 wLoadingQuit: Boolean = False;
45 {wWinPause: Byte = 0;}
46 ticksOverflow: Int64 = -1;
47 lastTicks: Uint32 = 0; // to detect overflow
49 const
50 // TODO: move this to a separate file
51 CP1251: array [0..127] of Word = (
52 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
53 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
54 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
55 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
56 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
57 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
58 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
59 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
60 );
62 // TODO: make a transition table or something
63 function WCharToCP1251(wc: Word): Word;
64 var
65 n: Word;
66 begin
67 Result := 0;
68 for n := 0 to 127 do
69 if CP1251[n] = wc then begin Result := n; break end;
70 Result := Result + 128;
71 end;
73 function g_Window_SetDisplay(PreserveGL: Boolean = False): Boolean;
74 var
75 mode, cmode: TSDL_DisplayMode;
76 begin
77 {$IFDEF HEADLESS}
78 Result := True;
79 Exit;
80 {$ENDIF}
82 Result := False;
84 e_WriteLog('Setting display mode...', MSG_NOTIFY);
86 wFlags := SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE;
87 if gFullscreen then wFlags := wFlags or SDL_WINDOW_FULLSCREEN;
88 if gWinMaximized then wFlags := wFlags or SDL_WINDOW_MAXIMIZED;
90 if h_Wnd <> nil then
91 begin
92 SDL_DestroyWindow(h_Wnd);
93 h_Wnd := nil;
94 end;
96 if gFullscreen then
97 begin
98 mode.w := gScreenWidth;
99 mode.h := gScreenHeight;
100 mode.format := 0;
101 mode.refresh_rate := 0;
102 mode.driverdata := nil;
103 if SDL_GetClosestDisplayMode(0, @mode, @cmode) = nil then
104 begin
105 gScreenWidth := 800;
106 gScreenHeight := 600;
107 end
108 else
109 begin
110 gScreenWidth := cmode.w;
111 gScreenHeight := cmode.h;
112 end;
113 end;
115 h_Wnd := SDL_CreateWindow(PChar(wTitle), gWinRealPosX, gWinRealPosY, gScreenWidth, gScreenHeight, wFlags);
116 if h_Wnd = nil then Exit;
118 SDL_GL_MakeCurrent(h_Wnd, h_GL);
119 SDL_ShowCursor(SDL_DISABLE);
121 Result := True;
122 end;
124 procedure ReShowCursor();
125 begin
126 // TODO: what was this for?
127 end;
129 function GetDisplayModes(dBPP: DWORD; var SelRes: DWORD): SArray;
130 var
131 mode: TSDL_DisplayMode;
132 res, i, k, n, pw, ph: Integer;
133 begin
134 SetLength(Result, 0);
135 {$IFDEF HEADLESS}Exit;{$ENDIF}
136 k := 0; SelRes := 0;
137 n := SDL_GetNumDisplayModes(0);
138 pw := 0; ph := 0;
139 for i := 0 to n do
140 begin
141 res := SDL_GetDisplayMode(0, i, @mode);
142 if res < 0 then continue;
143 if SDL_BITSPERPIXEL(mode.format) = gBPP then continue;
144 if (mode.w = pw) and (mode.h = ph) then continue;
145 if (mode.w = gScreenWidth) and (mode.h = gScreenHeight) then
146 SelRes := k;
147 Inc(k);
148 SetLength(Result, k);
149 Result[k-1] := IntToStr(mode.w) + 'x' + IntToStr(mode.h);
150 pw := mode.w; ph := mode.h
151 end;
153 e_WriteLog('SDL: Got ' + IntToStr(k) + ' resolutions.', MSG_NOTIFY);
154 end;
156 procedure Sleep(ms: LongWord);
157 begin
158 SDL_Delay(ms);
159 end;
161 procedure ChangeWindowSize();
162 begin
163 gWinSizeX := gScreenWidth;
164 gWinSizeY := gScreenHeight;
165 {$IFDEF HEADLESS}Exit;{$ENDIF}
166 e_ResizeWindow(gScreenWidth, gScreenHeight);
167 g_Game_SetupScreenSize();
168 g_Menu_Reset();
169 g_Game_ClearLoading();
170 end;
172 function g_Window_SetSize(W, H: Word; FScreen: Boolean): Boolean;
173 var
174 Preserve: Boolean;
175 begin
176 Result := False;
177 {$IFDEF HEADLESS}Exit;{$ENDIF}
178 Preserve := False;
180 if (gScreenWidth <> W) or (gScreenHeight <> H) then
181 begin
182 Result := True;
183 gScreenWidth := W;
184 gScreenHeight := H;
185 end;
187 if gFullscreen <> FScreen then
188 begin
189 Result := True;
190 gFullscreen := FScreen;
191 Preserve := True;
192 end;
194 if Result then
195 begin
196 g_Window_SetDisplay(Preserve);
197 ChangeWindowSize();
198 end;
199 end;
201 function WindowEventHandler(ev: TSDL_WindowEvent): Boolean;
202 var
203 wActivate, wDeactivate: Boolean;
204 begin
205 Result := False;
206 wActivate := False;
207 wDeactivate := False;
209 case ev.event of
210 SDL_WINDOWEVENT_MOVED:
211 begin
212 if not (gFullscreen or gWinMaximized) then
213 begin
214 gWinRealPosX := ev.data1;
215 gWinRealPosY := ev.data2;
216 end;
217 end;
219 SDL_WINDOWEVENT_MINIMIZED:
220 begin
221 if not wMinimized then
222 begin
223 e_ResizeWindow(0, 0);
224 wMinimized := True;
226 if g_debug_WinMsgs then
227 begin
228 g_Console_Add('Now minimized');
229 e_WriteLog('[DEBUG] WinMsgs: Now minimized', MSG_NOTIFY);
230 end;
231 wDeactivate := True;
232 end;
233 end;
235 SDL_WINDOWEVENT_RESIZED:
236 begin
237 gScreenWidth := ev.data1;
238 gScreenHeight := ev.data2;
239 ChangeWindowSize();
240 SwapBuffers();
241 if g_debug_WinMsgs then
242 begin
243 g_Console_Add('Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2));
244 e_WriteLog('[DEBUG] WinMsgs: Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2), MSG_NOTIFY);
245 end;
246 end;
248 SDL_WINDOWEVENT_EXPOSED:
249 SwapBuffers();
251 SDL_WINDOWEVENT_MAXIMIZED:
252 begin
253 if wMinimized then
254 begin
255 e_ResizeWindow(gScreenWidth, gScreenHeight);
256 wMinimized := False;
257 wActivate := True;
258 end;
259 if not gWinMaximized then
260 begin
261 gWinMaximized := True;
262 if g_debug_WinMsgs then
263 begin
264 g_Console_Add('Now maximized');
265 e_WriteLog('[DEBUG] WinMsgs: Now maximized', MSG_NOTIFY);
266 end;
267 end;
268 end;
270 SDL_WINDOWEVENT_RESTORED:
271 begin
272 if wMinimized then
273 begin
274 e_ResizeWindow(gScreenWidth, gScreenHeight);
275 wMinimized := False;
276 wActivate := True;
277 end;
278 if gWinMaximized then
279 gWinMaximized := False;
280 if g_debug_WinMsgs then
281 begin
282 g_Console_Add('Now restored');
283 e_WriteLog('[DEBUG] WinMsgs: Now restored', MSG_NOTIFY);
284 end;
285 end;
287 SDL_WINDOWEVENT_FOCUS_GAINED:
288 begin
289 wActivate := True;
290 //e_WriteLog('window gained focus!', MSG_NOTIFY);
291 end;
293 SDL_WINDOWEVENT_FOCUS_LOST:
294 begin
295 wDeactivate := True;
296 //e_WriteLog('window lost focus!', MSG_NOTIFY);
297 end;
298 end;
300 if wDeactivate then
301 begin
302 if gWinActive then
303 begin
304 e_WriteLog('deactivating window', MSG_NOTIFY);
305 e_EnableInput := False;
306 e_ClearInputBuffer();
308 if gMuteWhenInactive then
309 begin
310 //e_WriteLog('deactivating sounds', MSG_NOTIFY);
311 e_MuteChannels(True);
312 end;
314 if g_debug_WinMsgs then
315 begin
316 g_Console_Add('Now inactive');
317 e_WriteLog('[DEBUG] WinMsgs: Now inactive', MSG_NOTIFY);
318 end;
320 gWinActive := False;
321 end;
322 end
323 else if wActivate then
324 begin
325 if not gWinActive then
326 begin
327 //e_WriteLog('activating window', MSG_NOTIFY);
328 e_EnableInput := True;
330 if gMuteWhenInactive then
331 begin
332 //e_WriteLog('activating sounds', MSG_NOTIFY);
333 e_MuteChannels(False);
334 end;
336 if g_debug_WinMsgs then
337 begin
338 g_Console_Add('Now active');
339 e_WriteLog('[DEBUG] WinMsgs: Now active', MSG_NOTIFY);
340 end;
342 gWinActive := True;
343 end;
344 end;
345 end;
347 function EventHandler(ev: TSDL_Event): Boolean;
348 var
349 key, keychr: Word;
350 uc: UnicodeChar;
351 //joy: Integer;
352 begin
353 Result := False;
354 case ev.type_ of
355 SDL_WINDOWEVENT:
356 Result := WindowEventHandler(ev.window);
358 SDL_QUITEV:
359 begin
360 if gExit <> EXIT_QUIT then
361 begin
362 if not wLoadingProgress then
363 begin
364 g_Game_Free();
365 g_Game_Quit();
366 end
367 else
368 wLoadingQuit := True;
369 end;
370 Result := True;
371 end;
373 SDL_KEYDOWN:
374 begin
375 key := ev.key.keysym.scancode;
376 KeyPress(key);
377 end;
379 SDL_TEXTINPUT:
380 begin
381 Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
382 keychr := Word(uc);
383 if (keychr > 127) then
384 keychr := WCharToCP1251(keychr);
385 CharPress(Chr(keychr));
386 end;
388 // other key presses and joysticks are handled in e_input
389 end;
390 end;
392 procedure SwapBuffers();
393 begin
394 {$IFDEF HEADLESS}Exit;{$ENDIF}
395 SDL_GL_SwapWindow(h_Wnd);
396 end;
398 procedure KillGLWindow();
399 begin
400 if h_Wnd <> nil then SDL_DestroyWindow(h_Wnd);
401 if h_GL <> nil then SDL_GL_DeleteContext(h_GL);
402 h_Wnd := nil;
403 h_GL := nil;
404 //wWindowCreated := False;
405 end;
407 function CreateGLWindow(Title: PChar): Boolean;
408 //var
409 // flags: LongWord;
410 begin
411 Result := False;
413 gWinSizeX := gScreenWidth;
414 gWinSizeY := gScreenHeight;
416 wTitle := Title;
417 e_WriteLog('Creating window', MSG_NOTIFY);
419 if not g_Window_SetDisplay() then
420 begin
421 KillGLWindow();
422 e_WriteLog('Window creation error (resolution not supported?)', MSG_FATALERROR);
423 exit;
424 end;
426 {$IFNDEF HEADLESS}
427 h_Gl := SDL_GL_CreateContext(h_Wnd);
428 if h_Gl = nil then Exit;
429 {$ENDIF}
430 //wWindowCreated := True;
432 e_ResizeWindow(gScreenWidth, gScreenHeight);
433 e_InitGL();
435 Result := True;
436 end;
438 {$IFDEF WIN32}
439 // windoze sux; in headless mode `GetTickCount()` (and SDL) returns shit
440 function GetTimer(): Int64;
441 var
442 F, C: Int64;
443 begin
444 QueryPerformanceFrequency(F);
445 QueryPerformanceCounter(C);
446 Result := Round(C/F*1000{000});
447 end;
448 {$ELSE}
449 function GetTimer(): Int64;
450 var
451 t: Uint32;
452 tt: Int64;
453 begin
454 t := SDL_GetTicks() {* 1000}; // TODO: do we really need microseconds here? k8: NOPE!
455 if ticksOverflow = -1 then
456 begin
457 ticksOverflow := 0;
458 lastTicks := t;
459 end
460 else
461 begin
462 if lastTicks > t then
463 begin
464 // overflow, increment overflow ;-)
465 ticksOverflow := ticksOverflow+(Int64($ffffffff)+Int64(1));
466 tt := (Int64($ffffffff)+Int64(1))+Int64(t);
467 t := Uint32(tt-lastTicks);
468 end;
469 end;
470 lastTicks := t;
471 result := ticksOverflow+Int64(t);
472 end;
473 {$ENDIF}
475 procedure ResetTimer();
476 begin
477 wNeedTimeReset := True;
478 end;
480 procedure PushExitEvent();
481 var
482 ev: TSDL_Event;
483 begin
484 ev.type_ := SDL_QUITEV;
485 SDL_PushEvent(@ev);
486 end;
488 procedure ProcessLoading();
489 var
490 ev: TSDL_Event;
491 ID: DWORD;
492 begin
493 FillChar(ev, SizeOf(ev), 0);
494 //wNeedFree := False;
495 wLoadingProgress := True;
496 while SDL_PollEvent(@ev) > 0 do
497 begin
498 if (ev.type_ = SDL_QUITEV) then
499 break;
500 end;
501 //wNeedFree := True;
503 if (ev.type_ = SDL_QUITEV) or (gExit = EXIT_QUIT) then
504 begin
505 wLoadingProgress := False;
506 exit;
507 end;
509 if not wMinimized then
510 begin
511 if g_Texture_Get('INTER', ID) then
512 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
513 else
514 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
516 DrawLoadingStat();
517 SwapBuffers();
519 ReShowCursor();
520 end;
522 e_SoundUpdate();
524 if NetMode = NET_SERVER then
525 g_Net_Host_Update
526 else
527 if (NetMode = NET_CLIENT) and (NetState <> NET_STATE_AUTH) then
528 g_Net_Client_UpdateWhileLoading;
529 wLoadingProgress := False;
530 end;
532 function ProcessMessage(): Boolean;
533 var
534 i, t: Integer;
535 ev: TSDL_Event;
536 begin
537 Result := False;
538 FillChar(ev, SizeOf(ev), 0);
540 while SDL_PollEvent(@ev) > 0 do
541 begin
542 Result := EventHandler(ev);
543 if ev.type_ = SDL_QUITEV then exit;
544 end;
546 Time := GetTimer();
547 Time_Delta := Time - Time_Old;
549 flag := False;
551 if wNeedTimeReset then
552 begin
553 Time_Delta := (27777 div 1000);
554 wNeedTimeReset := False;
555 end;
557 t := Time_Delta div (27777 div 1000);
558 if t > 0 then
559 begin
560 flag := True;
561 for i := 1 to t do
562 begin
563 if NetMode = NET_SERVER then g_Net_Host_Update()
564 else if NetMode = NET_CLIENT then g_Net_Client_Update();
565 Update();
566 end;
567 end
568 else
569 begin
570 if NetMode = NET_SERVER then g_Net_Host_Update()
571 else if NetMode = NET_CLIENT then g_Net_Client_Update();
572 end;
574 if wLoadingQuit then
575 begin
576 g_Game_Free();
577 g_Game_Quit();
578 end;
580 if gExit = EXIT_QUIT then
581 begin
582 Result := True;
583 Exit;
584 end;
586 // Âðåìÿ ïðåäûäóùåãî îáíîâëåíèÿ:
587 if flag then
588 begin
589 Time_Old := Time - (Time_Delta mod (27777 div 1000));
590 if (not wMinimized) then
591 begin
592 Draw();
593 SwapBuffers();
594 ReShowCursor();
595 end;
596 end
597 else
598 Sleep(1);
600 e_SoundUpdate();
601 end;
603 procedure ReDrawWindow;
604 begin
605 SwapBuffers();
606 ReShowCursor();
607 end;
609 procedure InitOpenGL(VSync: Boolean);
610 var
611 v: Byte;
612 begin
613 {$IFDEF HEADLESS}Exit;{$ENDIF}
614 if VSync then v := 1 else v := 0;
615 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
616 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
617 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
618 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
619 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
620 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
621 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
622 SDL_GL_SetSwapInterval(v);
623 end;
625 function SDLMain(): Integer;
626 begin
627 {$IFDEF HEADLESS}
628 e_NoGraphics := True;
629 {$ENDIF}
631 e_WriteLog('Creating GL window', MSG_NOTIFY);
632 if not CreateGLWindow(PChar(Format('Doom 2D: Forever %s', [GAME_VERSION]))) then
633 begin
634 Result := 0;
635 exit;
636 end;
638 e_WriteLog('Initializing OpenGL', MSG_NOTIFY);
639 InitOpenGL(gVSync);
641 {EnumDisplayModes();}
643 Init();
644 Time_Old := GetTimer();
646 // Êîìàíäíàÿ ñòðîêà:
647 if ParamCount > 0 then
648 g_Game_Process_Params();
650 // Çàïðîñ ÿçûêà:
651 if (not gGameOn) and gAskLanguage then
652 g_Menu_AskLanguage();
654 e_WriteLog('Entering the main loop', MSG_NOTIFY);
656 while not ProcessMessage() do
657 { Main Loop } ;
659 Release();
660 KillGLWindow();
662 Result := 0;
663 end;
665 end.