Various gamepad improvements

- treat DPad as 4 regular buttons (was already the case mostly, but now
  the code is simpler)
- rename in_invertLook to joy_invertLook and in_useJoystick to
  in_useGamepad and remove unused CVars
- make controller Start button generate K_ESCAPE events, so it can
  always be used to open/close the menu (similar to D3BFG)
- move mousecursor with sticks, A button (south) for left-click,
  B button (east) for right-click (doesn't work in PDA yet)
- removed special handling of K_JOY_BTN_* in idWindow::HandleEvent()
  by generating fake mouse button events for gamepad A/B
  in idUserInterfaceLocal::HandleEvent()
This commit is contained in:
Daniel Gibson 2024-01-16 17:26:45 +01:00
parent f8557f6bd5
commit 6eac0540bf
7 changed files with 131 additions and 58 deletions

View file

@ -122,6 +122,10 @@ static const keyname_t keynames[] =
{"JOY_BTN_RSTICK", K_JOY_BTN_RSTICK, NULL},
{"JOY_BTN_LSHOULDER", K_JOY_BTN_LSHOULDER, NULL},
{"JOY_BTN_RSHOULDER", K_JOY_BTN_RSHOULDER, NULL},
{"JOY_DPAD_UP", K_JOY_DPAD_UP, NULL},
{"JOY_DPAD_DOWN", K_JOY_DPAD_DOWN, NULL},
{"JOY_DPAD_LEFT", K_JOY_DPAD_LEFT, NULL},
{"JOY_DPAD_RIGHT", K_JOY_DPAD_RIGHT, NULL},
{"JOY_BTN_MISC1", K_JOY_BTN_MISC1, NULL},
{"JOY_BTN_RPADDLE1", K_JOY_BTN_RPADDLE1, NULL},
{"JOY_BTN_LPADDLE1", K_JOY_BTN_LPADDLE1, NULL},
@ -141,11 +145,6 @@ static const keyname_t keynames[] =
{"JOY_TRIGGER1", K_JOY_TRIGGER1, NULL},
{"JOY_TRIGGER2", K_JOY_TRIGGER2, NULL},
{"JOY_DPAD_UP", K_JOY_DPAD_UP, NULL},
{"JOY_DPAD_DOWN", K_JOY_DPAD_DOWN, NULL},
{"JOY_DPAD_LEFT", K_JOY_DPAD_LEFT, NULL},
{"JOY_DPAD_RIGHT", K_JOY_DPAD_RIGHT, NULL},
{"AUX1", K_AUX1, "#str_07094"},
{"AUX2", K_AUX2, "#str_07095"},
{"AUX3", K_AUX3, "#str_07096"},

View file

@ -147,6 +147,7 @@ typedef enum {
K_JOY_BTN_EAST, // right face button, like Xbox B
K_JOY_BTN_WEST, // left face button, like Xbox X
K_JOY_BTN_NORTH, // top face button, like Xbox Y
K_JOY_BTN_BACK,
K_JOY_BTN_GUIDE, // Note: this one should probably not be used?
K_JOY_BTN_START,
@ -154,7 +155,12 @@ typedef enum {
K_JOY_BTN_RSTICK, // press right stick
K_JOY_BTN_LSHOULDER,
K_JOY_BTN_RSHOULDER,
// NOTE: in SDL3, the 4 DPAD buttons would be following, we have those later
K_JOY_DPAD_UP,
K_JOY_DPAD_DOWN,
K_JOY_DPAD_LEFT,
K_JOY_DPAD_RIGHT,
K_JOY_BTN_MISC1, // Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button)
K_JOY_BTN_RPADDLE1, // Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1)
K_JOY_BTN_LPADDLE1, // Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3)
@ -174,11 +180,7 @@ typedef enum {
K_JOY_TRIGGER1,
K_JOY_TRIGGER2,
K_JOY_DPAD_UP,
K_JOY_DPAD_DOWN,
K_JOY_DPAD_LEFT,
K_JOY_DPAD_RIGHT,
K_LAST_JOY = K_JOY_DPAD_RIGHT,
K_LAST_JOY = K_JOY_TRIGGER2,
K_GRAVE_A = 229, // lowercase a with grave accent FIXME: used to be 224; this probably isn't used anyway

View file

@ -428,8 +428,6 @@ idCVar idUsercmdGenLocal::m_smooth( "m_smooth", "1", CVAR_SYSTEM | CVAR_ARCHIVE
idCVar idUsercmdGenLocal::m_strafeSmooth( "m_strafeSmooth", "4", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "number of samples blended for mouse moving", 1, 8, idCmdSystem::ArgCompletion_Integer<1,8> );
idCVar idUsercmdGenLocal::m_showMouseRate( "m_showMouseRate", "0", CVAR_SYSTEM | CVAR_BOOL, "shows mouse movement" );
idCVar joy_mergedThreshold( "joy_mergedThreshold", "1", CVAR_BOOL | CVAR_ARCHIVE, "If the thresholds aren't merged, you drift more off center" );
idCVar joy_newCode( "joy_newCode", "0", CVAR_BOOL | CVAR_ARCHIVE, "Use the new codepath" );
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_range( "joy_range", "1.0", CVAR_FLOAT | CVAR_ARCHIVE, "allow full range to be mapped to a smaller offset" );
@ -437,14 +435,15 @@ idCVar joy_gammaLook( "joy_gammaLook", "1", CVAR_INTEGER | CVAR_ARCHIVE, "use a
idCVar joy_powerScale( "joy_powerScale", "2", CVAR_FLOAT | CVAR_ARCHIVE, "Raise joystick values to this power" );
idCVar joy_pitchSpeed( "joy_pitchSpeed", "130", CVAR_ARCHIVE | CVAR_FLOAT, "pitch speed when pressing up or down on the joystick", 60, 600 );
idCVar joy_yawSpeed( "joy_yawSpeed", "240", CVAR_ARCHIVE | CVAR_FLOAT, "pitch speed when pressing left or right on the joystick", 60, 600 );
idCVar joy_invertLook( "joy_invertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
// these were a bad idea!
idCVar joy_dampenLook( "joy_dampenLook", "1", CVAR_BOOL | CVAR_ARCHIVE, "Do not allow full acceleration on look" );
idCVar joy_deltaPerMSLook( "joy_deltaPerMSLook", "0.003", CVAR_FLOAT | CVAR_ARCHIVE, "Max amount to be added on look per MS" );
idCVar in_useJoystick( "in_useJoystick", "1", CVAR_ARCHIVE | CVAR_BOOL, "enables/disables the gamepad for PC use" );
idCVar in_invertLook( "in_invertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
idCVar in_mouseInvertLook( "in_mouseInvertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
idCVar in_useGamepad( "in_useGamepad", "1", CVAR_ARCHIVE | CVAR_BOOL, "enables/disables the gamepad for PC use" );
// TODO idCVar in_mouseInvertLook( "in_mouseInvertLook", "0", CVAR_ARCHIVE | CVAR_BOOL, "inverts the look controls so the forward looks up (flight controls) - the proper way to play games!" );
static idUsercmdGenLocal localUsercmdGen;
idUsercmdGen *usercmdGen = &localUsercmdGen;
@ -838,7 +837,7 @@ void idUsercmdGenLocal::HandleJoystickAxis( int keyNum, float unclampedValue, fl
lastLookValuePitch = lookValue;
}
float invertPitch = in_invertLook.GetBool() ? -1.0f : 1.0f;
float invertPitch = joy_invertLook.GetBool() ? -1.0f : 1.0f;
viewangles[PITCH] -= MS2SEC( pollTime - lastPollTime ) * lookValue * joy_pitchSpeed.GetFloat() * invertPitch;
break;
}
@ -848,7 +847,7 @@ void idUsercmdGenLocal::HandleJoystickAxis( int keyNum, float unclampedValue, fl
lastLookValuePitch = lookValue;
}
float invertPitch = in_invertLook.GetBool() ? -1.0f : 1.0f;
float invertPitch = joy_invertLook.GetBool() ? -1.0f : 1.0f;
viewangles[PITCH] += MS2SEC( pollTime - lastPollTime ) * lookValue * joy_pitchSpeed.GetFloat() * invertPitch;
break;
}
@ -1259,9 +1258,6 @@ void idUsercmdGenLocal::Joystick( void ) {
Key( joyButton, ( value != 0 ) );
} else if ( ( action >= J_AXIS_MIN ) && ( action <= J_AXIS_MAX ) ) {
joystickAxis[ action - J_AXIS_MIN ] = static_cast<float>( value ) / 32767.0f;
} else if ( action >= J_DPAD_UP && action <= J_DPAD_RIGHT ) {
int joyButton = K_JOY_DPAD_UP + ( action - J_DPAD_UP );
Key( joyButton, ( value != 0 ) );
} else {
//assert( !"Unknown joystick event" );
}
@ -1294,7 +1290,7 @@ void idUsercmdGenLocal::UsercmdInterrupt( void ) {
Keyboard();
// process the system joystick events
if ( in_useJoystick.GetBool() ) {
if ( in_useGamepad.GetBool() ) {
Joystick();
}
@ -1341,7 +1337,7 @@ usercmd_t idUsercmdGenLocal::GetDirectUsercmd( void ) {
Keyboard();
// process the system joystick events
if ( in_useJoystick.GetBool() ) {
if ( in_useGamepad.GetBool() ) {
Joystick();
}
// create the usercmd

View file

@ -60,6 +60,8 @@ If you have questions concerning this license or the applicable additional terms
#define SDLK_PRINTSCREEN SDLK_PRINT
#endif
extern idCVar in_useGamepad; // from UsercmdGen.cpp
// NOTE: g++-4.7 doesn't like when this is static (for idCmdSystem::ArgCompletion_String<kbdNames>)
const char *_in_kbdNames[] = {
#if SDL_VERSION_ATLEAST(2, 0, 0) // auto-detection is only available for SDL2
@ -125,7 +127,7 @@ static idList<mouse_poll_t> mouse_polls;
static idList<joystick_poll_t> joystick_polls;
static bool buttonStates[K_LAST_KEY];
static int joyAxis[MAX_JOYSTICK_AXIS];
static float joyAxis[MAX_JOYSTICK_AXIS];
static idList<sysEvent_t> event_overflow;
@ -273,7 +275,7 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
if (key <= K_JOY_BTN_NORTH) {
#if SDL_VERSION_ATLEAST(3, 0, 0)
SDL_GamepadButton gpbtn = SDL_GAMEPAD_BUTTON_SOUTH + (key - K_JOY_BTN_NORTH);
SDL_GamepadButton gpbtn = SDL_GAMEPAD_BUTTON_SOUTH + (key - K_JOY_BTN_SOUTH);
SDL_GamepadButtonLabel label = SDL_GetGamepadButtonLabeForTypel(TODO, gpbtn);
switch(label) {
case SDL_GAMEPAD_BUTTON_LABEL_A:
@ -310,7 +312,6 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
}
// the labels for the remaining keys are the same for SDL2 and SDL3 (and all controllers)
// Note: Would be nicer with "Pad " at the beginning, but then it's too long for the keybinding window :-/
switch(key) {
case K_JOY_BTN_BACK:
return "Pad Back";
@ -328,7 +329,16 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
return "Pad LShoulder";
case K_JOY_BTN_RSHOULDER:
return "Pad RShoulder";
// NOTE: in SDL3, the 4 DPAD buttons would be following, we have those later
case K_JOY_DPAD_UP:
return "DPad Up";
case K_JOY_DPAD_DOWN:
return "DPad Down";
case K_JOY_DPAD_LEFT:
return "DPad Left";
case K_JOY_DPAD_RIGHT:
return "DPad Right";
case K_JOY_BTN_MISC1:
return "Pad Misc";
case K_JOY_BTN_RPADDLE1:
@ -340,6 +350,8 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
case K_JOY_BTN_LPADDLE2:
return "Pad P4";
// Note: Would be nicer with "Pad " (or even "Gamepad ") at the beginning,
// but then it's too long for the keybinding window :-/
case K_JOY_STICK1_UP:
return "Stick1 Up";
case K_JOY_STICK1_DOWN:
@ -363,16 +375,8 @@ const char* Sys_GetLocalizedJoyKeyName( int key ) {
case K_JOY_TRIGGER2:
return "Trigger 2";
case K_JOY_DPAD_UP:
return "DPad Up";
case K_JOY_DPAD_DOWN:
return "DPad Down";
case K_JOY_DPAD_LEFT:
return "DPad Left";
case K_JOY_DPAD_RIGHT:
return "DPad Right";
default:
assert(0 && "missing a case in Sys_GetLocalizedJoyKeyName() for axes or dpad!");
assert(0 && "missing a case in Sys_GetLocalizedJoyKeyName()!");
}
}
#endif // SDL2+
@ -1210,16 +1214,26 @@ sysEvent_t Sys_GetEvent() {
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
{
if ( !in_useGamepad.GetBool() ) {
common->Warning( "Gamepad support is disabled! Set the in_useGamepad CVar to 1 to enable it!\n" );
continue;
}
// 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;
}
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_BTN_SOUTH ) && ( jEvent <= J_ACTION_MAX ) ) {
res.evValue = K_JOY_BTN_SOUTH + ( jEvent - J_BTN_SOUTH );
return res;
} else if ( ( jEvent >= J_DPAD_UP ) && ( jEvent <= J_DPAD_RIGHT ) ) {
res.evValue = K_JOY_DPAD_UP + ( jEvent - J_DPAD_UP );
if ( ( jEvent >= J_ACTION_FIRST ) && ( jEvent <= J_ACTION_MAX ) ) {
res.evValue = K_FIRST_JOY + ( jEvent - J_ACTION_FIRST );
return res;
}
@ -1230,6 +1244,12 @@ sysEvent_t Sys_GetEvent() {
{
const int range = 16384;
if ( !in_useGamepad.GetBool() ) {
// not printing a message here, I guess we get lots of spurious axis events..
// TODO: or print a message if value is big enough?
continue;
}
sys_jEvents jEvent = mapjoyaxis( (SDL_GameControllerAxis)ev.caxis.axis);
joystick_polls.Append(joystick_poll_t( jEvent, ev.caxis.value) );
@ -1251,15 +1271,18 @@ sysEvent_t Sys_GetEvent() {
PushButton( K_JOY_TRIGGER2, ( ev.caxis.value > range ) );
}
if ( jEvent >= J_AXIS_MIN && jEvent <= J_AXIS_MAX ) {
// 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;
int percent = ( ev.caxis.value * 16 ) / range;
if ( joyAxis[axis] != percent ) {
joyAxis[axis] = percent;
res.evType = SE_JOYSTICK;
res.evValue = axis;
res.evValue2 = percent;
return res;
float val = ev.caxis.value * (1.25f / 32767.0f);
// 25% deadzone
if( val < 0.0f ) {
val = fminf(val + 0.25f, 0.0f);
} else {
val = fmaxf(val - 0.25f, 0.0f);
}
joyAxis[axis] = val;
}
continue; // try to get a decent event.
@ -1300,6 +1323,28 @@ sysEvent_t Sys_GetEvent() {
}
}
// first return joyaxis events, if gamepad is enabled and, and 16ms are over
// (or we haven't returned the values for all axis yet)
if ( in_useGamepad.GetBool() ) {
static unsigned int lastMS = 0;
static int joyAxisToSend = 0;
unsigned int nowMS = Sys_Milliseconds();
if ( nowMS - lastMS >= 16 ) {
int val = joyAxis[joyAxisToSend] * 100; // float to percent
res.evType = SE_JOYSTICK;
res.evValue = joyAxisToSend;
res.evValue2 = val;
++joyAxisToSend;
if(joyAxisToSend == MAX_JOYSTICK_AXIS) {
// we're done for this frame, so update lastMS and reset joyAxisToSend
joyAxisToSend = 0;
lastMS = nowMS;
}
return res;
}
}
return res_none;
}

View file

@ -91,7 +91,12 @@ typedef enum {
J_BTN_RSTICK, // press right stick
J_BTN_LSHOULDER,
J_BTN_RSHOULDER,
// NOTE: in SDL3, the 4 DPAD buttons would be following, we have those at the end
J_DPAD_UP,
J_DPAD_DOWN,
J_DPAD_LEFT,
J_DPAD_RIGHT,
J_BTN_MISC1, // Additional button (e.g. Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button, Amazon Luna microphone button)
J_BTN_RPADDLE1, // Upper or primary paddle, under your right hand (e.g. Xbox Elite paddle P1)
J_BTN_LPADDLE1, // Upper or primary paddle, under your left hand (e.g. Xbox Elite paddle P3)
@ -111,18 +116,13 @@ typedef enum {
J_AXIS_MAX = J_AXIS_MIN + MAX_JOYSTICK_AXIS - 1,
J_DPAD_UP,
J_DPAD_DOWN,
J_DPAD_LEFT,
J_DPAD_RIGHT,
MAX_JOY_EVENT
} sys_jEvents;
struct sysEvent_t {
sysEventType_t evType;
int evValue;
int evValue2;
int evValue; // for keys: K_* or ASCII code; for joystick: axis; for mouse: mouseX
int evValue2; // for keys: 0/1 for up/down; for axis: value; for mouse: mouseY
int evPtrLength; // bytes of data pointed to by evPtr, for journaling
void * evPtr; // this must be manually freed if not NULL
};

View file

@ -344,6 +344,9 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim
return ret;
}
// DG: used to turn gamepad A into left mouse click
sysEvent_t fakedEvent = {};
if ( event->evType == SE_MOUSE || event->evType == SE_MOUSE_ABS ) {
if ( !desktop || (desktop->GetFlags() & WIN_MENUGUI) ) {
// DG: this is a fullscreen GUI, scale the mousedelta added to cursorX/Y
@ -401,6 +404,34 @@ const char *idUserInterfaceLocal::HandleEvent( const sysEvent_t *event, int _tim
cursorY = 0;
}
}
else if ( event->evType == SE_JOYSTICK && event->evValue2 != 0 )
{
// evValue: axis = jEvent - J_AXIS_MIN;
// evValue2: percent (-100 to 100)
// currently uses both sticks for cursor movement
// TODO could use one stick for scrolling (maybe by generating K_UPARROW/DOWNARROW events?)
float addVal = event->evValue2 * 0.1f;
if( event->evValue == 0 || event->evValue == 2 ) {
cursorX += addVal;
} else if( event->evValue == 1 || event->evValue == 3 ) {
cursorY += addVal;
}
if (cursorX < 0) {
cursorX = 0;
}
if (cursorY < 0) {
cursorY = 0;
}
}
else if( event->evType == SE_KEY && (event->evValue == K_JOY_BTN_SOUTH || event->evValue == K_JOY_BTN_EAST) )
{
// map gamepad buttons south/east (A/B on xbox controller) to mouse1/2
fakedEvent = *event;
fakedEvent.evValue = (event->evValue == K_JOY_BTN_SOUTH) ? K_MOUSE1 : K_MOUSE2;
event = &fakedEvent;
}
if ( desktop ) {
return desktop->HandleEvent( event, updateVisuals );

View file

@ -736,7 +736,7 @@ const char *idWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals)
*updateVisuals = true;
}
if (event->evValue == K_MOUSE1 || event->evValue == K_JOY_BTN_EAST) {
if (event->evValue == K_MOUSE1) {
if (!event->evValue2 && GetCaptureChild()) {
GetCaptureChild()->LoseCapture();
@ -785,7 +785,7 @@ const char *idWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals)
} else if (!actionUpRun) {
actionUpRun = RunScript( ON_ACTIONRELEASE );
}
} else if (event->evValue == K_MOUSE2 || event->evValue == K_JOY_BTN_SOUTH) {
} else if (event->evValue == K_MOUSE2) {
if (!event->evValue2 && GetCaptureChild()) {
GetCaptureChild()->LoseCapture();