DEADSOFTWARE

we should set OpenGL flags before creating game window
[d2df-sdl.git] / src / game / g_window.pas
index bb20c47dd3feba89956de37b19402342bcbdecc8..651675a2572865cb3fc2feeaeb657f963a9efbb5 100644 (file)
@@ -1,9 +1,25 @@
+(* Copyright (C)  DooM 2D:Forever Developers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$MODE DELPHI}
 unit g_window;
 
 interface
 
 uses
-  WADEDITOR;
+  wadreader;
 
 function  SDLMain(): Integer;
 function  GetTimer(): Int64;
@@ -23,23 +39,30 @@ function  g_Window_SetSize(W, H: Word; FScreen: Boolean): Boolean;
 implementation
 
 uses
-  SDL, GL, GLExt, e_graphics, e_log, g_main,
+{$IFDEF WINDOWS}Windows,{$ENDIF}
+  SDL2, GL, GLExt, e_graphics, e_log, g_main,
   g_console, SysUtils, e_input, g_options, g_game,
   g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net;
 
 var
-  h_Wnd: PSDL_Surface;
+  h_Wnd: PSDL_Window;
+  h_GL: TSDL_GLContext;
   wFlags: LongWord = 0;
   Time, Time_Delta, Time_Old: Int64;
   flag: Boolean;
+  wTitle: PChar = nil;
   wNeedTimeReset: Boolean = False;
-  wWindowCreated: Boolean = False;
+  //wWindowCreated: Boolean = False;
   //wCursorShown: Boolean = False;
   wMinimized: Boolean = False;
   //wNeedFree: Boolean = True;
   wLoadingProgress: Boolean = False;
   wLoadingQuit: Boolean = False;
   {wWinPause: Byte = 0;}
+{$IFNDEF WINDOWS}
+  ticksOverflow: Int64 = -1;
+  lastTicks: Uint32 = 0; // to detect overflow
+{$ENDIF}
 
 const
   // TODO: move this to a separate file
@@ -56,34 +79,64 @@ const
 
 // TODO: make a transition table or something
 function WCharToCP1251(wc: Word): Word;
+var
+  n: Word;
 begin
-  for Result := 0 to 127 do
-    if CP1251[Result] = wc then
-      break;
+  Result := 0;
+  for n := 0 to 127 do
+    if CP1251[n] = wc then begin Result := n; break end;
   Result := Result + 128;
 end;
 
 function g_Window_SetDisplay(PreserveGL: Boolean = False): Boolean;
+var
+  mode, cmode: TSDL_DisplayMode;
 begin
+{$IFDEF HEADLESS}
+  Result := True;
+  Exit;
+{$ENDIF}
+
   Result := False;
 
   e_WriteLog('Setting display mode...', MSG_NOTIFY);
 
-  if wWindowCreated and PreserveGL then
-    e_SaveGLContext(); // we need this and restore because of a bug in SDL1.2, apparently
+  wFlags := SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE;
+  if gFullscreen then wFlags := wFlags or SDL_WINDOW_FULLSCREEN;
+  if gWinMaximized then wFlags := wFlags or SDL_WINDOW_MAXIMIZED;
 
-  wFlags := SDL_RESIZABLE or SDL_OPENGL;
-  if gFullscreen then wFlags := wFlags or SDL_FULLSCREEN;
+  if h_Wnd <> nil then
+  begin
+    SDL_DestroyWindow(h_Wnd);
+    h_Wnd := nil;
+  end;
 
-  h_Wnd := SDL_SetVideoMode(gScreenWidth, gScreenHeight, gBPP, wFlags);
-  SDL_EnableUNICODE(SDL_ENABLE);
-  SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
-  SDL_ShowCursor(SDL_DISABLE);
+  if gFullscreen then
+  begin
+    mode.w := gScreenWidth;
+    mode.h := gScreenHeight;
+    mode.format := 0;
+    mode.refresh_rate := 0;
+    mode.driverdata := nil;
+    if SDL_GetClosestDisplayMode(0, @mode, @cmode) = nil then
+    begin
+      gScreenWidth := 800;
+      gScreenHeight := 600;
+    end
+    else
+    begin
+      gScreenWidth := cmode.w;
+      gScreenHeight := cmode.h;
+    end;
+  end;
+
+  h_Wnd := SDL_CreateWindow(PChar(wTitle), gWinRealPosX, gWinRealPosY, gScreenWidth, gScreenHeight, wFlags);
+  if h_Wnd = nil then Exit;
 
