DEADSOFTWARE

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