Make PDA work with gamepad, incl. making Pad A emulate leftclick

this is a bit hacky and ugly, and doesn't work properly in multiplayer
mode yet, see FIXME in idUserInterfaceLocal::Activate()
This commit is contained in:
Daniel Gibson 2024-01-17 06:33:06 +01:00
parent e0bb01ef52
commit 03ec74fd6f
4 changed files with 90 additions and 11 deletions

View file

@ -353,6 +353,7 @@ private:
void CircleToSquare( float & axis_x, float & axis_y ) const;
void HandleJoystickAxis( int keyNum, float unclampedValue, float threshold, bool positive );
void JoystickMove( void );
void JoystickFakeMouse(float axis_x, float axis_y, float deadzone);
void MouseMove( void );
void CmdButtons( void );
@ -429,7 +430,7 @@ idCVar idUsercmdGenLocal::m_strafeSmooth( "m_strafeSmooth", "4", CVAR_SYSTEM | C
idCVar idUsercmdGenLocal::m_showMouseRate( "m_showMouseRate", "0", CVAR_SYSTEM | CVAR_BOOL, "shows mouse movement" );
idCVar joy_triggerThreshold( "joy_triggerThreshold", "0.05", CVAR_FLOAT | CVAR_ARCHIVE, "how far the joystick triggers have to be pressed before they register as down" );
idCVar joy_deadZone( "joy_deadZone", "0.4", CVAR_FLOAT | CVAR_ARCHIVE, "specifies how large the dead-zone is on the joystick" );
idCVar joy_deadZone( "joy_deadZone", "0.25", CVAR_FLOAT | CVAR_ARCHIVE, "specifies how large the dead-zone is on the joystick" );
idCVar joy_range( "joy_range", "1.0", CVAR_FLOAT | CVAR_ARCHIVE, "allow full range to be mapped to a smaller offset" );
idCVar joy_gammaLook( "joy_gammaLook", "1", CVAR_INTEGER | CVAR_ARCHIVE, "use a log curve instead of a power curve for movement" );
idCVar joy_powerScale( "joy_powerScale", "2", CVAR_FLOAT | CVAR_ARCHIVE, "Raise joystick values to this power" );
@ -870,6 +871,33 @@ void idUsercmdGenLocal::HandleJoystickAxis( int keyNum, float unclampedValue, fl
}
}
static float joyAxisToMouseDelta(float axis, float deadzone)
{
float ret = 0.0f;
float val = fabsf(axis); // calculations below require a positive value
if(val > deadzone) {
// from deadzone .. 1 to 0 .. 1-deadzone
val -= deadzone;
// and then to 0..1
val = val * (1.0f / (1.0f - deadzone));
// make it exponential curve - exp(val*3) should return sth between 1 and 20;
// then turning that into 0.5 .. 10
ret = expf( val * 3.0f ) * 0.5f;
if(axis < 0.0f) // restore sign
ret = -ret;
}
return ret;
}
void idUsercmdGenLocal::JoystickFakeMouse(float axis_x, float axis_y, float deadzone)
{
float x = joyAxisToMouseDelta(axis_x, deadzone);
float y = joyAxisToMouseDelta(axis_y, deadzone);
continuousMouseX += x;
continuousMouseY += y;
}
/*
=================
idUsercmdGenLocal::JoystickMove
@ -888,6 +916,8 @@ void idUsercmdGenLocal::JoystickMove() {
HandleJoystickAxis( K_JOY_STICK1_LEFT, axis_x, threshold, false );
HandleJoystickAxis( K_JOY_STICK1_RIGHT, axis_x, threshold, true );
JoystickFakeMouse( axis_x, axis_y, threshold );
axis_y = joystickAxis[ AXIS_RIGHT_Y ];
axis_x = joystickAxis[ AXIS_RIGHT_X ];
CircleToSquare( axis_x, axis_y );
@ -897,6 +927,8 @@ void idUsercmdGenLocal::JoystickMove() {
HandleJoystickAxis( K_JOY_STICK2_LEFT, axis_x, threshold, false );
HandleJoystickAxis( K_JOY_STICK2_RIGHT, axis_x, threshold, true );
JoystickFakeMouse( axis_x, axis_y, threshold );
HandleJoystickAxis( K_JOY_TRIGGER1, joystickAxis[ AXIS_LEFT_TRIG ], triggerThreshold, true );
HandleJoystickAxis( K_JOY_TRIGGER2, joystickAxis[ AXIS_RIGHT_TRIG ], triggerThreshold, true );
}

View file

@ -61,6 +61,7 @@ If you have questions concerning this license or the applicable additional terms
#endif
extern idCVar in_useGamepad; // from UsercmdGen.cpp
extern idCVar joy_deadZone; // ditto
// NOTE: g++-4.7 doesn't like when this is static (for idCmdSystem::ArgCompletion_String<kbdNames>)
const char *_in_kbdNames[] = {
@ -898,6 +899,25 @@ void Sys_GrabMouseCursor(bool grabIt) {
GLimp_GrabInput(flags);
}
static bool interactiveGuiActive = false;
/*
===============
Sys_SetInteractiveIngameGuiActive
Tell the input system that currently an interactive *ingame* UI has focus,
so there is an active cursor.
Used for an ungodly hack to make gamepad button south (A) behave like
left mouse button in that case, so "clicking" with gamepad in the PDA
(and ingame GUIs) works as expected.
Not set for proper menus like main menu etc - the gamepad hacks for that
are in idUserInterfaceLocal::HandleEvent().
I hope this won't explode in my face :-p
===============
*/
void Sys_SetInteractiveIngameGuiActive(bool active)
{
interactiveGuiActive = active;
}
static void PushButton( int key, bool value ) {
// So we don't keep sending the same SE_KEY message over and over again
@ -1218,20 +1238,27 @@ sysEvent_t Sys_GetEvent() {
common->Warning( "Gamepad support is disabled! Set the in_useGamepad CVar to 1 to enable it!\n" );
continue;
}
res.evType = SE_KEY;
res.evValue2 = ev.cbutton.state == SDL_PRESSED ? 1 : 0;
// special case: always treat the start button as escape so it opens/closes the menu
// (also makes that button non-bindable)
if ( ev.cbutton.button == SDL_CONTROLLER_BUTTON_START ) {
res.evType = SE_KEY;
res.evValue = K_ESCAPE;
res.evValue2 = ev.cbutton.state == SDL_PRESSED ? 1 : 0;
return res;
} else if( ev.cbutton.button == SDL_CONTROLLER_BUTTON_A && interactiveGuiActive && sessLocal.GetActiveMenu() == NULL ) {
// ugly hack: currently an interactive ingame GUI (with a cursor) is active/focused
// so pretend that the gamepads A (south) button is the left mouse button
// so it can be used for "clicking"..
mouse_polls.Append( mouse_poll_t(M_ACTION1, res.evValue2) );
res.evValue = K_MOUSE1;
return res;
}
sys_jEvents jEvent = mapjoybutton( (SDL_GameControllerButton)ev.cbutton.button);
joystick_polls.Append(joystick_poll_t(jEvent, ev.cbutton.state == SDL_PRESSED ? 1 : 0) );
res.evType = SE_KEY;
res.evValue2 = ev.cbutton.state == SDL_PRESSED ? 1 : 0;
if ( ( jEvent >= J_ACTION_FIRST ) && ( jEvent <= J_ACTION_MAX ) ) {
res.evValue = K_FIRST_JOY + ( jEvent - J_ACTION_FIRST );
return res;
@ -1274,18 +1301,28 @@ sysEvent_t Sys_GetEvent() {
// NOTE: the stuff set here is only used to move the cursor in menus
// ingame movement is done via joystick_polls
int axis = jEvent - J_AXIS_MIN;
float val = ev.caxis.value * (1.25f / 32767.0f);
// 25% deadzone
if( val < 0.0f ) {
val = fminf(val + 0.25f, 0.0f);
float dz = joy_deadZone.GetFloat();
float val = fabsf(ev.caxis.value * (1.0f / 32767.0f));
if(val < dz) {
val = 0.0f;
} else {
val = fmaxf(val - 0.25f, 0.0f);
// from deadzone .. 1 to 0 .. 1-deadzone
val -= dz;
// and then to 0..1
val = val * (1.0f / (1.0f - dz));
if( ev.caxis.value < 0 ) {
val = -val;
}
}
joyAxis[axis] = val;
}
continue; // try to get a decent event.
// handle next event; joy axis events are generated below,
// when there are no further SDL events
continue;
}
break;

View file

@ -248,6 +248,11 @@ void Sys_EndJoystickInputEvents();
// when in windowed mode
void Sys_GrabMouseCursor( bool grabIt );
// DG: added this for an ungodly hack for gamepad support
// active = true means "currently a GUI with a cursor is active/focused"
// active = false means "that GUI is not active anymore"
void Sys_SetInteractiveIngameGuiActive(bool active);
void Sys_ShowWindow( bool show );
bool Sys_IsWindowVisible( void );
void Sys_ShowConsole( int visLevel, bool quitOnClose );

View file

@ -582,6 +582,11 @@ const char *idUserInterfaceLocal::Activate(bool activate, int _time) {
time = _time;
active = activate;
if ( desktop ) {
// FIXME: this works ok, mostly, except in multiplayer, where this function
// is called twice with activate=true, and the first time GetActiveMenu() returns NULL, the second time not
if(interactive && sessLocal.GetActiveMenu() == NULL) {
Sys_SetInteractiveIngameGuiActive(activate);
}
activateStr = "";
desktop->Activate( activate, activateStr );
return activateStr;