DEADSOFTWARE

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