From d75d3aacf937753e9afb4c5b5356324747d519cc Mon Sep 17 00:00:00 2001 From: Marcus Minhorst Date: Sun, 29 Jun 2025 18:32:21 -0400 Subject: [PATCH] SDL2 GameController API utilized --- src/common/console/c_bind.cpp | 4 +- src/common/console/keydef.h | 9 +- src/common/platform/posix/sdl/i_input.cpp | 42 ++- src/common/platform/posix/sdl/i_joystick.cpp | 358 ++++++++++++------- 4 files changed, 275 insertions(+), 138 deletions(-) diff --git a/src/common/console/c_bind.cpp b/src/common/console/c_bind.cpp index fe794c736..078a9a2b1 100644 --- a/src/common/console/c_bind.cpp +++ b/src/common/console/c_bind.cpp @@ -147,7 +147,9 @@ const char *KeyNames[NUM_KEYS] = "DPadUp","DPadDown","DPadLeft","DPadRight", // Gamepad buttons "Pad_Start","Pad_Back","LThumb","RThumb", "LShoulder","RShoulder","LTrigger","RTrigger", - "Pad_A", "Pad_B", "Pad_X", "Pad_Y" + "Pad_A", "Pad_B", "Pad_X", "Pad_Y", + "Paddle_1", "Paddle_2", "Paddle_3", "Paddle_4", + "Guide", "Pad_Misc", "Pad_Touchpad" }; FKeyBindings Bindings; diff --git a/src/common/console/keydef.h b/src/common/console/keydef.h index 108806774..620b5f5e7 100644 --- a/src/common/console/keydef.h +++ b/src/common/console/keydef.h @@ -134,8 +134,15 @@ enum EKeyCodes KEY_PAD_B = 0x1C1, KEY_PAD_X = 0x1C2, KEY_PAD_Y = 0x1C3, + KEY_PAD_PADDLE1 = 0x1C4, + KEY_PAD_PADDLE2 = 0x1C5, + KEY_PAD_PADDLE3 = 0x1C6, + KEY_PAD_PADDLE4 = 0x1C7, + KEY_PAD_GUIDE = 0x1C8, + KEY_PAD_MISC1 = 0x1C9, + KEY_PAD_TOUCHPAD = 0x1CA, - NUM_KEYS = 0x1C4, + NUM_KEYS = 0x1CB, NUM_JOYAXISBUTTONS = 8, }; diff --git a/src/common/platform/posix/sdl/i_input.cpp b/src/common/platform/posix/sdl/i_input.cpp index e48160c79..d9c05a666 100644 --- a/src/common/platform/posix/sdl/i_input.cpp +++ b/src/common/platform/posix/sdl/i_input.cpp @@ -31,6 +31,9 @@ ** */ #include +#include +#include "c_cvars.h" +#include "dobject.h" #include "m_argv.h" #include "m_joy.h" #include "v_video.h" @@ -56,7 +59,6 @@ static bool NativeMouse = true; CVAR (Bool, use_mouse, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) - extern int WaitingForKey; static const SDL_Keycode DIKToKeySym[256] = @@ -527,14 +529,52 @@ void MessagePump (const SDL_Event &sev) case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: + if (SDL_IsGameController(sev.jdevice.which)) + break; // let SDL_CONTROLLERBUTTON* handle this event.type = sev.type == SDL_JOYBUTTONDOWN ? EV_KeyDown : EV_KeyUp; event.data1 = KEY_FIRSTJOYBUTTON + sev.jbutton.button; if(event.data1 != 0) D_PostEvent(&event); break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + event.type = sev.type == SDL_CONTROLLERBUTTONDOWN ? EV_KeyDown : EV_KeyUp; + switch (sev.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_A: event.data1 = KEY_PAD_A; break; + case SDL_CONTROLLER_BUTTON_B: event.data1 = KEY_PAD_B; break; + case SDL_CONTROLLER_BUTTON_X: event.data1 = KEY_PAD_X; break; + case SDL_CONTROLLER_BUTTON_Y: event.data1 = KEY_PAD_Y; break; + case SDL_CONTROLLER_BUTTON_BACK: event.data1 = KEY_PAD_BACK; break; + case SDL_CONTROLLER_BUTTON_GUIDE: event.data1 = KEY_PAD_GUIDE; break; + case SDL_CONTROLLER_BUTTON_START: event.data1 = KEY_PAD_START; break; + case SDL_CONTROLLER_BUTTON_LEFTSTICK: event.data1 = KEY_PAD_LTHUMB; break; + case SDL_CONTROLLER_BUTTON_RIGHTSTICK: event.data1 = KEY_PAD_RTHUMB; break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: event.data1 = KEY_PAD_LSHOULDER; break; + case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: event.data1 = KEY_PAD_RSHOULDER; break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: event.data1 = KEY_PAD_DPAD_UP; break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: event.data1 = KEY_PAD_DPAD_DOWN; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: event.data1 = KEY_PAD_DPAD_LEFT; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: event.data1 = KEY_PAD_DPAD_RIGHT; break; + case SDL_CONTROLLER_BUTTON_MISC1: event.data1 = KEY_PAD_MISC1; break; + case SDL_CONTROLLER_BUTTON_PADDLE1: event.data1 = KEY_PAD_PADDLE1; break; + case SDL_CONTROLLER_BUTTON_PADDLE2: event.data1 = KEY_PAD_PADDLE2; break; + case SDL_CONTROLLER_BUTTON_PADDLE3: event.data1 = KEY_PAD_PADDLE3; break; + case SDL_CONTROLLER_BUTTON_PADDLE4: event.data1 = KEY_PAD_PADDLE4; break; + case SDL_CONTROLLER_BUTTON_TOUCHPAD: event.data1 = KEY_PAD_TOUCHPAD; break; + default: event.data1 = 0; + } + if(event.data1 != 0) + D_PostEvent(&event); + break; + case SDL_JOYDEVICEADDED: case SDL_JOYDEVICEREMOVED: + case SDL_CONTROLLERDEVICEADDED: + case SDL_CONTROLLERDEVICEREMOVED: + if ((sev.type == SDL_JOYDEVICEADDED || sev.type == SDL_JOYDEVICEREMOVED) && SDL_IsGameController(sev.jdevice.which)) + break; // skip double event I_UpdateDeviceList(); event.type = EV_DeviceChange; D_PostEvent (&event); diff --git a/src/common/platform/posix/sdl/i_joystick.cpp b/src/common/platform/posix/sdl/i_joystick.cpp index 8dc9fd8ac..b276f0fb4 100644 --- a/src/common/platform/posix/sdl/i_joystick.cpp +++ b/src/common/platform/posix/sdl/i_joystick.cpp @@ -31,10 +31,12 @@ ** */ #include +#include #include "basics.h" #include "cmdlib.h" +#include "d_eventbase.h" #include "m_joy.h" #include "keydef.h" @@ -48,30 +50,54 @@ class SDLInputJoystick: public IJoystickConfig public: SDLInputJoystick(int DeviceIndex) : DeviceIndex(DeviceIndex), Multiplier(1.0f) , Enabled(true) { - Device = SDL_JoystickOpen(DeviceIndex); - if(Device != NULL) + if (SDL_IsGameController(DeviceIndex)) { - NumAxes = SDL_JoystickNumAxes(Device); - NumHats = SDL_JoystickNumHats(Device); + Mapping = SDL_GameControllerOpen(DeviceIndex); - SetDefaultConfig(); + DefaultAxes = DefaultControllerAxes; + DefaultAxesCount = sizeof(DefaultControllerAxes) / sizeof(EJoyAxis); + + if(Mapping != NULL) + { + NumAxes = SDL_CONTROLLER_AXIS_MAX; + NumHats = 0; + + SetDefaultConfig(); + } + } + else + { + Device = SDL_JoystickOpen(DeviceIndex); + DefaultAxes = DefaultJoystickAxes; + DefaultAxesCount = sizeof(DefaultJoystickAxes) / sizeof(EJoyAxis); + + if(Device != NULL) + { + NumAxes = SDL_JoystickNumAxes(Device); + NumHats = SDL_JoystickNumHats(Device); + + SetDefaultConfig(); + } } } ~SDLInputJoystick() { - if(Device != NULL) + if(Device != NULL || Mapping != NULL) M_SaveJoystickConfig(this); + SDL_GameControllerClose(Mapping); SDL_JoystickClose(Device); } bool IsValid() const { - return Device != NULL; + return Device != NULL || Mapping != NULL; } FString GetName() { - return SDL_JoystickName(Device); + return (Mapping) + ? SDL_GameControllerName(Mapping) + : SDL_JoystickName(Device); } float GetSensitivity() { @@ -127,7 +153,7 @@ public: } bool IsAxisMapDefault(int axis) { - if(axis >= 5) + if(axis >= DefaultAxesCount) return Axes[axis].GameAxis == JOYAXIS_None; return Axes[axis].GameAxis == DefaultAxes[axis]; } @@ -141,18 +167,31 @@ public: for(int i = 0;i < GetNumAxes();i++) { AxisInfo info; - if(i < NumAxes) - info.Name.Format("Axis %d", i+1); - else - info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y'); + + if (Mapping) { + switch(i) { + case SDL_CONTROLLER_AXIS_LEFTX: info.Name = "Left Stick X"; break; + case SDL_CONTROLLER_AXIS_LEFTY: info.Name = "Left Stick Y"; break; + case SDL_CONTROLLER_AXIS_RIGHTX: info.Name = "Right Stick X"; break; + case SDL_CONTROLLER_AXIS_RIGHTY: info.Name = "Right Stick Y"; break; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: info.Name = "Left Trigger"; break; + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: info.Name = "Right Trigger"; break; + default: info.Name.Format("Axis %d", i+1); break; + } + } else { + if(i < NumAxes) + info.Name.Format("Axis %d", i+1); + else + info.Name.Format("Hat %d (%c)", (i-NumAxes)/2 + 1, (i-NumAxes)%2 == 0 ? 'x' : 'y'); + } + info.DeadZone = DEFAULT_DEADZONE; info.Multiplier = 1.0f; info.Value = 0.0; info.ButtonValue = 0; - if(i >= 5) - info.GameAxis = JOYAXIS_None; - else - info.GameAxis = DefaultAxes[i]; + + info.GameAxis = (i < DefaultAxesCount)? DefaultAxes[i]: JOYAXIS_None; + Axes.Push(info); } } @@ -161,7 +200,7 @@ public: { return Enabled; } - + void SetEnabled(bool enabled) { Enabled = enabled; @@ -188,9 +227,173 @@ public: } } - void ProcessInput() + void ProcessInput(); + +protected: + struct AxisInfo { - uint8_t buttonstate; + FString Name; + float DeadZone; + float Multiplier; + EJoyAxis GameAxis; + double Value; + uint8_t ButtonValue; + }; + static const EJoyAxis DefaultJoystickAxes[5]; + static const EJoyAxis DefaultControllerAxes[6]; + const EJoyAxis * DefaultAxes; + int DefaultAxesCount; + + int DeviceIndex; + SDL_Joystick *Device; + SDL_GameController *Mapping; + + float Multiplier; + bool Enabled; + TArray Axes; + int NumAxes; + int NumHats; + + friend class SDLInputJoystickManager; +}; + +// [Nash 4 Feb 2024] seems like on Linux, the third axis is actually the Left Trigger, resulting in the player uncontrollably looking upwards. +const EJoyAxis SDLInputJoystick::DefaultJoystickAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_None, JOYAXIS_Yaw, JOYAXIS_Pitch}; + +// Defaults if we have access to the Gamepad API for this device +const EJoyAxis SDLInputJoystick::DefaultControllerAxes[6] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_Yaw, JOYAXIS_Pitch, JOYAXIS_None, JOYAXIS_None}; + + + +class SDLInputJoystickManager +{ +public: + SDLInputJoystickManager() + { + this->UpdateDeviceList(); + } + + void UpdateDeviceList() + { + Joysticks.DeleteAndClear(); + for(int i = 0; i < SDL_NumJoysticks(); i++) + { + SDLInputJoystick *device = new SDLInputJoystick(i); + if(device->IsValid()) + Joysticks.Push(device); + else + delete device; + } + } + + void AddAxes(float axes[NUM_JOYAXIS]) + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + Joysticks[i]->AddAxes(axes); + } + + void GetDevices(TArray &sticks) + { + for(unsigned int i = 0;i < Joysticks.Size();i++) + { + M_LoadJoystickConfig(Joysticks[i]); + sticks.Push(Joysticks[i]); + } + } + + void ProcessInput() const + { + for(unsigned int i = 0;i < Joysticks.Size();++i) + if(Joysticks[i]->Enabled) Joysticks[i]->ProcessInput(); + } + +protected: + TDeletingArray Joysticks; +}; +static SDLInputJoystickManager *JoystickManager; + +void I_StartupJoysticks() +{ +#ifndef NO_SDL_JOYSTICK + if(SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) >= 0) + JoystickManager = new SDLInputJoystickManager(); +#endif +} +void I_ShutdownInput() +{ + if(JoystickManager) + { + delete JoystickManager; + SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); + } +} + +void I_GetJoysticks(TArray &sticks) +{ + sticks.Clear(); + + if (JoystickManager) + JoystickManager->GetDevices(sticks); +} + +void I_GetAxes(float axes[NUM_JOYAXIS]) +{ + for (int i = 0; i < NUM_JOYAXIS; ++i) + { + axes[i] = 0; + } + if (use_joystick && JoystickManager) + { + JoystickManager->AddAxes(axes); + } +} + +void I_ProcessJoysticks() +{ + if (use_joystick && JoystickManager) + JoystickManager->ProcessInput(); +} + +void PostKeyEvent(bool down, EKeyCodes which) +{ + event_t event = { 0,0,0,0,0,0,0 }; + event.type = down ? EV_KeyDown : EV_KeyUp; + event.data1 = which; + D_PostEvent(&event); +} + +void SDLInputJoystick::ProcessInput() { + uint8_t buttonstate; + + if (Mapping) + { + // GameController API available + + auto lastTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > MIN_DEADZONE; + auto lastTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > MIN_DEADZONE; + + for (auto i = 0; i < SDL_CONTROLLER_AXIS_MAX && i < NumAxes; ++i) + { + buttonstate = 0; + + Axes[i].Value = SDL_GameControllerGetAxis(Mapping, static_cast(i))/32767.0; + Axes[i].Value = Joy_RemoveDeadZone(Axes[i].Value, Axes[i].DeadZone, &buttonstate); + } + + auto currTriggerL = Axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT].Value > MIN_DEADZONE; + auto currTriggerR = Axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT].Value > MIN_DEADZONE; + + if (lastTriggerL != currTriggerL) PostKeyEvent(currTriggerL, KEY_PAD_LTRIGGER); + if (lastTriggerR != currTriggerR) PostKeyEvent(currTriggerR, KEY_PAD_RTRIGGER); + + buttonstate = Joy_XYAxesToButtons(Axes[0].Value, Axes[1].Value); + Joy_GenerateButtonEvents(Axes[0].ButtonValue, buttonstate, 4, KEY_JOYAXIS1PLUS); + Axes[0].ButtonValue = buttonstate; + + } + else + { + // Joystick API fallback for (int i = 0; i < NumAxes; ++i) { @@ -248,121 +451,6 @@ public: } } } - -protected: - struct AxisInfo - { - FString Name; - float DeadZone; - float Multiplier; - EJoyAxis GameAxis; - double Value; - uint8_t ButtonValue; - }; - static const EJoyAxis DefaultAxes[5]; - - int DeviceIndex; - SDL_Joystick *Device; - - float Multiplier; - bool Enabled; - TArray Axes; - int NumAxes; - int NumHats; - - friend class SDLInputJoystickManager; -}; - -// [Nash 4 Feb 2024] seems like on Linux, the third axis is actually the Left Trigger, resulting in the player uncontrollably looking upwards. -const EJoyAxis SDLInputJoystick::DefaultAxes[5] = {JOYAXIS_Side, JOYAXIS_Forward, JOYAXIS_None, JOYAXIS_Yaw, JOYAXIS_Pitch}; - -class SDLInputJoystickManager -{ -public: - SDLInputJoystickManager() - { - this->UpdateDeviceList(); - } - - void UpdateDeviceList() - { - Joysticks.DeleteAndClear(); - for(int i = 0; i < SDL_NumJoysticks(); i++) - { - SDLInputJoystick *device = new SDLInputJoystick(i); - if(device->IsValid()) - Joysticks.Push(device); - else - delete device; - } - } - - void AddAxes(float axes[NUM_JOYAXIS]) - { - for(unsigned int i = 0;i < Joysticks.Size();i++) - Joysticks[i]->AddAxes(axes); - } - - void GetDevices(TArray &sticks) - { - for(unsigned int i = 0;i < Joysticks.Size();i++) - { - M_LoadJoystickConfig(Joysticks[i]); - sticks.Push(Joysticks[i]); - } - } - - void ProcessInput() const - { - for(unsigned int i = 0;i < Joysticks.Size();++i) - if(Joysticks[i]->Enabled) Joysticks[i]->ProcessInput(); - } - -protected: - TDeletingArray Joysticks; -}; -static SDLInputJoystickManager *JoystickManager; - -void I_StartupJoysticks() -{ -#ifndef NO_SDL_JOYSTICK - if(SDL_InitSubSystem(SDL_INIT_JOYSTICK) >= 0) - JoystickManager = new SDLInputJoystickManager(); -#endif -} -void I_ShutdownInput() -{ - if(JoystickManager) - { - delete JoystickManager; - SDL_QuitSubSystem(SDL_INIT_JOYSTICK); - } -} - -void I_GetJoysticks(TArray &sticks) -{ - sticks.Clear(); - - if (JoystickManager) - JoystickManager->GetDevices(sticks); -} - -void I_GetAxes(float axes[NUM_JOYAXIS]) -{ - for (int i = 0; i < NUM_JOYAXIS; ++i) - { - axes[i] = 0; - } - if (use_joystick && JoystickManager) - { - JoystickManager->AddAxes(axes); - } -} - -void I_ProcessJoysticks() -{ - if (use_joystick && JoystickManager) - JoystickManager->ProcessInput(); } IJoystickConfig *I_UpdateDeviceList()