summaryrefslogtreecommitdiff
path: root/Source/Core/InputCommon/ControllerInterface/SDL
diff options
context:
space:
mode:
authorJun Bo Bi <jambonmcyeah@gmail.com>2021-08-06 11:10:44 -0400
committerShawn Hoffman <godisgovernment@gmail.com>2022-07-10 14:49:49 -0700
commit6cb936d0cf1a57fa461f097473b5b0b5ccaf09a1 (patch)
treed971fbeca0e9bbaa3ab2567e8bbd16a9795baf59 /Source/Core/InputCommon/ControllerInterface/SDL
parent38cb76dea54b59fa0eae458e0898edebed13e7ca (diff)
Add SDL motion input and rumble support
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface/SDL')
-rw-r--r--Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp144
-rw-r--r--Source/Core/InputCommon/ControllerInterface/SDL/SDL.h50
2 files changed, 149 insertions, 45 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
index a2e6893801..538e65fadf 100644
--- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
+++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp
@@ -56,8 +56,9 @@ static bool HandleEventAndContinue(const SDL_Event& e)
else if (e.type == SDL_JOYDEVICEREMOVED)
{
g_controller_interface.RemoveDevice([&e](const auto* device) {
- const Joystick* joystick = dynamic_cast<const Joystick*>(device);
- return joystick && SDL_JoystickInstanceID(joystick->GetSDLJoystick()) == e.jdevice.which;
+ return device->GetSource() == "SDL" &&
+ SDL_JoystickInstanceID(static_cast<const Joystick*>(device)->GetSDLJoystick()) ==
+ e.jdevice.which;
});
}
else if (e.type == s_populate_event_type)
@@ -79,7 +80,7 @@ static bool HandleEventAndContinue(const SDL_Event& e)
void Init()
{
#if !SDL_VERSION_ATLEAST(2, 0, 0)
- if (SDL_Init(SDL_INIT_JOYSTICK) != 0)
+ if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0)
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to initialize");
return;
#else
@@ -88,16 +89,20 @@ void Init()
SDL_InitSubSystem(SDL_INIT_JOYSTICK);
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
#endif
+
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+ SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
+#endif
+
s_hotplug_thread = std::thread([] {
Common::ScopeGuard quit_guard([] {
// TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up
SDL_Quit();
});
-
{
Common::ScopeGuard init_guard([] { s_init_event.Set(); });
- if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) != 0)
+ if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER) != 0)
{
ERROR_LOG_FMT(CONTROLLERINTERFACE, "SDL failed to initialize");
return;
@@ -172,25 +177,6 @@ void PopulateDevices()
Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index)
: m_joystick(joystick), m_name(StripSpaces(GetJoystickName(sdl_index)))
{
-// really bad HACKS:
-// to not use SDL for an XInput device
-// too many people on the forums pick the SDL device and ask:
-// "why don't my 360 gamepad triggers/rumble work correctly"
-#ifdef _WIN32
- // checking the name is probably good (and hacky) enough
- // but I'll double check with the num of buttons/axes
- std::string lcasename = GetName();
- Common::ToLower(&lcasename);
-
- if ((std::string::npos != lcasename.find("xbox 360")) &&
- (10 == SDL_JoystickNumButtons(joystick)) && (5 == SDL_JoystickNumAxes(joystick)) &&
- (1 == SDL_JoystickNumHats(joystick)) && (0 == SDL_JoystickNumBalls(joystick)))
- {
- // this device won't be used
- return;
- }
-#endif
-
if (SDL_JoystickNumButtons(joystick) > 255 || SDL_JoystickNumAxes(joystick) > 255 ||
SDL_JoystickNumHats(joystick) > 255 || SDL_JoystickNumBalls(joystick) > 255)
{
@@ -219,38 +205,78 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index)
AddAnalogInputs(new Axis(i, m_joystick, -32768), new Axis(i, m_joystick, 32767));
}
+ m_haptic = nullptr;
+
#ifdef USE_SDL_HAPTIC
m_haptic = SDL_HapticOpenFromJoystick(m_joystick);
- if (!m_haptic)
- return;
+ if (m_haptic)
+ {
+ const unsigned int supported_effects = SDL_HapticQuery(m_haptic);
- const unsigned int supported_effects = SDL_HapticQuery(m_haptic);
+ // Disable autocenter:
+ if (supported_effects & SDL_HAPTIC_AUTOCENTER)
+ SDL_HapticSetAutocenter(m_haptic, 0);
- // Disable autocenter:
- if (supported_effects & SDL_HAPTIC_AUTOCENTER)
- SDL_HapticSetAutocenter(m_haptic, 0);
+ // Constant
+ if (supported_effects & SDL_HAPTIC_CONSTANT)
+ AddOutput(new ConstantEffect(m_haptic));
- // Constant
- if (supported_effects & SDL_HAPTIC_CONSTANT)
- AddOutput(new ConstantEffect(m_haptic));
+ // Ramp
+ if (supported_effects & SDL_HAPTIC_RAMP)
+ AddOutput(new RampEffect(m_haptic));
+
+ // Periodic
+ for (auto waveform :
+ {SDL_HAPTIC_SINE, SDL_HAPTIC_TRIANGLE, SDL_HAPTIC_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHDOWN})
+ {
+ if (supported_effects & waveform)
+ AddOutput(new PeriodicEffect(m_haptic, waveform));
+ }
- // Ramp
- if (supported_effects & SDL_HAPTIC_RAMP)
- AddOutput(new RampEffect(m_haptic));
+ // LeftRight
+ if (supported_effects & SDL_HAPTIC_LEFTRIGHT)
+ {
+ AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Strong));
+ AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Weak));
+ }
+ }
+#endif
- // Periodic
- for (auto waveform :
- {SDL_HAPTIC_SINE, SDL_HAPTIC_TRIANGLE, SDL_HAPTIC_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHDOWN})
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+ if (!m_haptic)
{
- if (supported_effects & waveform)
- AddOutput(new PeriodicEffect(m_haptic, waveform));
+ AddOutput(new Motor(m_joystick));
}
+#endif
- // LeftRight
- if (supported_effects & SDL_HAPTIC_LEFTRIGHT)
+#ifdef USE_SDL_GAMECONTROLLER
+ m_controller = SDL_GameControllerOpen(sdl_index);
+
+ if (m_controller)
{
- AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Strong));
- AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Weak));
+ if (SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_ACCEL, SDL_TRUE) == 0)
+ {
+ AddInput(new MotionInput("Accel Up", m_controller, SDL_SENSOR_ACCEL, 1, 1));
+ AddInput(new MotionInput("Accel Down", m_controller, SDL_SENSOR_ACCEL, 1, -1));
+
+ AddInput(new MotionInput("Accel Left", m_controller, SDL_SENSOR_ACCEL, 0, -1));
+ AddInput(new MotionInput("Accel Right", m_controller, SDL_SENSOR_ACCEL, 0, 1));
+
+ AddInput(new MotionInput("Accel Forward", m_controller, SDL_SENSOR_ACCEL, 2, -1));
+ AddInput(new MotionInput("Accel Backward", m_controller, SDL_SENSOR_ACCEL, 2, 1));
+ }
+
+ if (SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_GYRO, SDL_TRUE) == 0)
+ {
+ AddInput(new MotionInput("Gyro Pitch Up", m_controller, SDL_SENSOR_GYRO, 0, 1));
+ AddInput(new MotionInput("Gyro Pitch Down", m_controller, SDL_SENSOR_GYRO, 0, -1));
+
+ AddInput(new MotionInput("Gyro Roll Left", m_controller, SDL_SENSOR_GYRO, 2, 1));
+ AddInput(new MotionInput("Gyro Roll Right", m_controller, SDL_SENSOR_GYRO, 2, -1));
+
+ AddInput(new MotionInput("Gyro Yaw Left", m_controller, SDL_SENSOR_GYRO, 1, 1));
+ AddInput(new MotionInput("Gyro Yaw Right", m_controller, SDL_SENSOR_GYRO, 1, -1));
+ }
}
#endif
}
@@ -267,6 +293,11 @@ Joystick::~Joystick()
}
#endif
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+ // stop all rumble
+ SDL_JoystickRumble(m_joystick, 0, 0, 0);
+#endif
+
// close joystick
SDL_JoystickClose(m_joystick);
}
@@ -442,6 +473,19 @@ bool Joystick::LeftRightEffect::UpdateParameters(s16 value)
}
#endif
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+std::string Joystick::Motor::GetName() const
+{
+ return "Motor";
+}
+
+void Joystick::Motor::SetState(ControlState state)
+{
+ Uint16 rumble = state * std::numeric_limits<Uint16>::max();
+ SDL_JoystickRumble(m_js, rumble, rumble, std::numeric_limits<Uint32>::max());
+}
+#endif
+
void Joystick::UpdateInput()
{
// TODO: Don't call this for every Joystick, only once per ControllerInterface::UpdateInput()
@@ -492,4 +536,14 @@ ControlState Joystick::Hat::GetState() const
{
return (SDL_JoystickGetHat(m_js, m_index) & (1 << m_direction)) > 0;
}
+#ifdef USE_SDL_GAMECONTROLLER
+
+ControlState Joystick::MotionInput::GetState() const
+{
+ std::array<float, 3> data{};
+ SDL_GameControllerGetSensorData(m_gc, m_type, data.data(), (int)data.size());
+ return m_scale * data[m_index];
+}
+
+#endif
} // namespace ciface::SDL
diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h
index 7fbf7cf23e..7bf7565d84 100644
--- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h
+++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.h
@@ -5,14 +5,24 @@
#include <SDL.h>
+#define INIT_FLAGS SDL_INIT_JOYSTICK
+
#if SDL_VERSION_ATLEAST(1, 3, 0)
#define USE_SDL_HAPTIC
#endif
+#if SDL_VERSION_ATLEAST(2, 0, 14)
+#define USE_SDL_GAMECONTROLLER
+#endif
+
#ifdef USE_SDL_HAPTIC
#include <SDL_haptic.h>
#endif
+#ifdef USE_SDL_GAMECONTROLLER
+#include <SDL_gamecontroller.h>
+#endif
+
#include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ciface::SDL
@@ -137,6 +147,42 @@ private:
};
#endif
+#if SDL_VERSION_ATLEAST(2, 0, 9)
+ class Motor : public Output
+ {
+ public:
+ explicit Motor(SDL_Joystick* js) : m_js(js){};
+ std::string GetName() const override;
+ void SetState(ControlState state) override;
+
+ private:
+ SDL_Joystick* const m_js;
+ };
+#endif
+
+#ifdef USE_SDL_GAMECONTROLLER
+ class MotionInput : public Input
+ {
+ public:
+ MotionInput(const char* name, SDL_GameController* gc, SDL_SensorType type, int index,
+ ControlState scale)
+ : m_name(name), m_gc(gc), m_type(type), m_index(index), m_scale(scale){};
+
+ std::string GetName() const override { return m_name; };
+ bool IsDetectable() const override { return false; };
+ ControlState GetState() const override;
+
+ private:
+ const char* m_name;
+
+ SDL_GameController* const m_gc;
+ SDL_SensorType const m_type;
+ int const m_index;
+
+ ControlState const m_scale;
+ };
+#endif
+
public:
void UpdateInput() override;
@@ -154,5 +200,9 @@ private:
#ifdef USE_SDL_HAPTIC
SDL_Haptic* m_haptic;
#endif
+
+#ifdef USE_SDL_GAMECONTROLLER
+ SDL_GameController* m_controller;
+#endif
};
} // namespace ciface::SDL