DEADSOFTWARE

a8c6b6a7b3c3bf8302e95d712e2a2b875750359a
[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 g_amodes.inc}
17 unit g_window;
19 interface
21 uses
22 wadreader;
24 function SDLMain(): Integer;
25 function GetTimer(): Int64;
26 procedure ResetTimer();
27 function CreateGLWindow(Title: PChar): Boolean;
28 procedure KillGLWindow();
29 procedure PushExitEvent();
30 function ProcessMessage(): Boolean;
31 procedure ProcessLoading();
32 procedure ReDrawWindow();
33 procedure SwapBuffers();
34 procedure Sleep(ms: LongWord);
35 function GetDisplayModes(dBPP: DWORD; var SelRes: DWORD): SArray;
36 function g_Window_SetDisplay(PreserveGL: Boolean = False): Boolean;
37 function g_Window_SetSize(W, H: Word; FScreen: Boolean): Boolean;
39 var
40 gwin_dump_extensions: Boolean = false;
41 gwin_has_stencil: Boolean = false;
42 gwin_k8_enable_light_experiments: Boolean = false;
44 implementation
46 uses
47 {$IFDEF WINDOWS}Windows,{$ENDIF}
48 SDL2, GL, GLExt, e_graphics, e_log, g_main,
49 g_console, SysUtils, e_input, g_options, g_game,
50 g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net;
52 var
53 h_Wnd: PSDL_Window;
54 h_GL: TSDL_GLContext;
55 wFlags: LongWord = 0;
56 Time, Time_Delta, Time_Old: Int64;
57 flag: Boolean;
58 wTitle: PChar = nil;
59 wNeedTimeReset: Boolean = False;
60 //wWindowCreated: Boolean = False;
61 //wCursorShown: Boolean = False;
62 wMinimized: Boolean = False;
63 //wNeedFree: Boolean = True;
64 wLoadingProgress: Boolean = False;
65 wLoadingQuit: Boolean = False;
66 {wWinPause: Byte = 0;}
67 {$IFNDEF WINDOWS}
68 ticksOverflow: Int64 = -1;
69 lastTicks: Uint32 = 0; // to detect overflow
70 {$ENDIF}
72 const
73 // TODO: move this to a separate file
74 CP1251: array [0..127] of Word = (
75 $0402,$0403,$201A,$0453,$201E,$2026,$2020,$2021,$20AC,$2030,$0409,$2039,$040A,$040C,$040B,$040F,
76 $0452,$2018,$2019,$201C,$201D,$2022,$2013,$2014,$003F,$2122,$0459,$203A,$045A,$045C,$045B,$045F,
77 $00A0,$040E,$045E,$0408,$00A4,$0490,$00A6,$00A7,$0401,$00A9,$0404,$00AB,$00AC,$00AD,$00AE,$0407,
78 $00B0,$00B1,$0406,$0456,$0491,$00B5,$00B6,$00B7,$0451,$2116,$0454,$00BB,$0458,$0405,$0455,$0457,
79 $0410,$0411,$0412,$0413,$0414,$0415,$0416,$0417,$0418,$0419,$041A,$041B,$041C,$041D,$041E,$041F,
80 $0420,$0421,$0422,$0423,$0424,$0425,$0426,$0427,$0428,$0429,$042A,$042B,$042C,$042D,$042E,$042F,
81 $0430,$0431,$0432,$0433,$0434,$0435,$0436,$0437,$0438,$0439,$043A,$043B,$043C,$043D,$043E,$043F,
82 $0440,$0441,$0442,$0443,$0444,$0445,$0446,$0447,$0448,$0449,$044A,$044B,$044C,$044D,$044E,$044F
83 );
85 // TODO: make a transition table or something
86 function WCharToCP1251(wc: Word): Word;
87 var
88 n: Word;
89 begin
90 Result := 0;
91 for n := 0 to 127 do
92 if CP1251[n] = wc then begin Result := n; break end;
93 Result := Result + 128;
94 end;
96 function g_Window_SetDisplay(PreserveGL: Boolean = False): Boolean;
97 var
98 mode, cmode: TSDL_DisplayMode;
99 begin
100 {$IFDEF HEADLESS}
101 Result := True;
102 Exit;
103 {$ENDIF}
105 Result := False;
107 e_WriteLog('Setting display mode...', MSG_NOTIFY);
109 wFlags := SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE;
110 if gFullscreen then wFlags := wFlags or SDL_WINDOW_FULLSCREEN;
111 if gWinMaximized then wFlags := wFlags or SDL_WINDOW_MAXIMIZED;
113 if h_Wnd <> nil then
114 begin
115 SDL_DestroyWindow(h_Wnd);
116 h_Wnd := nil;
117 end;
119 if gFullscreen then
120 begin
121 mode.w := gScreenWidth;
122 mode.h := gScreenHeight;
123 mode.format := 0;
124 mode.refresh_rate := 0;
125 mode.driverdata := nil;
126 if SDL_GetClosestDisplayMode(0, @mode, @cmode) = nil then
127 begin
128 gScreenWidth := 800;
129 gScreenHeight := 600;
130 end
131 else
132 begin
133 gScreenWidth := cmode.w;
134 gScreenHeight := cmode.h;
135 end;
136 end;
138 h_Wnd := SDL_CreateWindow(PChar(wTitle), gWinRealPosX, gWinRealPosY, gScreenWidth, gScreenHeight, wFlags);
139 if h_Wnd = nil then Exit;
141 SDL_GL_MakeCurrent(h_Wnd, h_GL);
142 SDL_ShowCursor(SDL_DISABLE);
144 Result := True;
145 end;
147 procedure ReShowCursor();
148 begin
149 // TODO: what was this for?
150 end;
152 function GetDisplayModes(dBPP: DWORD; var SelRes: DWORD): SArray;
153 var
154 mode: TSDL_DisplayMode;
155 res, i, k, n, pw, ph: Integer;
156 begin
157 SetLength(Result, 0);
158 {$IFDEF HEADLESS}Exit;{$ENDIF}
159 k := 0; SelRes := 0;
160 n := SDL_GetNumDisplayModes(0);
161 pw := 0; ph := 0;
162 for i := 0 to n do
163 begin
164 res := SDL_GetDisplayMode(0, i, @mode);
165 if res < 0 then continue;
166 if SDL_BITSPERPIXEL(mode.format) = gBPP then continue;
167 if (mode.w = pw) and (mode.h = ph) then continue;
168 if (mode.w = gScreenWidth) and (mode.h = gScreenHeight) then
169 SelRes := k;
170 Inc(k);
171 SetLength(Result, k);
172 Result[k-1] := IntToStr(mode.w) + 'x' + IntToStr(mode.h);
173 pw := mode.w; ph := mode.h
174 end;
176 e_WriteLog('SDL: Got ' + IntToStr(k) + ' resolutions.', MSG_NOTIFY);
177 end;
179 procedure Sleep(ms: LongWord);
180 begin
181 SDL_Delay(ms);
182 end;
184 procedure ChangeWindowSize();
185 begin
186 gWinSizeX := gScreenWidth;
187 gWinSizeY := gScreenHeight;
188 {$IFDEF HEADLESS}Exit;{$ENDIF}
189 e_ResizeWindow(gScreenWidth, gScreenHeight);
190 g_Game_SetupScreenSize();
191 g_Menu_Reset();
192 g_Game_ClearLoading();
193 end;
195 function g_Window_SetSize(W, H: Word; FScreen: Boolean): Boolean;
196 var
197 Preserve: Boolean;
198 begin
199 Result := False;
200 {$IFDEF HEADLESS}Exit;{$ENDIF}
201 Preserve := False;
203 if (gScreenWidth <> W) or (gScreenHeight <> H) then
204 begin
205 Result := True;
206 gScreenWidth := W;
207 gScreenHeight := H;
208 end;
210 if gFullscreen <> FScreen then
211 begin
212 Result := True;
213 gFullscreen := FScreen;
214 Preserve := True;
215 end;
217 if Result then
218 begin
219 g_Window_SetDisplay(Preserve);
220 ChangeWindowSize();
221 end;
222 end;
224 function WindowEventHandler(ev: TSDL_WindowEvent): Boolean;
225 var
226 wActivate, wDeactivate: Boolean;
227 begin
228 Result := False;
229 wActivate := False;
230 wDeactivate := False;
232 case ev.event of
233 SDL_WINDOWEVENT_MOVED:
234 begin
235 if not (gFullscreen or gWinMaximized) then
236 begin
237 gWinRealPosX := ev.data1;
238 gWinRealPosY := ev.data2;
239 end;
240 end;
242 SDL_WINDOWEVENT_MINIMIZED:
243 begin
244 if not wMinimized then
245 begin
246 e_ResizeWindow(0, 0);
247 wMinimized := True;
249 if g_debug_WinMsgs then
250 begin
251 g_Console_Add('Now minimized');
252 e_WriteLog('[DEBUG] WinMsgs: Now minimized', MSG_NOTIFY);
253 end;
254 wDeactivate := True;
255 end;
256 end;
258 SDL_WINDOWEVENT_RESIZED:
259 begin
260 gScreenWidth := ev.data1;
261 gScreenHeight := ev.data2;
262 ChangeWindowSize();
263 SwapBuffers();
264 if g_debug_WinMsgs then
265 begin
266 g_Console_Add('Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2));
267 e_WriteLog('[DEBUG] WinMsgs: Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2), MSG_NOTIFY);
268 end;
269 end;
271 SDL_WINDOWEVENT_EXPOSED:
272 SwapBuffers();
274 SDL_WINDOWEVENT_MAXIMIZED:
275 begin
276 if wMinimized then
277 begin
278 e_ResizeWindow(gScreenWidth, gScreenHeight);
279 wMinimized := False;
280 wActivate := True;
281 end;
282 if not gWinMaximized then
283 begin
284 gWinMaximized := True;
285 if g_debug_WinMsgs then
286 begin
287 g_Console_Add('Now maximized');
288 e_WriteLog('[DEBUG] WinMsgs: Now maximized', MSG_NOTIFY);
289 end;
290 end;
291 end;
293 SDL_WINDOWEVENT_RESTORED:
294 begin
295 if wMinimized then
296 begin
297 e_ResizeWindow(gScreenWidth, gScreenHeight);
298 wMinimized := False;
299 wActivate := True;
300 end;
301 if gWinMaximized then
302 gWinMaximized := False;
303 if g_debug_WinMsgs then
304 begin
305 g_Console_Add('Now restored');
306 e_WriteLog('[DEBUG] WinMsgs: Now restored', MSG_NOTIFY);
307 end;
308 end;
310 SDL_WINDOWEVENT_FOCUS_GAINED:
311 begin
312 wActivate := True;
313 //e_WriteLog('window gained focus!', MSG_NOTIFY);
314 end;
316 SDL_WINDOWEVENT_FOCUS_LOST:
317 begin
318 wDeactivate := True;
319 //e_WriteLog('window lost focus!', MSG_NOTIFY);
320 end;
321 end;
323 if wDeactivate then
324 begin
325 if gWinActive then
326 begin
327 e_WriteLog('deactivating window', MSG_NOTIFY);
328 e_EnableInput := False;
329 e_ClearInputBuffer();
331 if gMuteWhenInactive then
332 begin
333 //e_WriteLog('deactivating sounds', MSG_NOTIFY);
334 e_MuteChannels(True);
335 end;
337 if g_debug_WinMsgs then
338 begin
339 g_Console_Add('Now inactive');
340 e_WriteLog('[DEBUG] WinMsgs: Now inactive', MSG_NOTIFY);
341 end;
343 gWinActive := False;
344 end;
345 end
346 else if wActivate then
347 begin
348 if not gWinActive then
349 begin
350 //e_WriteLog('activating window', MSG_NOTIFY);
351 e_EnableInput := True;
353 if gMuteWhenInactive then
354 begin
355 //e_WriteLog('activating sounds', MSG_NOTIFY);
356 e_MuteChannels(False);
357 end;
359 if g_debug_WinMsgs then
360 begin
361 g_Console_Add('Now active');
362 e_WriteLog('[DEBUG] WinMsgs: Now active', MSG_NOTIFY);
363 end;
365 gWinActive := True;
366 end;
367 end;
368 end;
370 function EventHandler(ev: TSDL_Event): Boolean;
371 var
372 key, keychr: Word;
373 uc: UnicodeChar;
374 //joy: Integer;
375 begin
376 Result := False;
377 case ev.type_ of
378 SDL_WINDOWEVENT:
379 Result := WindowEventHandler(ev.window);
381 SDL_QUITEV:
382 begin
383 if gExit <> EXIT_QUIT then
384 begin
385 if not wLoadingProgress then
386 begin
387 g_Game_Free();
388 g_Game_Quit();
389 end
390 else
391 wLoadingQuit := True;
392 end;
393 Result := True;
394 end;
396 SDL_KEYDOWN:
397 begin
398 key := ev.key.keysym.scancode;
399 KeyPress(key);
400 end;
402 SDL_TEXTINPUT:
403 begin
404 Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
405 keychr := Word(uc);
406 if (keychr > 127) then
407 keychr := WCharToCP1251(keychr);
408 CharPress(Chr(keychr));
409 end;
411 // other key presses and joysticks are handled in e_input
412 end;
413 end;
415 procedure SwapBuffers();
416 begin
417 {$IFDEF HEADLESS}Exit;{$ENDIF}
418 SDL_GL_SwapWindow(h_Wnd);
419 end;
421 procedure KillGLWindow();
422 begin
423 if h_Wnd <> nil then SDL_DestroyWindow(h_Wnd);
424 if h_GL <> nil then SDL_GL_DeleteContext(h_GL);
425 h_Wnd := nil;
426 h_GL := nil;
427 //wWindowCreated := False;
428 end;
430 function CreateGLWindow(Title: PChar): Boolean;
431 //var
432 // flags: LongWord;
433 begin
434 Result := False;
436 gWinSizeX := gScreenWidth;
437 gWinSizeY := gScreenHeight;
439 wTitle := Title;
440 e_WriteLog('Creating window', MSG_NOTIFY);
442 if not g_Window_SetDisplay() then
443 begin
444 KillGLWindow();
445 e_WriteLog('Window creation error (resolution not supported?)', MSG_FATALERROR);
446 exit;
447 end;
449 {$IFNDEF HEADLESS}
450 h_Gl := SDL_GL_CreateContext(h_Wnd);
451 if h_Gl = nil then Exit;
452 {$ENDIF}
453 //wWindowCreated := True;
455 e_ResizeWindow(gScreenWidth, gScreenHeight);
456 e_InitGL();
458 Result := True;
459 end;
461 {$IFDEF WINDOWS}
462 // windoze sux; in headless mode `GetTickCount()` (and SDL) returns shit
463 function GetTimer(): Int64;
464 var
465 F, C: Int64;
466 begin
467 QueryPerformanceFrequency(F);
468 QueryPerformanceCounter(C);
469 Result := Round(C/F*1000{000});
470 end;
471 {$ELSE}
472 function GetTimer(): Int64;
473 var
474 t: Uint32;
475 tt: Int64;
476 begin
477 t := SDL_GetTicks() {* 1000}; // TODO: do we really need microseconds here? k8: NOPE!
478 if ticksOverflow = -1 then
479 begin
480 ticksOverflow := 0;
481 lastTicks := t;
482 end
483 else
484 begin
485 if lastTicks > t then
486 begin
487 // overflow, increment overflow ;-)
488 ticksOverflow := ticksOverflow+(Int64($ffffffff)+Int64(1));
489 tt := (Int64($ffffffff)+Int64(1))+Int64(t);
490 t := Uint32(tt-lastTicks);
491 end;
492 end;
493 lastTicks := t;
494 result := ticksOverflow+Int64(t);
495 end;
496 {$ENDIF}
498 procedure ResetTimer();
499 begin
500 wNeedTimeReset := True;
501 end;
503 procedure PushExitEvent();
504 var
505 ev: TSDL_Event;
506 begin
507 ev.type_ := SDL_QUITEV;
508 SDL_PushEvent(@ev);
509 end;
511 procedure ProcessLoading();
512 var
513 ev: TSDL_Event;
514 ID: DWORD;
515 begin
516 FillChar(ev, SizeOf(ev), 0);
517 //wNeedFree := False;
518 wLoadingProgress := True;
519 while SDL_PollEvent(@ev) > 0 do
520 begin
521 if (ev.type_ = SDL_QUITEV) then
522 break;
523 end;
524 //wNeedFree := True;
526 if (ev.type_ = SDL_QUITEV) or (gExit = EXIT_QUIT) then
527 begin
528 wLoadingProgress := False;
529 exit;
530 end;
532 if not wMinimized then
533 begin
534 if g_Texture_Get('INTER', ID) then
535 e_DrawSize(ID, 0, 0, 0, False, False, gScreenWidth, gScreenHeight)
536 else
537 e_Clear(GL_COLOR_BUFFER_BIT, 0, 0, 0);
539 DrawLoadingStat();
540 SwapBuffers();
542 ReShowCursor();
543 end;
545 e_SoundUpdate();
547 if NetMode = NET_SERVER then
548 begin
549 g_Net_Host_Update();
550 g_Net_Flush();
551 end
552 else if NetMode = NET_CLIENT then
553 if (NetState <> NET_STATE_AUTH) then
554 begin
555 g_Net_Client_UpdateWhileLoading();
556 g_Net_Flush();
557 end;
559 wLoadingProgress := False;
560 end;
562 function ProcessMessage(): Boolean;
563 var
564 i, t: Integer;
565 ev: TSDL_Event;
566 begin
567 Result := False;
568 FillChar(ev, SizeOf(ev), 0);
570 while SDL_PollEvent(@ev) > 0 do
571 begin
572 Result := EventHandler(ev);
573 if ev.type_ = SDL_QUITEV then exit;
574 end;
576 Time := GetTimer();
577 Time_Delta := Time - Time_Old;
579 flag := False;
581 if wNeedTimeReset then
582 begin
583 Time_Delta := 28{(27777 div 1000)};
584 wNeedTimeReset := False;
585 end;
587 t := Time_Delta div 28{(27777 div 1000)};
588 if t > 0 then
589 begin
590 flag := True;
591 for i := 1 to t do
592 begin
593 if NetMode = NET_SERVER then g_Net_Host_Update()
594 else if NetMode = NET_CLIENT then g_Net_Client_Update();
595 Update();
596 if NetMode <> NET_NONE then g_Net_Flush();
597 end;
598 end
599 else if NetMode <> NET_NONE then
600 begin
601 if NetMode = NET_SERVER then g_Net_Host_Update()
602 else if NetMode = NET_CLIENT then g_Net_Client_Update();
603 g_Net_Flush();
604 end;
606 if wLoadingQuit then
607 begin
608 g_Game_Free();
609 g_Game_Quit();
610 end;
612 if gExit = EXIT_QUIT then
613 begin
614 Result := True;
615 Exit;
616 end;
618 // Âðåìÿ ïðåäûäóùåãî îáíîâëåíèÿ:
619 if flag then
620 begin
621 Time_Old := Time - (Time_Delta mod 28{(27777 div 1000)});
622 if (not wMinimized) then
623 begin
624 Draw();
625 SwapBuffers();
626 ReShowCursor();
627 end;
628 end
629 else
630 Sleep(1);
632 e_SoundUpdate();
633 end;
635 procedure ReDrawWindow;
636 begin
637 SwapBuffers();
638 ReShowCursor();
639 end;
641 procedure InitOpenGL(VSync: Boolean);
642 var
643 v: Byte;
644 begin
645 {$IFDEF HEADLESS}Exit;{$ENDIF}
646 if VSync then v := 1 else v := 0;
647 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
648 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
649 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
650 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
651 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
652 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
653 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
654 if gwin_k8_enable_light_experiments then
655 begin
656 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1); // lights; it is enough to have 1-bit stencil buffer for lighting
657 end;
658 SDL_GL_SetSwapInterval(v);
659 end;
661 function glHasExtension (name: AnsiString): Boolean;
662 var
663 exts: PChar;
664 i: Integer;
665 found: Boolean;
666 extName: ShortString;
667 begin
668 result := false;
669 if length(name) = 0 then exit;
670 exts := glGetString(GL_EXTENSIONS);
671 if exts = nil then exit;
672 while (exts[0] <> #0) and (exts[0] = ' ') do Inc(exts);
673 while exts[0] <> #0 do
674 begin
675 if gwin_dump_extensions then
676 begin
677 i := 0;
678 while (exts[i] <> #0) and (exts[i] <> ' ') do Inc(i);
679 if i > 255 then
680 begin
681 e_WriteLog('FUUUUUUUUUUUUU', MSG_WARNING);
682 end
683 else
684 begin
685 Move(exts^, extName[1], i);
686 extName[0] := Char(i);
687 e_WriteLog(Format('EXT: %s', [extName]), MSG_NOTIFY);
688 end;
689 end;
690 found := true;
691 for i := 0 to length(name)-1 do
692 begin
693 if exts[i] = #0 then begin found := false; break; end;
694 if exts[i] <> name[i+1] then begin found := false; break; end;
695 end;
696 if found and ((exts[length(name)] = #0) or (exts[length(name)] = ' ')) then begin result := true; exit; end;
697 while (exts[0] <> #0) and (exts[0] <> ' ') do Inc(exts);
698 while (exts[0] <> #0) and (exts[0] = ' ') do Inc(exts);
699 end;
700 end;
702 function SDLMain(): Integer;
703 var
704 idx: Integer;
705 ltmp: Integer;
706 begin
707 {$IFDEF HEADLESS}
708 e_NoGraphics := True;
709 {$ENDIF}
711 for idx := 1 to ParamCount do
712 begin
713 if ParamStr(idx) = '--opengl-dump-exts' then gwin_dump_extensions := true;
714 if ParamStr(idx) = '--twinkletwinkle' then gwin_k8_enable_light_experiments := true;
715 end;
717 e_WriteLog('Initializing OpenGL', MSG_NOTIFY);
718 InitOpenGL(gVSync);
720 e_WriteLog('Creating GL window', MSG_NOTIFY);
721 if not CreateGLWindow(PChar(Format('Doom 2D: Forever %s', [GAME_VERSION]))) then
722 begin
723 Result := 0;
724 exit;
725 end;
727 {EnumDisplayModes();}
729 if gwin_k8_enable_light_experiments then
730 begin
731 SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, @ltmp);
732 e_WriteLog(Format('stencil buffer size: %d', [ltmp]), MSG_WARNING);
733 gwin_has_stencil := (ltmp > 0);
734 end
735 else
736 begin
737 gwin_has_stencil := false;
738 end;
740 if not glHasExtension('GL_ARB_texture_non_power_of_two') then
741 begin
742 e_WriteLog('Driver DID''T advertised NPOT textures support', MSG_WARNING);
743 glLegacyNPOT := true;
744 end
745 else
746 begin
747 e_WriteLog('Driver advertised NPOT textures support', MSG_NOTIFY);
748 glLegacyNPOT := false;
749 end;
750 gwin_dump_extensions := false;
752 Init();
753 Time_Old := GetTimer();
755 // Êîìàíäíàÿ ñòðîêà:
756 if ParamCount > 0 then
757 g_Game_Process_Params();
759 // Çàïðîñ ÿçûêà:
760 if (not gGameOn) and gAskLanguage then
761 g_Menu_AskLanguage();
763 e_WriteLog('Entering the main loop', MSG_NOTIFY);
765 while not ProcessMessage() do
766 { Main Loop } ;
768 Release();
769 KillGLWindow();
771 Result := 0;
772 end;
774 end.