From 2d2523df4a5e748e40842a1e2763bdf8a4c96f6e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Oct 2022 05:18:19 +0300 Subject: [PATCH] engine: client: touch: generalise touch emulation code * fix doubleclicks and wheels in VGUI --- engine/client/in_touch.c | 98 +++++++++++++++++++------------- engine/client/input.c | 64 ++++++++++++++++----- engine/client/input.h | 5 +- engine/client/keys.c | 1 - engine/client/vgui/vgui_draw.c | 73 +++++++++++++++--------- engine/client/vgui/vgui_draw.h | 2 + engine/platform/linux/in_evdev.c | 21 +++---- engine/platform/sdl/events.c | 37 +++++------- engine/platform/sdl/in_sdl.c | 7 ++- 9 files changed, 182 insertions(+), 126 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index a41a7e28..81d58e9a 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -151,7 +151,6 @@ convar_t *touch_exp_mult; convar_t *touch_grid_enable; convar_t *touch_grid_count; convar_t *touch_config_file; -convar_t *touch_enable; convar_t *touch_in_menu; convar_t *touch_joy_radius; convar_t *touch_dpad_radius; @@ -162,7 +161,9 @@ convar_t *touch_highlight_b; convar_t *touch_highlight_a; convar_t *touch_precise_amount; convar_t *touch_joy_texture; -convar_t *touch_emulate; + +CVAR_DEFINE_AUTO( touch_enable, DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" ); +CVAR_DEFINE_AUTO( touch_emulate, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "emulate touch with mouse" ); // code looks smaller with it #define B(x) (button->x) @@ -1080,8 +1081,8 @@ void Touch_Init( void ) touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy", FCVAR_FILTERABLE, "texture for move indicator"); // input devices cvar - touch_enable = Cvar_Get( "touch_enable", DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" ); - touch_emulate = Cvar_Get( "touch_emulate", "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "emulate touch with mouse" ); + Cvar_RegisterVariable( &touch_enable ); + Cvar_RegisterVariable( &touch_emulate ); /// TODO: touch sdl platform // SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" ); @@ -1343,7 +1344,7 @@ void Touch_Draw( void ) { touch_button_t *button; - if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) ) + if( !touch.initialized || ( !touch_enable.value && !touch.clientonly )) return; Touch_InitConfig(); @@ -1937,9 +1938,8 @@ static int Touch_ControlsEvent( touchEventType type, int fingerID, float x, floa int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ) { - // simulate menu mouse click - if( cls.key_dest != key_game && !CVAR_TO_BOOL(touch_in_menu) ) + if( cls.key_dest != key_game && !CVAR_TO_BOOL( touch_in_menu )) { touch.move_finger = touch.resize_finger = touch.look_finger = -1; // Hack for keyboard, hope it help @@ -1982,25 +1982,20 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx { VGui_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) ); - if( type != event_motion ) - VGui_KeyEvent( K_MOUSE1, type == event_down ? 1 : 0 ); - - // allow scoreboard scroll - if( host.mouse_visible && type == event_motion ) - return 0; + switch( type ) + { + case event_down: + VGui_MouseEvent( K_MOUSE1, 1 ); + break; + case event_up: + VGui_MouseEvent( K_MOUSE1, 0 ); + break; + default: break; + } } - if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) ) - { -#if 0 - if( type == event_down ) - Key_Event( K_MOUSE1, true ); - if( type == event_up ) - Key_Event( K_MOUSE1, false ); - Android_AddMove( dx * (float)refState.width, dy * (float)refState.height ); -#endif + if( !touch.initialized || ( !touch_enable.value && !touch.clientonly )) return 0; - } if( clgame.dllFuncs.pfnTouchEvent && clgame.dllFuncs.pfnTouchEvent( type, fingerID, x, y, dx, dy ) ) return true; @@ -2019,35 +2014,60 @@ void Touch_GetMove( float *forward, float *side, float *yaw, float *pitch ) void Touch_KeyEvent( int key, int down ) { - int xi, yi; - float x, y; static float lx, ly; + static int kidNamedFinger = -1; + touchEventType event; + float x, y; + int finger, xi, yi; - if( !CVAR_TO_BOOL(touch_emulate) ) + if( !touch_emulate.value ) { - if( CVAR_TO_BOOL(touch_enable) ) + if( touch_enable.value ) return; if( !touch.clientonly ) return; } - Platform_GetMousePos( &xi, &yi ); - - x = xi/SCR_W; - y = yi/SCR_H; + if( !key ) + { + if( kidNamedFinger < 0 ) + return; - if( cls.key_dest == key_menu && down < 2 && key == K_MOUSE1 ) + finger = kidNamedFinger; + event = event_motion; + } + else { - UI_MouseMove( xi, yi ); - UI_KeyEvent( key, down ); + finger = key == K_MOUSE1 ? 0 : 1; + if( down ) + { + event = event_down; + kidNamedFinger = finger; + } + else + { + event = event_up; + kidNamedFinger = -1; + } } - if( down == 1 ) - Touch_ControlsEvent( event_down, key == K_MOUSE1?0:1, x, y, 0, 0 ); - else - Touch_ControlsEvent( down? event_motion: event_up, key == K_MOUSE1?0:1, x, y, x-lx, y-ly ); - lx = x, ly = y; + // don't deactivate mouse in game + // checking a case when mouse and touchscreen + // can be used simultaneously + Platform_SetCursorType( dc_arrow ); + Platform_GetMousePos( &xi, &yi ); + + x = xi / SCR_W; + y = yi / SCR_H; + + Con_DPrintf( "event %d %.2f %.2f %.2f %.2f\n", + event, x, y, x - lx, y - ly ); + + IN_TouchEvent( event, finger, x, y, x - lx, y - ly ); + + lx = x; + ly = y; } void Touch_Shutdown( void ) diff --git a/engine/client/input.c b/engine/client/input.c index 69c41ca8..43647e57 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -63,7 +63,7 @@ uint IN_CollectInputDevices( void ) if( !m_ignore->value ) // no way to check is mouse connected, so use cvar only ret |= INPUT_DEVICE_MOUSE; - if( CVAR_TO_BOOL(touch_enable) ) + if( touch_enable.value ) ret |= INPUT_DEVICE_TOUCH; if( Joy_IsActive() ) // connected or enabled @@ -94,13 +94,13 @@ void IN_LockInputDevices( qboolean lock ) { SetBits( m_ignore->flags, FCVAR_READ_ONLY ); SetBits( joy_enable->flags, FCVAR_READ_ONLY ); - SetBits( touch_enable->flags, FCVAR_READ_ONLY ); + SetBits( touch_enable.flags, FCVAR_READ_ONLY ); } else { ClearBits( m_ignore->flags, FCVAR_READ_ONLY ); ClearBits( joy_enable->flags, FCVAR_READ_ONLY ); - ClearBits( touch_enable->flags, FCVAR_READ_ONLY ); + ClearBits( touch_enable.flags, FCVAR_READ_ONLY ); } } @@ -308,25 +308,39 @@ IN_MouseMove */ void IN_MouseMove( void ) { - POINT current_pos; + int x, y; if( !in_mouseinitialized ) return; + if( FBitSet( touch_emulate.flags, FCVAR_CHANGED )) + { + // FIXME: do not hide cursor if it was requested by GUI + if( !touch_emulate.value ) + Platform_SetCursorType( dc_none ); + + ClearBits( touch_emulate.flags, FCVAR_CHANGED ); + } + + if( touch_emulate.value ) + { + // touch emulation overrides all input + Touch_KeyEvent( 0, 0 ); + return; + } + // find mouse movement - Platform_GetMousePos( ¤t_pos.x, ¤t_pos.y ); + Platform_GetMousePos( &x, &y ); - VGui_MouseMove( current_pos.x, current_pos.y ); + VGui_MouseMove( x, y ); // HACKHACK: show cursor in UI, as mainui doesn't call // platform-dependent SetCursor anymore -#if XASH_SDL - if( UI_IsVisible() ) - SDL_ShowCursor( SDL_TRUE ); -#endif + if( UI_IsVisible( )) + Platform_SetCursorType( dc_arrow ); // if the menu is visible, move the menu cursor - UI_MouseMove( current_pos.x, current_pos.y ); + UI_MouseMove( x, y ); } /* @@ -336,8 +350,6 @@ IN_MouseEvent */ void IN_MouseEvent( int key, int down ) { - int i; - if( !in_mouseinitialized ) return; @@ -345,10 +357,15 @@ void IN_MouseEvent( int key, int down ) SetBits( in_mstate, BIT( key )); else ClearBits( in_mstate, BIT( key )); - if( cls.key_dest == key_game ) + // touch emulation overrides all input + if( touch_emulate.value ) + { + Touch_KeyEvent( K_MOUSE1 + key, down ); + } + else if( cls.key_dest == key_game ) { // perform button actions - VGui_KeyEvent( K_MOUSE1 + key, down ); + VGui_MouseEvent( K_MOUSE1 + key, down ); // don't do Key_Event here // client may override IN_MouseEvent @@ -363,6 +380,23 @@ void IN_MouseEvent( int key, int down ) } } +/* +============== +IN_MWheelEvent + +direction is negative for wheel down, otherwise wheel up +============== +*/ +void IN_MWheelEvent( int y ) +{ + int b = y > 0 ? K_MWHEELUP : K_MWHEELDOWN; + + VGui_MWheelEvent( y ); + + Key_Event( b, true ); + Key_Event( b, false ); +} + /* =========== IN_Shutdown diff --git a/engine/client/input.h b/engine/client/input.h index 32ad2b6b..25e2df49 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -34,6 +34,7 @@ void IN_Init( void ); void Host_InputFrame( void ); void IN_Shutdown( void ); void IN_MouseEvent( int key, int down ); +void IN_MWheelEvent( int direction ); void IN_ActivateMouse( void ); void IN_DeactivateMouse( void ); void IN_MouseSavePos( void ); @@ -57,8 +58,8 @@ typedef enum event_motion } touchEventType; -extern convar_t *touch_enable; -extern convar_t *touch_emulate; +extern convar_t touch_enable; +extern convar_t touch_emulate; void Touch_Draw( void ); void Touch_SetClientOnly( byte state ); diff --git a/engine/client/keys.c b/engine/client/keys.c index 682f44ad..a1e696c8 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -712,7 +712,6 @@ void GAME_EXPORT Key_Event( int key, int down ) } VGui_KeyEvent( key, down ); - Touch_KeyEvent( key, down ); // console key is hardcoded, so the user can never unbind it if( key == '`' || key == '~' ) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 56f456f1..67449772 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -402,9 +402,8 @@ enum VGUI_KeyCode VGUI_MapKey( int keyCode ) { VGUI_InitKeyTranslationTable(); - if( keyCode < 0 || keyCode >= (int)sizeof( s_pVirtualKeyTrans ) / (int)sizeof( s_pVirtualKeyTrans[0] )) + if( keyCode < 0 || keyCode >= ARRAYSIZE( s_pVirtualKeyTrans )) { - //Assert( false ); return (enum VGUI_KeyCode)-1; } else @@ -413,48 +412,66 @@ enum VGUI_KeyCode VGUI_MapKey( int keyCode ) } } -void VGui_KeyEvent( int key, int down ) +void VGui_MouseEvent( int key, int clicks ) { + enum VGUI_MouseAction mact; + enum VGUI_MouseCode code; + if( !vgui.initialized ) return; switch( key ) { - case K_MOUSE1: - if( down && host.mouse_visible ) { - Key_EnableTextInput(true, false); - } - vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_LEFT ); - return; - case K_MOUSE2: - vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_RIGHT ); - return; - case K_MOUSE3: - vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_MIDDLE ); + case K_MOUSE1: code = MOUSE_LEFT; break; + case K_MOUSE2: code = MOUSE_RIGHT; break; + case K_MOUSE3: code = MOUSE_MIDDLE; break; + default: return; + } + + if( clicks >= 2 ) + mact = MA_DOUBLE; + else if( clicks == 1 ) + mact = MA_PRESSED; + else + mact = MA_RELEASED; + + vgui.Mouse( mact, code ); +} + +void VGui_MWheelEvent( int y ) +{ + if( !vgui.initialized ) return; - case K_MWHEELDOWN: - vgui.Mouse( MA_WHEEL, 1 ); + + vgui.Mouse( MA_WHEEL, y ); +} + +void VGui_KeyEvent( int key, int down ) +{ + enum VGUI_KeyCode code; + + if( !vgui.initialized ) return; - case K_MWHEELUP: - vgui.Mouse( MA_WHEEL, -1 ); + + if(( code = VGUI_MapKey( key )) < 0 ) return; - default: - break; - } - if( down == 2 ) - vgui.Key( KA_TYPED, VGUI_MapKey( key ) ); - else - vgui.Key( down?KA_PRESSED:KA_RELEASED, VGUI_MapKey( key ) ); - //Msg("VGui_KeyEvent %d %d %d\n", key, VGUI_MapKey( key ), down ); + if( down ) + { + vgui.Key( KA_PRESSED, code ); + vgui.Key( KA_TYPED, code ); + } + else vgui.Key( KA_RELEASED, code ); } void VGui_MouseMove( int x, int y ) { - float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; - float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; if( vgui.initialized ) + { + float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; + float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; vgui.MouseMove( x / xscale, y / yscale ); + } } void VGui_Paint( void ) diff --git a/engine/client/vgui/vgui_draw.h b/engine/client/vgui/vgui_draw.h index dd5a4ad2..4119774c 100644 --- a/engine/client/vgui/vgui_draw.h +++ b/engine/client/vgui/vgui_draw.h @@ -29,6 +29,8 @@ void VGui_Startup( const char *clientlib, int width, int height ); void VGui_Shutdown( void ); void VGui_Paint( void ); void VGui_RunFrame( void ); +void VGui_MouseEvent( int key, int clicks ); +void VGui_MWheelEvent( int y ); void VGui_KeyEvent( int key, int down ); void VGui_MouseMove( int x, int y ); qboolean VGui_IsActive( void ); diff --git a/engine/platform/linux/in_evdev.c b/engine/platform/linux/in_evdev.c index 638ceddb..46075f38 100644 --- a/engine/platform/linux/in_evdev.c +++ b/engine/platform/linux/in_evdev.c @@ -343,23 +343,16 @@ void IN_EvdevFrame ( void ) { switch ( ev.code ) { - case REL_X: dx += ev.value; + case REL_X: + dx += ev.value; break; - case REL_Y: dy += ev.value; + case REL_Y: + dy += ev.value; break; - case REL_WHEEL: - if( ev.value > 0) - { - Key_Event( K_MWHEELDOWN, 1 ); - Key_Event( K_MWHEELDOWN, 0 ); - } - else - { - Key_Event( K_MWHEELUP, 1 ); - Key_Event( K_MWHEELUP, 0 ); - } + case REL_WHEEL: + IN_MWheelEvent( ev.value ); break; } } @@ -367,7 +360,7 @@ void IN_EvdevFrame ( void ) { int key = KeycodeFromEvdev( ev.code, ev.value ); - if( CVAR_TO_BOOL(evdev_keydebug) ) + if( CVAR_TO_BOOL( evdev_keydebug )) Con_Printf( "key %d %d %d\n", ev.code, key, ev.value ); Key_Event( key , ev.value ); diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 86d5cde0..2a1a217c 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -250,18 +250,6 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key ) Key_Event( keynum, down ); } -static void SDLash_MouseKey( int key, int down, int istouch ) -{ - if( CVAR_TO_BOOL( touch_emulate ) ) - { - Touch_KeyEvent( key, down ); - } - else if( in_mouseinitialized && !m_ignore->value && !istouch ) - { - Key_Event( key, down ); - } -} - /* ============= SDLash_MouseEvent @@ -270,14 +258,20 @@ SDLash_MouseEvent */ static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) { - int down = button.state != SDL_RELEASED; - uint mstate = 0; + int down; #if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( button.which == SDL_TOUCH_MOUSEID ) return; #endif + if( button.state == SDL_RELEASED ) + down = 0; + else if( button.clicks >= 2 ) + down = 2; // special state for double-click in UI + else + down = 1; + switch( button.button ) { case SDL_BUTTON_LEFT: @@ -297,10 +291,10 @@ static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) break; #if ! SDL_VERSION_ATLEAST( 2, 0, 0 ) case SDL_BUTTON_WHEELUP: - Key_Event( K_MWHEELUP, down ); + IN_MWheelEvent( -1 ); break; case SDL_BUTTON_WHEELDOWN: - Key_Event( K_MWHEELDOWN, down ); + IN_MWheelEvent( 1 ); break; #endif // ! SDL_VERSION_ATLEAST( 2, 0, 0 ) default: @@ -435,9 +429,7 @@ static void SDLash_EventFilter( SDL_Event *event ) /* Mouse events */ case SDL_MOUSEMOTION: if( host.mouse_visible ) - { SDL_GetRelativeMouseState( NULL, NULL ); - } break; case SDL_MOUSEBUTTONUP: @@ -478,12 +470,8 @@ static void SDLash_EventFilter( SDL_Event *event ) break; #if SDL_VERSION_ATLEAST( 2, 0, 0 ) case SDL_MOUSEWHEEL: - { - int wheelbutton = event->wheel.y < 0 ? K_MWHEELDOWN : K_MWHEELUP; - Key_Event( wheelbutton, true ); - Key_Event( wheelbutton, false ); + IN_MWheelEvent( event->wheel.y ); break; - } /* Touch events */ case SDL_FINGERDOWN: @@ -556,7 +544,8 @@ static void SDLash_EventFilter( SDL_Event *event ) { // Swap axis to follow default axis binding: // LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft - static int sdlControllerAxisToEngine[] = { + static int sdlControllerAxisToEngine[] = + { JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX, JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY, JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX, diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 7f18c8ab..42757dca 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -333,11 +333,12 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) break; } - host.mouse_visible = visible; - - if( CVAR_TO_BOOL( touch_emulate )) + // never disable cursor in touch emulation mode + if( !visible && touch_emulate.value ) return; + host.mouse_visible = visible; + #if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( host.mouse_visible ) {