DEADSOFTWARE

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