-  if wWindowCreated and PreserveGL then
-    e_RestoreGLContext();
+  SDL_GL_MakeCurrent(h_Wnd, h_GL);
+  SDL_ShowCursor(SDL_DISABLE);
 
-  Result := h_Wnd <> nil;
+  Result := True;
 end;
 
 procedure ReShowCursor();
@@ -93,32 +146,29 @@ end;
 
 function GetDisplayModes(dBPP: DWORD; var SelRes: DWORD): SArray;
 var
-  modesp: PPSDL_Rect;
-  tmpp: PSDL_Rect;
-  tmpr: SDL_Rect;
-  i: Integer;
+  mode: TSDL_DisplayMode;
+  res, i, k, n, pw, ph: Integer;
 begin
   SetLength(Result, 0);
-  modesp := SDL_ListModes(nil, SDL_FULLSCREEN or SDL_HWSURFACE);
-  if modesp = nil then exit;
-  if Pointer(-1) = modesp then exit;
-
-  tmpp := modesp^;
-  i := 0;
-  while tmpp <> nil do
+  {$IFDEF HEADLESS}Exit;{$ENDIF}
+  k := 0; SelRes := 0;
+  n := SDL_GetNumDisplayModes(0);
+  pw := 0; ph := 0;
+  for i := 0 to n do
   begin
-    tmpr := tmpp^;
-    if (tmpr.w = gScreenWidth) and (tmpr.h = gScreenHeight) then
-      SelRes := i;
-    SetLength(Result, Length(Result) + 1);
-    Result[i] := IntToStr(tmpr.w) + 'x' + IntToStr(tmpr.h);
-
-    modesp := Pointer(Cardinal(modesp) + SizeOf(PSDL_Rect));
-    tmpp := modesp^;
-    Inc(i);
+    res := SDL_GetDisplayMode(0, i, @mode);
+    if res < 0 then continue;
+    if SDL_BITSPERPIXEL(mode.format) = gBPP then continue;
+    if (mode.w = pw) and (mode.h = ph) then continue;
+    if (mode.w = gScreenWidth) and (mode.h = gScreenHeight) then
+      SelRes := k;
+    Inc(k);
+    SetLength(Result, k);
+    Result[k-1] := IntToStr(mode.w) + 'x' + IntToStr(mode.h);
+    pw := mode.w; ph := mode.h
   end;
 
-  e_WriteLog('SDL: Got ' + IntToStr(Length(Result)) + ' resolutions.', MSG_NOTIFY);
+  e_WriteLog('SDL: Got ' + IntToStr(k) + ' resolutions.', MSG_NOTIFY);
 end;
 
 procedure Sleep(ms: LongWord);
@@ -130,6 +180,7 @@ procedure ChangeWindowSize();
 begin
   gWinSizeX := gScreenWidth;
   gWinSizeY := gScreenHeight;
+  {$IFDEF HEADLESS}Exit;{$ENDIF}
   e_ResizeWindow(gScreenWidth, gScreenHeight);
   g_Game_SetupScreenSize();
   g_Menu_Reset();
@@ -141,6 +192,7 @@ var
   Preserve: Boolean;
 begin
   Result := False;
+  {$IFDEF HEADLESS}Exit;{$ENDIF}
   Preserve := False;
 
   if (gScreenWidth <> W) or (gScreenHeight <> H) then
@@ -164,103 +216,162 @@ begin
   end;
 end;
 
-function EventHandler(ev: TSDL_Event): Boolean;
+function WindowEventHandler(ev: TSDL_WindowEvent): Boolean;
 var
-  key, keychr: Word;
-  //joy: Integer;
+  wActivate, wDeactivate: Boolean;
 begin
   Result := False;
-  case ev.type_ of
-    SDL_VIDEORESIZE:
+  wActivate := False;
+  wDeactivate := False;
+
+  case ev.event of
+    SDL_WINDOWEVENT_MOVED:
     begin
-      g_Window_SetSize(ev.resize.w, ev.resize.h, gFullscreen);
-      e_Clear();
+      if not (gFullscreen or gWinMaximized) then
+      begin
+        gWinRealPosX := ev.data1;
+        gWinRealPosY := ev.data2;
+      end;
     end;
 
