From 1b5401f0727c171cf02d4484624bb3198f7a10bd Mon Sep 17 00:00:00 2001 From: DeaDDooMER Date: Sat, 24 Feb 2018 22:11:16 +0300 Subject: [PATCH] Added touchscreen controls --- src/engine/e_graphics.pas | 33 ++++- src/engine/e_log.pas | 4 +- src/game/g_console.pas | 5 +- src/game/g_game.pas | 5 +- src/game/g_main.pas | 2 + src/game/g_options.pas | 5 +- src/game/g_touch.pas | 299 ++++++++++++++++++++++++++++++++++++++ src/game/g_window.pas | 10 +- src/lib/sdl2/sdlvideo.inc | 6 + 9 files changed, 360 insertions(+), 9 deletions(-) create mode 100644 src/game/g_touch.pas diff --git a/src/engine/e_graphics.pas b/src/engine/e_graphics.pas index 891c658..bfef6d2 100644 --- a/src/engine/e_graphics.pas +++ b/src/engine/e_graphics.pas @@ -958,8 +958,10 @@ end; procedure e_DrawQuad(X1, Y1, X2, Y2: Integer; Red, Green, Blue: Byte; Alpha: Byte = 0); var nX1, nY1, nX2, nY2: Integer; +{$IFDEF USE_NANOGL} + v: array [0..15] of GLfloat; +{$ENDIF} begin -{$IFNDEF USE_NANOGL} // FIXIT: nanoGL doesn't support glBegin(GL_LINES) if e_NoGraphics then Exit; // Only top-left/bottom-right quad if X1 > X2 then @@ -985,7 +987,34 @@ begin glDisable(GL_TEXTURE_2D); glColor4ub(Red, Green, Blue, 255-Alpha); glLineWidth(1); +{$IFDEF USE_NANOGL} + nX1 := X1; nY1 := Y1; + nX2 := X2; nY2 := Y1; + e_LineCorrection(nX1, nY1, nX2, nY2); + v[0] := nX1; v[1] := nY1; v[2] := nX2; v[3] := nY2; + + nX1 := X2; nY1 := Y1; + nX2 := X2; nY2 := Y2; + e_LineCorrection(nX1, nY1, nX2, nY2); + v[4] := nX1; v[5] := nY1; v[6] := nX2; v[7] := nY2; + + nX1 := X2; nY1 := Y2; + nX2 := X1; nY2 := Y2; + e_LineCorrection(nX1, nY1, nX2, nY2); + v[8] := nX1; v[9] := nY1; v[10] := nX2; v[11] := nY2; + + nX1 := X1; nY1 := Y2; + nX2 := X1; nY2 := Y1; + e_LineCorrection(nX1, nY1, nX2, nY2); + v[12] := nX1; v[13] := nY1; v[14] := nX2; v[15] := nY2; + glVertexPointer(2, GL_FLOAT, 0, @v[0]); + glEnableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDrawArrays(GL_LINES, 0, 16); +{$ELSE} glBegin(GL_LINES); nX1 := X1; nY1 := Y1; nX2 := X2; nY2 := Y1; @@ -1011,11 +1040,11 @@ begin glVertex2i(nX1, nY1); glVertex2i(nX2, nY2); glEnd(); +{$ENDIF} glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); glDisable(GL_BLEND); -{$ENDIF} end; procedure e_DrawFillQuad(X1, Y1, X2, Y2: Integer; Red, Green, Blue, Alpha: Byte; diff --git a/src/engine/e_log.pas b/src/engine/e_log.pas index e2a88e0..22db652 100644 --- a/src/engine/e_log.pas +++ b/src/engine/e_log.pas @@ -99,8 +99,8 @@ begin {$IFDEF ANDROID} cstr := GetMem(len + 1); for slen := 0 to len - 1 do - cstr[slen] := Char(b[slen]); - cstr[slen + 1] := #0; + cstr[slen] := Chr(b[slen]); + cstr[len] := #0; SDL_Log(cstr, []); Dispose(cstr); {$ENDIF} diff --git a/src/game/g_console.pas b/src/game/g_console.pas index 3ed1d13..230e5b6 100644 --- a/src/game/g_console.pas +++ b/src/game/g_console.pas @@ -60,7 +60,7 @@ implementation uses g_textures, g_main, e_graphics, e_input, g_game, - SysUtils, g_basic, g_options, Math, + SysUtils, g_basic, g_options, Math, g_touch, g_menu, g_language, g_net, g_netmsg, e_log, conbuf; @@ -926,6 +926,7 @@ begin if gChatShow then Exit; gConsoleShow := not gConsoleShow; Cons_Shown := True; + g_Touch_ShowKeyboard(gConsoleShow or gChatShow); end; procedure g_Console_Chat_Switch(Team: Boolean = False); @@ -938,6 +939,7 @@ begin gChatEnter := False; Line := ''; CPos := 1; + g_Touch_ShowKeyboard(gConsoleShow or gChatShow); end; procedure g_Console_Char(C: AnsiChar); @@ -1114,6 +1116,7 @@ begin CPos := 1; gChatShow := False; gJustChatted := True; + g_Touch_ShowKeyboard(gConsoleShow or gChatShow); end; end; IK_TAB: diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 0961896..5bceb41 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -22,7 +22,8 @@ uses SysUtils, Classes, MAPDEF, g_basic, g_player, e_graphics, g_res_downloader, - g_sound, g_gui, utils, md5, mempool, xprofiler; + g_sound, g_gui, utils, md5, mempool, xprofiler, + g_touch; type TGameSettings = record @@ -3759,6 +3760,8 @@ begin if gGameOn then drawProfilers(); g_Holmes_DrawUI(); + + g_Touch_Draw; end; procedure g_Game_Quit(); diff --git a/src/game/g_main.pas b/src/game/g_main.pas index 9a7250f..93655f6 100644 --- a/src/game/g_main.pas +++ b/src/game/g_main.pas @@ -116,8 +116,10 @@ begin raise Exception.Create('SDL: Init failed: ' + SDL_GetError()); {$IFNDEF HEADLESS} +{$IFNDEF ANDROID} SDL_StartTextInput(); {$ENDIF} +{$ENDIF} {$IFNDEF HEADLESS} flexloaded := true; diff --git a/src/game/g_options.pas b/src/game/g_options.pas index d05ed83..ee8518a 100644 --- a/src/game/g_options.pas +++ b/src/game/g_options.pas @@ -190,7 +190,10 @@ begin KeyPrevWeapon := 71; KeyOpen := 54; KeyStrafe := 0; - for i := 0 to High(KeyWeapon) do + for i := 0 to 9 do + KeyWeapon[i] := 30 + i; + KeyWeapon[10] := 45; + for i := 10 to High(KeyWeapon) do KeyWeapon[i] := 0; KeyRight2 := 0; diff --git a/src/game/g_touch.pas b/src/game/g_touch.pas new file mode 100644 index 0000000..0a02e57 --- /dev/null +++ b/src/game/g_touch.pas @@ -0,0 +1,299 @@ +(* 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 . + *) +{$INCLUDE ../shared/a_modes.inc} +unit g_touch; + +interface + + uses + SDL2; + + 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_weapons, g_console; + + const + CTL_NONE = 0; + CTL_LEFT = 1; + CTL_RIGHT = 2; + CTL_UP = 3; + CTL_DOWN = 4; + CTL_FIRE = 5; + CTL_OPEN = 6; + CTL_JUMP = 7; + CTL_CHAT = 8; + CTL_ESC = 9; + CTL_W0 = 10; + CTL_W1 = 11; + CTL_W2 = 12; + CTL_W3 = 13; + CTL_W4 = 14; + CTL_W5 = 15; + CTL_W6 = 16; + CTL_W7 = 17; + CTL_W8 = 18; + CTL_W9 = 19; + CTL_W10 = 20; + CTL_CON = 21; + CTL_STAT = 22; + CTL_TCHAT = 23; + CTL_LAST = 23; + + var + size: Single; + enabled: Boolean; + keyFinger: array [1..CTL_LAST] of Integer; + + procedure GetControlRect(control: Integer; out x, y, w, h: Integer; out founded: Boolean); + var + sw, sh, sz: Integer; + dpi: Single; + begin + if SDL_GetDisplayDPI(0, @dpi, nil, nil) <> 0 then + dpi := 96; + + founded := true; + sz := Trunc(size * dpi); + x := 0; y := 0; w := sz; h := sz; + sw := gScreenWidth; sh := gScreenHeight; + case control of + CTL_LEFT: begin x := 0; y := sh div 2 - h div 2; end; + CTL_RIGHT: begin x := w; y := sh div 2 - h div 2; end; + CTL_UP: begin x := sw - w - 1; y := sh div 2 - h div 2 - h; end; + CTL_DOWN: begin x := sw - w - 1; y := sh div 2 - h div 2 + h; end; + CTL_FIRE: begin x := sw - 1*w - 1; y := sh div 2 - h div 2; end; + CTL_OPEN: begin x := sw - 3*w - 1; y := sh div 2 - h div 2; end; + CTL_JUMP: begin x := sw - 2*w - 1; y := sh div 2 - h div 2; end; + else + w := sz div 2; h := sz div 2; + case control of + CTL_W0: begin x := sw div 2 - w div 2 - 5*w - 1; y := sh - 1*h - 1; end; + CTL_W1: begin x := sw div 2 - w div 2 - 4*w - 1; y := sh - 1*h - 1; end; + CTL_W2: begin x := sw div 2 - w div 2 - 3*w - 1; y := sh - 1*h - 1; end; + CTL_W3: begin x := sw div 2 - w div 2 - 2*w - 1; y := sh - 1*h - 1; end; + CTL_W4: begin x := sw div 2 - w div 2 - 1*w - 1; y := sh - 1*h - 1; end; + CTL_W5: begin x := sw div 2 - w div 2 + 0*w - 1; y := sh - 1*h - 1; end; + CTL_W6: begin x := sw div 2 - w div 2 + 1*w - 1; y := sh - 1*h - 1; end; + CTL_W7: begin x := sw div 2 - w div 2 + 2*w - 1; y := sh - 1*h - 1; end; + CTL_W8: begin x := sw div 2 - w div 2 + 3*w - 1; y := sh - 1*h - 1; end; + CTL_W9: begin x := sw div 2 - w div 2 + 4*w - 1; y := sh - 1*h - 1; end; + CTL_W10: begin x := sw div 2 - w div 2 + 5*w - 1; y := sh - 1*h - 1; end; + CTL_CHAT: begin x := sw div 2 - w div 2 - 2*w - 1; y := sh - 2*h - 1; end; + CTL_ESC: begin x := sw div 2 - w div 2 - 1*w - 1; y := sh - 2*h - 1; end; + CTL_CON: begin x := sw div 2 - w div 2 + 0*w - 1; y := sh - 2*h - 1; end; + CTL_STAT: begin x := sw div 2 - w div 2 + 1*w - 1; y := sh - 2*h - 1; end; + CTL_TCHAT: begin x := sw div 2 - w div 2 + 2*w - 1; y := sh - 2*h - 1; end; + else + founded := false + end + end + end; + + function GetMenuKey(control: Integer): Word; + begin + case control of + CTL_LEFT: result := IK_LEFT; + CTL_RIGHT: result := IK_RIGHT; + CTL_UP: result := IK_UP; + CTL_DOWN: result := IK_DOWN; + CTL_OPEN: result := IK_ENTER; + CTL_FIRE: result := IK_ENTER; + CTL_JUMP: result := IK_SPACE; + CTL_ESC: result := IK_ESCAPE; + CTL_W0: result := SDL_SCANCODE_0; + CTL_W1: result := SDL_SCANCODE_1; + CTL_W2: result := SDL_SCANCODE_2; + CTL_W3: result := SDL_SCANCODE_3; + CTL_W4: result := SDL_SCANCODE_4; + CTL_W5: result := SDL_SCANCODE_5; + CTL_W6: result := SDL_SCANCODE_6; + CTL_W7: result := SDL_SCANCODE_7; + CTL_W8: result := SDL_SCANCODE_8; + CTL_W9: result := SDL_SCANCODE_9; + CTL_CON: result := IK_GRAVE; + else + result := IK_INVALID; + end + end; + + function GetPlayerKey(control: Integer): Word; + begin + case control of + CTL_LEFT: result := gGameControls.P1Control.KeyLeft; + CTL_RIGHT: result := gGameControls.P1Control.KeyRight; + CTL_UP: result := gGameControls.P1Control.KeyUp; + CTL_DOWN: result := gGameControls.P1Control.KeyDown; + CTL_OPEN: result := gGameControls.P1Control.KeyOpen; + CTL_FIRE: result := gGameControls.P1Control.KeyFire; + CTL_JUMP: result := gGameControls.P1Control.KeyJump; + CTL_CHAT: result := gGameControls.GameControls.Chat; + CTL_ESC: result := IK_ESCAPE; + CTL_W0: result := gGameControls.P1Control.KeyWeapon[WEAPON_KASTET]; + CTL_W1: result := gGameControls.P1Control.KeyWeapon[WEAPON_SAW]; + CTL_W2: result := gGameControls.P1Control.KeyWeapon[WEAPON_PISTOL]; + CTL_W3: result := gGameControls.P1Control.KeyWeapon[WEAPON_SHOTGUN1]; + CTL_W4: result := gGameControls.P1Control.KeyWeapon[WEAPON_SHOTGUN2]; + CTL_W5: result := gGameControls.P1Control.KeyWeapon[WEAPON_CHAINGUN]; + CTL_W6: result := gGameControls.P1Control.KeyWeapon[WEAPON_ROCKETLAUNCHER]; + CTL_W7: result := gGameControls.P1Control.KeyWeapon[WEAPON_PLASMA]; + CTL_W8: result := gGameControls.P1Control.KeyWeapon[WEAPON_BFG]; + CTL_W9: result := gGameControls.P1Control.KeyWeapon[WEAPON_SUPERPULEMET]; + CTL_W10: result := gGameControls.P1Control.KeyWeapon[WEAPON_FLAMETHROWER]; + CTL_CON: result := IK_GRAVE; + CTL_STAT: result := gGameControls.GameControls.Stat; + CTL_TCHAT: result := gGameControls.GameControls.TeamChat; + else + result := IK_INVALID + end + end; + + function GetControlName(control: Integer): String; + begin + case control of + CTL_LEFT: result := 'LEFT'; + CTL_RIGHT: result := 'RIGHT'; + CTL_UP: result := 'UP'; + CTL_DOWN: result := 'DOWN'; + CTL_OPEN: result := 'OPEN'; + CTL_FIRE: result := 'FIRE'; + CTL_JUMP: result := 'JUMP'; + CTL_CHAT: result := 'CHAT'; + CTL_ESC: result := 'ESC'; + CTL_W0: result := '0'; + CTL_W1: result := '1'; + CTL_W2: result := '2'; + CTL_W3: result := '3'; + CTL_W4: result := '4'; + CTL_W5: result := '5'; + CTL_W6: result := '6'; + CTL_W7: result := '7'; + CTL_W8: result := '8'; + CTL_W9: result := '9'; + CTL_W10: result := '10'; + CTL_CON: result := 'CON'; + CTL_STAT: result := 'STAT'; + CTL_TCHAT: result := 'TEAM'; + else + result := '(WAT?)' + end + end; + + procedure DrawRect(x, y, w, h: Integer); + begin + e_DrawQuad(x, y, x + w, y + h, 0, 255, 0, 127); + end; + + function IntersectControl(ctl, xx, yy: Integer): Boolean; + var + x, y, w, h: Integer; + founded: Boolean; + begin + GetControlRect(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_ShowKeyboard(yes: Boolean); + begin +{$IFNDEF HEADLESS} + if not enabled then + Exit; + + if yes then + SDL_StartTextInput + else + SDL_StopTextInput +{$ENDIF} + end; + + procedure g_Touch_HandleEvent(const ev: TSDL_TouchFingerEvent); + var + x, y, i, finger: Integer; + begin + if not enabled then + Exit; + if SDL_IsTextInputActive() = SDL_True then + Exit; + + finger := ev.fingerId + 2; + x := Trunc(ev.x * gScreenWidth); + y := Trunc(ev.y * gScreenHeight); + + for i := 1 to CTL_LAST do + begin + if IntersectControl(i, x, y) then + begin + if ev.type_ = SDL_FINGERUP then + keyFinger[i] := 0 + else if ev.type_ = SDL_FINGERMOTION then + keyFinger[i] := finger + else if ev.type_ = SDL_FINGERDOWN then + begin + KeyPress(GetMenuKey(i)); + keyFinger[i] := finger; + end + end + else if keyFinger[i] = finger then + begin + if ev.type_ = SDL_FINGERUP then + keyFinger[i] := 0 + else if ev.type_ = SDL_FINGERMOTION then + keyFinger[i] := 0 + end; + + e_KeyUpDown(GetPlayerKey(i), keyFinger[i] <> 0); + e_KeyUpDown(GetMenuKey(i), keyFinger[i] <> 0); + end; + end; + + procedure g_Touch_Draw; + var + i, x, y, w, h: Integer; + founded: Boolean; + begin +{$IFNDEF HEADLESS} + if not enabled then + Exit; + if SDL_IsTextInputActive() = SDL_True then + Exit; + + for i := 1 to CTL_LAST do + begin + GetControlRect(i, x, y, w, h, founded); + if founded then + begin + DrawRect(x, y, w, h); + e_TextureFontPrint(x, y, GetControlName(i), gStdFont) + end; + end; +{$ENDIF} + end; + +initialization +{$IFDEF ANDROID} + enabled := true; +{$ENDIF} + size := 1; + conRegVar('touch_enable', @enabled, 'enable/disable virtual buttons', 'draw buttons'); + conRegVar('touch_size', @size, 0.1, 10, 'size of virtual buttons', 'button size'); +end. + + diff --git a/src/game/g_window.pas b/src/game/g_window.pas index 395f44e..3a29d08 100644 --- a/src/game/g_window.pas +++ b/src/game/g_window.pas @@ -1,4 +1,4 @@ - (* Copyright (C) Doom 2D: Forever Developers +(* 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 @@ -61,7 +61,8 @@ uses g_console, e_input, g_options, g_game, g_basic, g_textures, e_sound, g_sound, g_menu, ENet, g_net, g_map, g_gfx, g_monsters, g_holmes, xprofiler, - sdlcarcass, fui_ctls; + sdlcarcass, fui_ctls, + g_touch; const @@ -471,6 +472,8 @@ begin SDL_KEYDOWN, SDL_KEYUP: begin key := ev.key.keysym.scancode; + if key = SDL_SCANCODE_AC_BACK then + key := SDL_SCANCODE_ESCAPE; down := (ev.type_ = SDL_KEYDOWN); {$IF not DEFINED(HEADLESS)} if fuiOnSDLEvent(ev) then @@ -497,6 +500,9 @@ begin if (keychr > 0) and (keychr <= 255) then CharPress(AnsiChar(keychr)); end; + SDL_FINGERMOTION, SDL_FINGERDOWN, SDL_FINGERUP: + g_Touch_HandleEvent(ev.tfinger); + // other key presses and joysticks are handled in e_input end; end; diff --git a/src/lib/sdl2/sdlvideo.inc b/src/lib/sdl2/sdlvideo.inc index 2ec24e8..6c4c42c 100644 --- a/src/lib/sdl2/sdlvideo.inc +++ b/src/lib/sdl2/sdlvideo.inc @@ -389,6 +389,12 @@ function SDL_GetDisplayBounds(displayIndex: SInt32; rect: PSDL_Rect): SInt32 cde function SDL_GetNumDisplayModes(displayIndex: SInt32): SInt32 cdecl; external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_SDL_GetNumDisplayModes' {$ENDIF} {$ENDIF}; + {** + * Get the dots/pixels-per-inch for a display. + *} + +function SDL_GetDisplayDPI(displayIndex: SInt32; ddpi, hdpi, vdpi: PFloat): SInt32 cdecl; external SDL_LibName {$IFDEF DELPHI} {$IFDEF MACOS} name '_SDL_GetDisplayDPI' {$ENDIF} {$ENDIF}; + {** * Fill in information about a specific display mode. * -- 2.29.2