From 724d5405f0f3fe166b931b1b8b5745b2eb340651 Mon Sep 17 00:00:00 2001 From: DeaDDooMER Date: Tue, 8 Oct 2019 22:59:52 +0300 Subject: [PATCH] revive sdl2 system driver --- src/engine/e_sound_sdl.inc | 5 + src/game/g_main.pas | 34 --- src/game/sdl/g_system.pas | 21 +- src/game/sdl2/g_system.pas | 449 +++++++++++++++++++++++++++++++++++++ src/game/sdl2/g_touch.pas | 322 ++++++++++++++++++++++++++ src/game/stub/g_system.pas | 4 +- 6 files changed, 791 insertions(+), 44 deletions(-) create mode 100644 src/game/sdl2/g_system.pas create mode 100644 src/game/sdl2/g_touch.pas diff --git a/src/engine/e_sound_sdl.inc b/src/engine/e_sound_sdl.inc index 4d9eaba..ccfd9c4 100644 --- a/src/engine/e_sound_sdl.inc +++ b/src/engine/e_sound_sdl.inc @@ -178,6 +178,11 @@ begin Result := False; SoundInitialized := False; + {$IFDEF HEADLESS} + // HACK: shit this into env and hope for the best + SetEnvVar('SDL_AUDIODRIVER', 'dummy'); + {$ELSEIF} + if NoOutput then begin Result := true; Exit end; // wow, this is actually MIDI player! diff --git a/src/game/g_main.pas b/src/game/g_main.pas index 92c0ff5..222002b 100644 --- a/src/game/g_main.pas +++ b/src/game/g_main.pas @@ -39,9 +39,6 @@ uses {$INCLUDE ../nogl/noGLuses.inc} {$IFDEF ENABLE_HOLMES} g_holmes, fui_wadread, fui_style, fui_gfx_gl, -{$ENDIF} -{$IFDEF USE_SDL2} - SDL2, {$ENDIF} wadreader, e_log, g_window, e_graphics, e_input, g_game, g_console, g_gui, @@ -87,30 +84,6 @@ begin {$ENDIF} e_WriteToStdOut := False; //{$IFDEF HEADLESS}True;{$ELSE}False;{$ENDIF} -{$IFDEF USE_SDL2} -{$IFDEF HEADLESS} - {$IFDEF USE_SDLMIXER} - sdlflags := SDL_INIT_TIMER or SDL_INIT_AUDIO or $00004000; - // HACK: shit this into env and hope for the best - SetEnvVar('SDL_AUDIODRIVER', 'dummy'); - {$ELSE} - sdlflags := SDL_INIT_TIMER or $00004000; - {$ENDIF} -{$ELSE} - {$IFDEF USE_SDLMIXER} - {*sdlflags := SDL_INIT_EVERYTHING;*} - sdlflags := SDL_INIT_JOYSTICK or SDL_INIT_TIMER or SDL_INIT_VIDEO; - {$ELSE} - sdlflags := SDL_INIT_JOYSTICK or SDL_INIT_TIMER or SDL_INIT_VIDEO; - {$ENDIF} -{$ENDIF} - - SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, '0'); - - if SDL_Init(sdlflags) < 0 then - raise Exception.Create('SDL: Init failed: ' + SDL_GetError()); -{$ENDIF} - e_WriteLog('Init Input', TMsgType.Notify); e_InitInput; @@ -125,9 +98,7 @@ begin e_WriteLog(gLanguage, TMsgType.Notify); g_Language_Set(gLanguage); -{$IFNDEF USE_SDL2} sys_Init; -{$ENDIF} {$IF not DEFINED(HEADLESS) and DEFINED(ENABLE_HOLMES)} flexloaded := true; @@ -182,12 +153,7 @@ begin SDLMain(); {$WARNINGS ON} -{$IFDEF USE_SDL2} - e_WriteLog('Releasing SDL', TMsgType.Notify); - SDL_Quit(); -{$ELSE} sys_Final; -{$ENDIF} end; procedure Init(); diff --git a/src/game/sdl/g_system.pas b/src/game/sdl/g_system.pas index 5caa9c9..5fe7302 100644 --- a/src/game/sdl/g_system.pas +++ b/src/game/sdl/g_system.pas @@ -48,6 +48,9 @@ implementation e_log, e_graphics, e_input, g_options, g_window, g_console, g_game, g_menu, g_gui, g_main; + const + GameTitle = 'Doom 2D: Forever (SDL 1.2)'; + var userResize: Boolean; modeResize: Integer; @@ -57,7 +60,7 @@ implementation function sys_GetTicks (): Int64; begin - Result := SDL_GetTicks() + result := SDL_GetTicks() end; procedure sys_Delay (ms: Integer); @@ -89,8 +92,8 @@ implementation function InitWindow (w, h, bpp: Integer; fullScreen: Boolean): Boolean; var flags: Uint32; begin - e_LogWritefln('InitWindow %s %s %s %s', [w, h, bpp, fullscreen]); - Result := False; + e_LogWritefln('InitWindow %s %s %s %s', [w, h, bpp, fullScreen]); + result := False; SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); @@ -106,9 +109,9 @@ implementation screen := SDL_SetVideoMode(w, h, bpp, flags); if screen <> nil then begin - SDL_WM_SetCaption('Doom 2D: Forever (SDL 1.2)', nil); + SDL_WM_SetCaption(GameTitle, nil); UpdateSize(w, h); - Result := True + result := True end end else @@ -260,7 +263,7 @@ implementation end else if gConsoleShow or gChatShow or (g_ActiveWindow <> nil) then begin - KeyPress(key) + KeyPress(key) // key repeat in menus and shit end; if down and IsValid1251(ev.keysym.unicode) and IsPrintable1251(ch) then CharPress(ch) @@ -284,6 +287,7 @@ implementation InitWindow(ev.resize.w, ev.resize.h, gBPP, gFullscreen) end; SDL_KEYUP, SDL_KEYDOWN: HandleKeyboard(ev.key); + SDL_VIDEOEXPOSE: sys_Repaint; end end end; @@ -300,14 +304,15 @@ implementation procedure sys_Init; var flags: Uint32; ok: Boolean; begin + e_WriteLog('Init SDL', TMsgType.Notify); flags := SDL_INIT_VIDEO or SDL_INIT_AUDIO or SDL_INIT_TIMER or SDL_INIT_JOYSTICK (*or SDL_INIT_NOPARACHUTE*); if SDL_Init(flags) <> 0 then - raise Exception.Create('SDL: Init failed: ' + SDL_GetError()); + raise Exception.Create('SDL: Init failed: ' + SDL_GetError); ok := InitWindow(gScreenWidth, gScreenHeight, gBPP, gFullScreen); if not ok then - raise Exception.Create('SDL: failed to set videomode: ' + SDL_GetError); + raise Exception.Create('SDL: Failed to set videomode: ' + SDL_GetError); SDL_EnableUNICODE(1); SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); end; diff --git a/src/game/sdl2/g_system.pas b/src/game/sdl2/g_system.pas new file mode 100644 index 0000000..5a24a48 --- /dev/null +++ b/src/game/sdl2/g_system.pas @@ -0,0 +1,449 @@ +(* 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, version 3 of the License ONLY. + * + * 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 . + *) +{$INCLUDE ../shared/a_modes.inc} +unit g_system; + +interface + + uses Utils; + + (* --- Utils --- *) + function sys_GetTicks (): Int64; + procedure sys_Delay (ms: Integer); + + (* --- Graphics --- *) + function sys_GetDispalyModes (bpp: Integer): SSArray; + function sys_SetDisplayMode (w, h, bpp: Integer; fullscreen: Boolean): Boolean; + procedure sys_EnableVSync (yes: Boolean); + procedure sys_Repaint; + + (* --- Input --- *) + function sys_HandleInput (): Boolean; + procedure sys_RequestQuit; + + (* --- Init --- *) + procedure sys_Init; + procedure sys_Final; + +implementation + + uses + SysUtils, SDL2, GL, Math, + e_log, e_graphics, e_input, + g_touch, + g_options, g_window, g_console, g_game, g_menu, g_gui, g_main; + + const + GameTitle = 'Doom 2D: Forever (SDL 2)'; + + var + window: PSDL_Window; + context: TSDL_GLContext; + display: Integer; + JoystickHandle: array [0..e_MaxJoys - 1] of PSDL_Joystick; + JoystickHatState: array [0..e_MaxJoys - 1, 0..e_MaxJoyHats - 1, HAT_LEFT..HAT_DOWN] of Boolean; + JoystickZeroAxes: array [0..e_MaxJoys - 1, 0..e_MaxJoyAxes - 1] of Integer; + + (* --------- Utils --------- *) + + function sys_GetTicks (): Int64; + begin + result := SDL_GetTicks() + end; + + procedure sys_Delay (ms: Integer); + begin + SDL_Delay(ms) + end; + + (* --------- Graphics --------- *) + + procedure UpdateSize (w, h: Integer); + begin + gWinSizeX := w; + gWinSizeY := h; + gWinRealPosX := 0; + gWinRealPosY := 0; + gScreenWidth := w; + gScreenHeight := h; + {$IFDEF ENABLE_HOLMES} + fuiScrWdt := w; + fuiScrHgt := h; + {$ENDIF} + e_ResizeWindow(w, h); + e_InitGL; + g_Game_SetupScreenSize; + g_Menu_Reset; + g_Game_ClearLoading; + end; + + function InitWindow (w, h, bpp: Integer; fullScreen: Boolean): Boolean; + var flags: UInt32; + begin + e_LogWritefln('InitWindow %s %s %s %s', [w, h, bpp, fullScreen]); + result := false; + if window = nil then + begin + {$IFDEF USE_GLES1} + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + {$ELSE} + 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_STENCIL_SIZE, 8); // lights; it is enough to have 1-bit stencil buffer for lighting, but... + {$ENDIF} + flags := SDL_WINDOW_OPENGL or SDL_WINDOW_RESIZABLE; + if fullScreen then flags := flags or SDL_WINDOW_FULLSCREEN; + window := SDL_CreateWindow(GameTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, flags); + if window <> nil then + begin + context := SDL_GL_CreateContext(window); + if context <> nil then + begin + UpdateSize(w, h); + result := true + end + else + begin + e_LogWritefln('SDL: unable to create OpenGL context: %s', [SDL_GetError]) + end + end + else + begin + e_LogWritefln('SDL: unable to create window: %s', [SDL_GetError]) + end + end + else + begin + if fullScreen then flags := SDL_WINDOW_FULLSCREEN else flags := 0; + SDL_SetWindowSize(window, w, h); + SDL_SetWindowFullscreen(window, flags); + UpdateSize(w, h); + result := true + end + end; + + procedure sys_Repaint; + begin + SDL_GL_SwapWindow(window) + end; + + procedure sys_EnableVSync (yes: Boolean); + begin + if yes then + SDL_GL_SetSwapInterval(1) + else + SDL_GL_SetSwapInterval(0) + end; + + function sys_GetDispalyModes (bpp: Integer): SSArray; + var i, count, num, pw, ph: Integer; m: TSDL_DisplayMode; + begin + result := nil; + num := SDL_GetNumDisplayModes(display); + if num < 0 then + e_LogWritefln('SDL: unable to get numer of available display modes: %s', [SDL_GetError]); + if num > 0 then + begin + e_LogWritefln('Video modes for display %s:', [display]); + SetLength(result, num); + i := 0; count := 0; pw := 0; ph := 0; + while i < num do + begin + SDL_GetDisplayMode(display, i, @m); + if ((pw <> m.w) or (ph <> m.h)) then + begin + e_LogWritefln('* %sx%sx%s@%s', [m.w, m.h, SDL_BITSPERPIXEL(m.format), m.refresh_rate]); + pw := m.w; ph := m.h; + result[count] := IntToStr(m.w) + 'x' + IntToStr(m.h); + Inc(count); + end + else + begin + e_LogWritefln('- %sx%sx%s@%s', [m.w, m.h, SDL_BITSPERPIXEL(m.format), m.refresh_rate]); + end; + Inc(i) + end; + SetLength(result, count) + end + end; + + function sys_SetDisplayMode (w, h, bpp: Integer; fullScreen: Boolean): Boolean; + begin + result := InitWindow(w, h, bpp, fullScreen) + end; + + (* --------- Joystick --------- *) + + procedure HandleJoyButton (var ev: TSDL_JoyButtonEvent); + var down: Boolean; key: Integer; + begin + if (ev.which < e_MaxJoys) and (ev.button < e_MaxJoyBtns) then + begin + key := e_JoyButtonToKey(ev.which, ev.button); + down := ev.type_ = SDL_JOYBUTTONDOWN; + if g_dbg_input then + e_LogWritefln('Input Debug: jbutton, joy=%s, button=%s, keycode=%s, press=%s', [ev.which, ev.button, key, down]); + e_KeyUpDown(key, down); + g_Console_ProcessBind(key, down) + end + else + begin + if g_dbg_input then + begin + down := ev.type_ = SDL_JOYBUTTONDOWN; + e_LogWritefln('Input Debug: NOT IN RANGE! jbutton, joy=%s, button=%s, press=%s', [ev.which, ev.button, down]) + end + end + end; + + procedure HandleJoyAxis (var ev: TSDL_JoyAxisEvent); + var key, minuskey: Integer; + begin + if (ev.which < e_MaxJoys) and (ev.axis < e_MaxJoyAxes) then + begin + key := e_JoyAxisToKey(ev.which, ev.axis, AX_PLUS); + minuskey := e_JoyAxisToKey(ev.which, ev.axis, AX_MINUS); + + if g_dbg_input then + e_LogWritefln('Input Debug: jaxis, joy=%s, axis=%s, value=%s, zeroaxes=%s, deadzone=%s', [ev.which, ev.axis, ev.value, JoystickZeroAxes[ev.which, ev.axis], e_JoystickDeadzones[ev.which]]); + + if ev.value < JoystickZeroAxes[ev.which, ev.axis] - e_JoystickDeadzones[ev.which] then + begin + if (e_KeyPressed(key)) then + begin + e_KeyUpDown(key, False); + g_Console_ProcessBind(key, False) + end; + e_KeyUpDown(minuskey, True); + g_Console_ProcessBind(minuskey, True) + end + else if ev.value > JoystickZeroAxes[ev.which, ev.axis] + e_JoystickDeadzones[ev.which] then + begin + if (e_KeyPressed(minuskey)) then + begin + e_KeyUpDown(minuskey, False); + g_Console_ProcessBind(minuskey, False) + end; + e_KeyUpDown(key, True); + g_Console_ProcessBind(key, True) + end + else + begin + if (e_KeyPressed(minuskey)) then + begin + e_KeyUpDown(minuskey, False); + g_Console_ProcessBind(minuskey, False) + end; + if (e_KeyPressed(key)) then + begin + e_KeyUpDown(key, False); + g_Console_ProcessBind(key, False) + end + end + end + else + begin + if g_dbg_input then + e_LogWritefln('Input Debug: NOT IN RANGE! jaxis, joy=%s, axis=%s, value=%s, zeroaxes=%s, deadzone=%s', [ev.which, ev.axis, ev.value, JoystickZeroAxes[ev.which, ev.axis], e_JoystickDeadzones[ev.which]]) + end + end; + + procedure HandleJoyHat (var ev: TSDL_JoyHatEvent); + var + down: Boolean; + i, key: Integer; + hat: array [HAT_LEFT..HAT_DOWN] of Boolean; + begin + if (ev.which < e_MaxJoys) and (ev.hat < e_MaxJoyHats) then + begin + if g_dbg_input then + e_LogWritefln('Input Debug: jhat, joy=%s, hat=%s, value=%s', [ev.which, ev.hat, ev.value]); + hat[HAT_UP] := LongBool(ev.value and SDL_HAT_UP); + hat[HAT_DOWN] := LongBool(ev.value and SDL_HAT_DOWN); + hat[HAT_LEFT] := LongBool(ev.value and SDL_HAT_LEFT); + hat[HAT_RIGHT] := LongBool(ev.value and SDL_HAT_RIGHT); + for i := HAT_LEFT to HAT_DOWN do + begin + if JoystickHatState[ev.which, ev.hat, i] <> hat[i] then + begin + down := hat[i]; + key := e_JoyHatToKey(ev.which, ev.hat, i); + e_KeyUpDown(key, down); + g_Console_ProcessBind(key, down) + end + end; + JoystickHatState[ev.which, ev.hat] := hat + end + else + begin + if g_dbg_input then + e_LogWritefln('Input Debug: NOT IN RANGE! jhat, joy=%s, hat=%s, value=%s', [ev.which, ev.hat, ev.value]) + end; + end; + + procedure HandleJoyAdd (var ev: TSDL_JoyDeviceEvent); + var i: Integer; + begin + if (ev.which < e_MaxJoys) then + begin + JoystickHandle[ev.which] := SDL_JoystickOpen(ev.which); + if JoystickHandle[ev.which] <> nil then + begin + e_LogWritefln('Added Joystick %s', [ev.which]); + e_JoystickAvailable[ev.which] := True; + for i := 0 to Min(SDL_JoystickNumAxes(JoystickHandle[ev.which]), e_MaxJoyAxes) - 1 do + JoystickZeroAxes[ev.which, i] := SDL_JoystickGetAxis(JoystickHandle[ev.which], i) + end + else + begin + e_LogWritefln('Warning! Failed to open Joystick %s', [ev.which]) + end + end + else + begin + e_LogWritefln('Warning! Added Joystick %s, but we support only <= %s', [ev.which, e_MaxJoys]) + end + end; + + procedure HandleJoyRemove (var ev: TSDL_JoyDeviceEvent); + begin + e_LogWritefln('Removed Joystick %s', [ev.which]); + if (ev.which < e_MaxJoys) then + begin + e_JoystickAvailable[ev.which] := False; + if JoystickHandle[ev.which] <> nil then + SDL_JoystickClose(JoystickHandle[ev.which]); + JoystickHandle[ev.which] := nil + end + end; + + (* --------- Input --------- *) + + function HandleWindow (var ev: TSDL_WindowEvent): Boolean; + begin + result := false; + case ev.event of + SDL_WINDOWEVENT_RESIZED: UpdateSize(ev.data1, ev.data2); + SDL_WINDOWEVENT_EXPOSED: sys_Repaint; + SDL_WINDOWEVENT_CLOSE: result := true; + end + end; + + procedure HandleKeyboard (var ev: TSDL_KeyboardEvent); + var down: Boolean; key: Integer; + begin + key := ev.keysym.scancode; + down := (ev.type_ = SDL_KEYDOWN); + if key = SDL_SCANCODE_AC_BACK then + key := SDL_SCANCODE_ESCAPE; + if ev._repeat = 0 then + begin + if g_dbg_input then + e_LogWritefln('Input Debug: keysym, press=%s, scancode=%s', [down, key]); + e_KeyUpDown(key, down); + g_Console_ProcessBind(key, down); + end + else if gConsoleShow or gChatShow or (g_ActiveWindow <> nil) then + begin + KeyPress(key) // key repeat in menus and shit + end + end; + + procedure HandleTextInput (var ev: TSDL_TextInputEvent); + var ch: UnicodeChar; sch: AnsiChar; + begin + if g_dbg_input then + e_LogWritefln('Input Debug: text, text=%s', [ev.text]); + Utf8ToUnicode(@ch, PChar(ev.text), 1); + if IsValid1251(Word(ch)) then + begin + sch := AnsiChar(wchar2win(ch)); + CharPress(sch); + end; + end; + + function sys_HandleInput (): Boolean; + var ev: TSDL_Event; + begin + result := false; + while SDL_PollEvent(@ev) <> 0 do + begin + case ev.type_ of + SDL_QUITEV: result := true; + SDL_WINDOWEVENT: result := HandleWindow(ev.window); + SDL_KEYUP, SDL_KEYDOWN: HandleKeyboard(ev.key); + SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP: HandleJoyButton(ev.jbutton); + SDL_JOYAXISMOTION: HandleJoyAxis(ev.jaxis); + SDL_JOYHATMOTION: HandleJoyHat(ev.jhat); + SDL_JOYDEVICEADDED: HandleJoyAdd(ev.jdevice); + SDL_JOYDEVICEREMOVED: HandleJoyRemove(ev.jdevice); + SDL_TEXTINPUT: HandleTextInput(ev.text); + SDL_FINGERMOTION, SDL_FINGERDOWN, SDL_FINGERUP: g_Touch_HandleEvent(ev.tfinger); + end + end + end; + + procedure sys_RequestQuit; + var ev: TSDL_Event; + begin + ev.type_ := SDL_QUITEV; + SDL_PushEvent(@ev) + end; + + (* --------- Init --------- *) + + procedure sys_Init; + var flags: UInt32; ok: Boolean; + begin + e_WriteLog('Init SDL2', TMsgType.Notify); + {$IFDEF HEADLESS} + {$IFDEF USE_SDLMIXER} + flags := SDL_INIT_TIMER or SDL_INIT_AUDIO or $00004000; + {$ELSE} + flags := SDL_INIT_TIMER or $00004000; + {$ENDIF} + {$ELSE} + flags := SDL_INIT_JOYSTICK or SDL_INIT_TIMER or SDL_INIT_VIDEO; + {$ENDIF} + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, '0'); + if SDL_Init(flags) <> 0 then + raise Exception.Create('SDL: Init failed: ' + SDL_GetError); + ok := InitWindow(gScreenWidth, gScreenHeight, gBPP, gFullscreen); + if not ok then + raise Exception.Create('SDL: Failed to set videomode: ' + SDL_GetError); + end; + + procedure sys_Final; + begin + e_WriteLog('Releasing SDL2', TMsgType.Notify); + if context <> nil then + SDL_GL_DeleteContext(context); + if window <> nil then + SDL_DestroyWindow(window); + window := nil; + context := nil; + SDL_Quit + end; + +initialization + conRegVar('sdl2_display_index', @display, 'use display index as base', ''); +end. diff --git a/src/game/sdl2/g_touch.pas b/src/game/sdl2/g_touch.pas new file mode 100644 index 0000000..6971879 --- /dev/null +++ b/src/game/sdl2/g_touch.pas @@ -0,0 +1,322 @@ +(* 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, version 3 of the License ONLY. + * + * 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 . + *) +{$INCLUDE ../shared/a_modes.inc} +unit g_touch; + +interface + + uses + SDL2; + + var + g_touch_enabled: Boolean = False; + g_touch_size: Single = 1.0; + g_touch_offset: Single = 50.0; + g_touch_fire: Boolean = True; + g_touch_alt: Boolean = False; + + procedure g_Touch_Init; + procedure g_Touch_ShowKeyboard(yes: Boolean); + procedure g_Touch_HandleEvent(const ev: TSDL_TouchFingerEvent); + procedure g_Touch_Draw; + +implementation + + uses + SysUtils, + e_log, e_graphics, e_input, g_options, g_game, g_main, g_gui, g_weapons, g_console, g_window; + + var + angleFire: Boolean; + keyFinger: array [VK_FIRSTKEY..VK_LASTKEY] of Integer; + + procedure GetKeyRect(key: Integer; out x, y, w, h: Integer; out founded: Boolean); + var + sw, sh, sz: Integer; + dpi: Single; + + procedure S (xx, yy, ww, hh: Single); + begin + x := Trunc(xx); + y := Trunc(yy); + w := Trunc(ww); + h := Trunc(hh); + founded := true; + end; + + begin + founded := false; + if SDL_GetDisplayDPI(0, @dpi, nil, nil) <> 0 then + dpi := 96; + + sz := Trunc(g_touch_size * dpi); sw := gScreenWidth; sh := gScreenHeight; + x := 0; y := Round(sh * g_touch_offset / 100); + w := sz; h := sz; + + if SDL_IsTextInputActive() = SDL_True then + case key of + VK_HIDEKBD: S(sw - (sz/2), 0, sz / 2, sz / 2); + end + else if g_touch_alt then + case key of + (* top ------- x ------------------------------- y w ----- h -- *) + VK_CONSOLE: S(0, 0, sz / 2, sz / 2); + VK_ESCAPE: S(sw - 1*(sz/2) - 1, 0, sz / 2, sz / 2); + VK_SHOWKBD: S(sw - 2*(sz/2) - 1, 0, sz / 2, sz / 2); + VK_CHAT: S(sw / 2 - (sz/2) / 2 - (sz/2) - 1, 0, sz / 2, sz / 2); + VK_STATUS: S(sw / 2 - (sz/2) / 2 - 1, 0, sz / 2, sz / 2); + VK_TEAM: S(sw / 2 - (sz/2) / 2 + (sz/2) - 1, 0, sz / 2, sz / 2); + (* left --- x - y -------------- w - h --- *) + VK_PREV: S(0, sh - 3.0*sz - 1, sz, sz / 2); + VK_LEFT: S(0, sh - 2.0*sz - 1, sz, sz * 2); + VK_RIGHT: S(sz, sh - 2.0*sz - 1, sz, sz * 2); + (* right - x ------------ y -------------- w - h -- *) + VK_NEXT: S(sw - 1*sz - 1, sh - 3.0*sz - 1, sz, sz / 2); + VK_UP: S(sw - 2*sz - 1, sh - 2.0*sz - 1, sz, sz / 2); + VK_FIRE: S(sw - 2*sz - 1, sh - 1.5*sz - 1, sz, sz); + VK_DOWN: S(sw - 2*sz - 1, sh - 0.5*sz - 1, sz, sz / 2); + VK_JUMP: S(sw - 1*sz - 1, sh - 2.0*sz - 1, sz, sz); + VK_OPEN: S(sw - 1*sz - 1, sh - 1.0*sz - 1, sz, sz); + end + else + case key of + (* left ----- x ----- y -------------- w ----- h -- *) + VK_ESCAPE: S(0.0*sz, y - 1*sz - sz/2, sz, sz / 2); + VK_LSTRAFE: S(0.0*sz, y - 0*sz - sz/2, sz / 2, sz); + VK_LEFT: S(0.5*sz, y - 0*sz - sz/2, sz, sz); + VK_RIGHT: S(1.5*sz, y - 0*sz - sz/2, sz, sz); + VK_RSTRAFE: S(2.5*sz, y - 0*sz - sz/2, sz / 2, sz); + (* right - x ------------ y --------------- w - h *) + VK_UP: S(sw - 1*sz - 1, y - 1*sz - sz/2, sz, sz); + VK_FIRE: S(sw - 1*sz - 1, y - 0*sz - sz/2, sz, sz); + VK_DOWN: S(sw - 1*sz - 1, y - -1*sz - sz/2, sz, sz); + VK_NEXT: S(sw - 2*sz - 1, y - 1*sz - sz/2, sz, sz); + VK_JUMP: S(sw - 2*sz - 1, y - 0*sz - sz/2, sz, sz); + VK_PREV: S(sw - 3*sz - 1, y - 1*sz - sz/2, sz, sz); + VK_OPEN: S(sw - 3*sz - 1, y - 0*sz - sz/2, sz, sz); + (* bottom ---- x -------------------------- y ---------------- w ----- h -- *) + VK_CHAT: S(sw/2 - sz/4 - 2*(sz/2) - 1, sh - 2*(sz/2) - 1, sz / 2, sz / 2); + VK_CONSOLE: S(sw/2 - sz/4 - 1*(sz/2) - 1, sh - 2*(sz/2) - 1, sz / 2, sz / 2); + VK_STATUS: S(sw/2 - sz/4 - 0*(sz/2) - 1, sh - 2*(sz/2) - 1, sz / 2, sz / 2); + VK_TEAM: S(sw/2 - sz/4 - -1*(sz/2) - 1, sh - 2*(sz/2) - 1, sz / 2, sz / 2); + VK_SHOWKBD: S(sw/2 - sz/4 - -2*(sz/2) - 1, sh - 2*(sz/2) - 1, sz / 2, sz / 2); + VK_0: S(sw/2 - sz/4 - 5*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_1: S(sw/2 - sz/4 - 4*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_2: S(sw/2 - sz/4 - 3*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_3: S(sw/2 - sz/4 - 2*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_4: S(sw/2 - sz/4 - 1*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_5: S(sw/2 - sz/4 - 0*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_6: S(sw/2 - sz/4 - -1*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_7: S(sw/2 - sz/4 - -2*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_8: S(sw/2 - sz/4 - -3*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_9: S(sw/2 - sz/4 - -4*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + VK_A: S(sw/2 - sz/4 - -5*(sz/2) - 1, sh - 1*(sz/2) - 1, sz / 2, sz / 2); + end + end; + + function GetKeyName(key: Integer): String; + begin + case key of + VK_SHOWKBD: result := 'KBD'; + VK_HIDEKBD: result := 'KBD'; + VK_LEFT: result := 'LEFT'; + VK_RIGHT: result := 'RIGHT'; + VK_UP: result := 'UP'; + VK_DOWN: result := 'DOWN'; + VK_FIRE: result := 'FIRE'; + VK_OPEN: result := 'OPEN'; + VK_JUMP: result := 'JUMP'; + VK_CHAT: result := 'CHAT'; + VK_ESCAPE: result := 'ESC'; + VK_0: result := '0'; + VK_1: result := '1'; + VK_2: result := '2'; + VK_3: result := '3'; + VK_4: result := '4'; + VK_5: result := '5'; + VK_6: result := '6'; + VK_7: result := '7'; + VK_8: result := '8'; + VK_9: result := '9'; + VK_A: result := '10'; + VK_B: result := '11'; + VK_C: result := '12'; + VK_D: result := '13'; + VK_E: result := '14'; + VK_F: result := '15'; + VK_CONSOLE: result := 'CON'; + VK_STATUS: result := 'STAT'; + VK_TEAM: result := 'TEAM'; + VK_PREV: result := ' 0) and (key < e_MaxInputKeys) then + result := e_KeyNames[key] + else + result := '<' + IntToStr(key) + '>' + end + end; + + function IntersectControl(ctl, xx, yy: Integer): Boolean; + var + x, y, w, h: Integer; + founded: Boolean; + begin + GetKeyRect(ctl, x, y, w, h, founded); + result := founded and (xx >= x) and (yy >= y) and (xx <= x + w) and (yy <= y + h); + end; + + procedure g_Touch_Init; + begin +{$IFNDEF HEADLESS} + g_Touch_ShowKeyboard(FALSE); + g_touch_enabled := SDL_GetNumTouchDevices() > 0 +{$ENDIF} + end; + + procedure g_Touch_ShowKeyboard(yes: Boolean); + begin +{$IFNDEF HEADLESS} + if g_dbg_input then + e_LogWritefln('g_Touch_ShowKeyboard(%s)', [yes]); + (* on desktop we always receive text (needed for cheats) *) + if yes or (SDL_HasScreenKeyboardSupport() = SDL_FALSE) then + SDL_StartTextInput + else + SDL_StopTextInput +{$ENDIF} + end; + + procedure g_Touch_HandleEvent(const ev: TSDL_TouchFingerEvent); + var + x, y, i, finger: Integer; + + procedure KeyUp (finger, i: Integer); + begin + if g_dbg_input then + e_LogWritefln('Input Debug: g_touch.KeyUp, finger=%s, key=%s', [finger, i]); + + keyFinger[i] := 0; + e_KeyUpDown(i, False); + g_Console_ProcessBind(i, False); + + (* up/down + fire hack *) + if g_touch_fire and (gGameSettings.GameType <> GT_NONE) and (g_ActiveWindow = nil) and angleFire then + begin + if (i = VK_UP) or (i = VK_DOWN) then + begin + angleFire := False; + keyFinger[VK_FIRE] := 0; + e_KeyUpDown(VK_FIRE, False); + g_Console_ProcessBind(VK_FIRE, False) + end + end + end; + + procedure KeyDown (finger, i: Integer); + begin + if g_dbg_input then + e_LogWritefln('Input Debug: g_touch.KeyDown, finger=%s, key=%s', [finger, i]); + + keyFinger[i] := finger; + e_KeyUpDown(i, True); + g_Console_ProcessBind(i, True); + + (* up/down + fire hack *) + if g_touch_fire and (gGameSettings.GameType <> GT_NONE) and (g_ActiveWindow = nil) then + begin + if i = VK_UP then + begin + angleFire := True; + keyFinger[VK_FIRE] := -1; + e_KeyUpDown(VK_FIRE, True); + g_Console_ProcessBind(VK_FIRE, True) + end + else if i = VK_DOWN then + begin + angleFire := True; + keyFinger[VK_FIRE] := -1; + e_KeyUpDown(VK_FIRE, True); + g_Console_ProcessBind(VK_FIRE, True) + end + end + end; + + procedure KeyMotion (finger, i: Integer); + begin + if keyFinger[i] <> finger then + begin + KeyUp(finger, i); + KeyDown(finger, i) + end + end; + + begin + if not g_touch_enabled then + Exit; + + finger := ev.fingerId + 2; + x := Trunc(ev.x * gScreenWidth); + y := Trunc(ev.y * gScreenHeight); + + for i := VK_FIRSTKEY to VK_LASTKEY do + begin + if IntersectControl(i, x, y) then + begin + if ev.type_ = SDL_FINGERUP then + KeyUp(finger, i) + else if ev.type_ = SDL_FINGERMOTION then + KeyMotion(finger, i) + else if ev.type_ = SDL_FINGERDOWN then + keyDown(finger, i) + end + else if keyFinger[i] = finger then + begin + if ev.type_ = SDL_FINGERUP then + KeyUp(finger, i) + else if ev.type_ = SDL_FINGERMOTION then + KeyUp(finger, i) + end + end + end; + + procedure g_Touch_Draw; + var i, x, y, w, h: Integer; founded: Boolean; + begin +{$IFNDEF HEADLESS} + if not g_touch_enabled then + Exit; + + for i := VK_FIRSTKEY to VK_LASTKEY do + begin + GetKeyRect(i, x, y, w, h, founded); + if founded then + begin + e_DrawQuad(x, y, x + w, y + h, 0, 255, 0, 31); + e_TextureFontPrintEx(x, y, GetKeyName(i), gStdFont, 255, 255, 255, 1, True) + end + end +{$ENDIF} + end; + +initialization + conRegVar('touch_enable', @g_touch_enabled, 'enable/disable virtual buttons', 'draw buttons'); + conRegVar('touch_fire', @g_touch_fire, 'enable/disable fire when press virtual up/down', 'fire when press up/down'); + conRegVar('touch_size', @g_touch_size, 0.1, 10, 'size of virtual buttons', 'button size'); + conRegVar('touch_offset', @g_touch_offset, 0, 100, '', ''); + conRegVar('touch_alt', @g_touch_alt, 'althernative virtual buttons layout', 'althernative layout'); +end. diff --git a/src/game/stub/g_system.pas b/src/game/stub/g_system.pas index 84ae121..4e9fced 100644 --- a/src/game/stub/g_system.pas +++ b/src/game/stub/g_system.pas @@ -62,12 +62,12 @@ implementation function sys_GetDispalyModes (bpp: Integer): SSArray; begin - SetLength(result, 0); + result := nil end; function sys_SetDisplayMode (w, h, bpp: Integer; fullscreen: Boolean): Boolean; begin - result := True + result := true end; (* --------- Input --------- *) -- 2.29.2