-    SDL_ACTIVEEVENT:
+    SDL_WINDOWEVENT_MINIMIZED:
     begin
-      if (ev.active.gain = 0) then
+      if not wMinimized then
       begin
+        e_ResizeWindow(0, 0);
+        wMinimized := True;
+
         if g_debug_WinMsgs then
         begin
-          g_Console_Add('Inactive');
-          e_WriteLog('[DEBUG] WinMsgs: Inactive', MSG_NOTIFY);
+          g_Console_Add('Now minimized');
+          e_WriteLog('[DEBUG] WinMsgs: Now minimized', MSG_NOTIFY);
         end;
+        wDeactivate := True;
+      end;
+    end;
 
-        if LongBool(ev.active.state and SDL_APPINPUTFOCUS) and gWinActive then
-        begin
-          e_EnableInput := False;
-          e_ClearInputBuffer();
-
-          if gMuteWhenInactive then
-            e_MuteChannels(True);
-
-          if g_debug_WinMsgs then
-          begin
-            g_Console_Add('Inactive indeed');
-            e_WriteLog('[DEBUG] WinMsgs: Inactive indeed', MSG_NOTIFY);
-          end;
+    SDL_WINDOWEVENT_RESIZED:
+    begin
+      gScreenWidth := ev.data1;
+      gScreenHeight := ev.data2;
+      ChangeWindowSize();
+      SwapBuffers();
+      if g_debug_WinMsgs then
+      begin
+        g_Console_Add('Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2));
+        e_WriteLog('[DEBUG] WinMsgs: Resized to ' + IntToStr(ev.data1) + 'x' + IntToStr(ev.data2), MSG_NOTIFY);
+      end;
+    end;
 
-          gWinActive := False;
-        end;
+    SDL_WINDOWEVENT_EXPOSED:
+      SwapBuffers();
 
-        if LongBool(ev.active.state and SDL_APPACTIVE) and (not wMinimized) then
-        begin
-          e_ResizeWindow(0, 0);
-          wMinimized := True;
-
-          if g_debug_WinMsgs then
-          begin
-            g_Console_Add('Minimized indeed');
-            e_WriteLog('[DEBUG] WinMsgs: Minimized indeed', MSG_NOTIFY);
-          end;
-        end;
-      end
-      else
+    SDL_WINDOWEVENT_MAXIMIZED:
+    begin
+      if wMinimized then
       begin
+        e_ResizeWindow(gScreenWidth, gScreenHeight);
+        wMinimized := False;
+        wActivate := True;
+      end;
+      if not gWinMaximized then
+      begin
+        gWinMaximized := True;
         if g_debug_WinMsgs then
         begin
-          g_Console_Add('Active');
-          e_WriteLog('[DEBUG] WinMsgs: Active', MSG_NOTIFY);
+          g_Console_Add('Now maximized');
+          e_WriteLog('[DEBUG] WinMsgs: Now maximized', MSG_NOTIFY);
         end;
+      end;
+    end;
 
-        // Åñëè îêíî áûëî íåàêòèâíûì:
-        if LongBool(ev.active.state and SDL_APPINPUTFOCUS) and (not gWinActive) then
-        begin
-          e_EnableInput := True;
-
-          if gMuteWhenInactive then
-            e_MuteChannels(False);
+    SDL_WINDOWEVENT_RESTORED:
+    begin
+      if wMinimized then
+      begin
+        e_ResizeWindow(gScreenWidth, gScreenHeight);
+        wMinimized := False;
+        wActivate := True;
+      end;
+      if gWinMaximized then
+        gWinMaximized := False;
+      if g_debug_WinMsgs then
+      begin
+        g_Console_Add('Now restored');
+        e_WriteLog('[DEBUG] WinMsgs: Now restored', MSG_NOTIFY);
+      end;
+    end;
 
-          if g_debug_WinMsgs then
-          begin
-            g_Console_Add('Active indeed');
-            e_WriteLog('[DEBUG] WinMsgs: Active indeed', MSG_NOTIFY);
-          end;
+    SDL_WINDOWEVENT_FOCUS_GAINED:
+    begin
+      wActivate := True;
+      //e_WriteLog('window gained focus!', MSG_NOTIFY);
+    end;
 
-          gWinActive := True;
-        end;
+    SDL_WINDOWEVENT_FOCUS_LOST:
+    begin
+      wDeactivate := True;
+      //e_WriteLog('window lost focus!', MSG_NOTIFY);
+    end;
+  end;
 
-        if LongBool(ev.active.state and SDL_APPACTIVE) and wMinimized then
-        begin
-          e_ResizeWindow(gScreenWidth, gScreenHeight);
+  if wDeactivate then
+  begin
+    if gWinActive then
+    begin
+      e_WriteLog('deactivating window', MSG_NOTIFY);
+      e_EnableInput := False;
+      e_ClearInputBuffer();
 
-          wMinimized := False;
+      if gMuteWhenInactive then
+      begin
+        //e_WriteLog('deactivating sounds', MSG_NOTIFY);
+        e_MuteChannels(True);
+      end;
 
-          if g_debug_WinMsgs then
-          begin
-            g_Console_Add('Restored indeed');
-            e_WriteLog('[DEBUG] WinMsgs: Restored indeed', MSG_NOTIFY);
-          end;
-        end;
+      if g_debug_WinMsgs then
+      begin
+        g_Console_Add('Now inactive');
+        e_WriteLog('[DEBUG] WinMsgs: Now inactive', MSG_NOTIFY);
       end;
-    end;
 
-    SDL_VIDEOEXPOSE:
+      gWinActive := False;
+    end;
+  end
+  else if wActivate then
+  begin
+    if not gWinActive then
     begin
-      // TODO: the fuck is this event?
-      // Draw();
+      //e_WriteLog('activating window', MSG_NOTIFY);
+      e_EnableInput := True;
+
+      if gMuteWhenInactive then
+      begin
+        //e_WriteLog('activating sounds', MSG_NOTIFY);
+        e_MuteChannels(False);
+      end;
+
+      if g_debug_WinMsgs then
+      begin
+        g_Console_Add('Now active');
+        e_WriteLog('[DEBUG] WinMsgs: Now active', MSG_NOTIFY);
+      end;
+
+      gWinActive := True;
     end;
+  end;
+end;
+
+function EventHandler(ev: TSDL_Event): Boolean;
+var
+  key, keychr: Word;
+  uc: UnicodeChar;
+  //joy: Integer;
+begin
+  Result := False;
+  case ev.type_ of
+    SDL_WINDOWEVENT:
+      Result := WindowEventHandler(ev.window);
 
     SDL_QUITEV:
     begin
@@ -279,29 +390,36 @@ begin
 
     SDL_KEYDOWN:
     begin
-      key := ev.key.keysym.sym;
-      keychr := ev.key.keysym.unicode;
+      key := ev.key.keysym.scancode;
       KeyPress(key);
-      if (keychr > 7) and (key <> IK_BACKSPACE) then
-      begin
-        if (keychr >= 128) then
-          keychr := WCharToCP1251(keychr);
-        CharPress(Chr(keychr));
-      end;
     end;
 
-    // key presses and joysticks are handled in e_input
+    SDL_TEXTINPUT:
+    begin
+      Utf8ToUnicode(@uc, PChar(ev.text.text), 1);
+      keychr := Word(uc);
+      if (keychr > 127) then
+        keychr := WCharToCP1251(keychr);
+      CharPress(Chr(keychr));
+    end;
+
+    // other key presses and joysticks are handled in e_input
   end;
 end;
 
 procedure SwapBuffers();
 begin
-  SDL_GL_SwapBuffers();
+  {$IFDEF HEADLESS}Exit;{$ENDIF}
+  SDL_GL_SwapWindow(h_Wnd);
 end;
 
 procedure KillGLWindow();
 begin
-  wWindowCreated := False;
+  if h_Wnd <> nil then SDL_DestroyWindow(h_Wnd);
+  if h_GL <> nil then SDL_GL_DeleteContext(h_GL);
+  h_Wnd := nil;
+  h_GL := nil;
+  //wWindowCreated := False;
 end;
 
 function CreateGLWindow(Title: PChar): Boolean;
@@ -313,6 +431,7 @@ begin
   gWinSizeX := gScreenWidth;
   gWinSizeY := gScreenHeight;
 
+  wTitle := Title;
   e_WriteLog('Creating window', MSG_NOTIFY);
 
   if not g_Window_SetDisplay() then
@@ -322,8 +441,11 @@ begin
     exit;
   end;
 
-  SDL_WM_SetCaption(Title, Title);
-  wWindowCreated := True;
+{$IFNDEF HEADLESS}
+  h_Gl := SDL_GL_CreateContext(h_Wnd);
+  if h_Gl = nil then Exit;
+{$ENDIF}
+  //wWindowCreated := True;
 
   e_ResizeWindow(gScreenWidth, gScreenHeight);
   e_InitGL();
@@ -331,10 +453,42 @@ begin
   Result := True;
 end;
 
+{$IFDEF WINDOWS}
+// windoze sux; in headless mode `GetTickCount()` (and SDL) returns shit
 function GetTimer(): Int64;
+var
+  F, C: Int64;
+begin
+  QueryPerformanceFrequency(F);
+  QueryPerformanceCounter(C);
+  Result := Round(C/F*1000{000});
+end;
+{$ELSE}
+function GetTimer(): Int64;
+var
+  t: Uint32;
+  tt: Int64;
 begin
-  Result := SDL_GetTicks() * 1000; // TODO: do we really need microseconds here?
+  t := SDL_GetTicks() {* 1000}; // TODO: do we really need microseconds here? k8: NOPE!
+  if ticksOverflow = -1 then
+  begin
+    ticksOverflow := 0;
+    lastTicks := t;
+  end
+  else
+  begin
+    if lastTicks > t then
+    begin
+      // overflow, increment overflow ;-)
+      ticksOverflow := ticksOverflow+(Int64($ffffffff)+Int64(1));
+      tt := (Int64($ffffffff)+Int64(1))+Int64(t);
+      t := Uint32(tt-lastTicks);
+    end;
+  end;
+  lastTicks := t;
+  result := ticksOverflow+Int64(t);
 end;
+{$ENDIF}
 
 procedure ResetTimer();
 begin
@@ -354,6 +508,7 @@ var
   ev: TSDL_Event;
   ID: DWORD;
 begin
+  FillChar(ev, SizeOf(ev), 0);
   //wNeedFree := False;
   wLoadingProgress := True;
   while SDL_PollEvent(@ev) > 0 do
@@ -398,6 +553,7 @@ var
   ev: TSDL_Event;
 begin
   Result := False;
+  FillChar(ev, SizeOf(ev), 0);
 
   while SDL_PollEvent(@ev) > 0 do
   begin
@@ -412,11 +568,11 @@ begin
 
   if wNeedTimeReset then
   begin
-    Time_Delta := 27777;
+    Time_Delta := 28{(27777 div 1000)};
     wNeedTimeReset := False;
   end;
 
-  t := Time_Delta div 27777;
+  t := Time_Delta div 28{(27777 div 1000)};
   if t > 0 then
   begin
     flag := True;
@@ -448,7 +604,7 @@ begin
 // Âðåìÿ ïðåäûäóùåãî îáíîâëåíèÿ:
   if flag then
   begin
-    Time_Old := Time - (Time_Delta mod 27777);
+    Time_Old := Time - (Time_Delta mod 28{(27777 div 1000)});
     if (not wMinimized) then
     begin
       Draw();
@@ -472,17 +628,27 @@ procedure InitOpenGL(VSync: Boolean);
 var
   v: Byte;
 begin
+  {$IFDEF HEADLESS}Exit;{$ENDIF}
   if VSync then v := 1 else v := 0;
+  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
+  SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
   SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
   SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
   SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
   SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
-  SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, v);
+  SDL_GL_SetSwapInterval(v);
 end;
 
 function SDLMain(): Integer;
 begin
+{$IFDEF HEADLESS}
+  e_NoGraphics := True;
+{$ENDIF}
+
+  e_WriteLog('Initializing OpenGL', MSG_NOTIFY);
+  InitOpenGL(gVSync);
+
   e_WriteLog('Creating GL window', MSG_NOTIFY);
   if not CreateGLWindow(PChar(Format('Doom 2D: Forever %s', [GAME_VERSION]))) then
   begin
@@ -490,9 +656,6 @@ begin
     exit;
   end;
 
-  e_WriteLog('Initializing OpenGL', MSG_NOTIFY);
-  InitOpenGL(gVSync);
-
   {EnumDisplayModes();}
 
   Init();