diff options
Diffstat (limited to 'Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp')
| -rw-r--r-- | Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp | 253 |
1 files changed, 220 insertions, 33 deletions
diff --git a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp index a2e6893801..6dec6003fd 100644 --- a/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp +++ b/Source/Core/InputCommon/ControllerInterface/SDL/SDL.cpp @@ -15,6 +15,8 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" #ifdef _WIN32 +#include <Windows.h> + #pragma comment(lib, "SDL2.lib") #endif @@ -56,8 +58,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) @@ -76,10 +79,77 @@ static bool HandleEventAndContinue(const SDL_Event& e) } #endif +static void EnableSDLLogging() +{ + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); + SDL_LogSetOutputFunction( + [](void*, int category, SDL_LogPriority priority, const char* message) { + std::string category_name; + switch (category) + { + case SDL_LOG_CATEGORY_APPLICATION: + category_name = "app"; + break; + case SDL_LOG_CATEGORY_ERROR: + category_name = "error"; + break; + case SDL_LOG_CATEGORY_ASSERT: + category_name = "assert"; + break; + case SDL_LOG_CATEGORY_SYSTEM: + category_name = "system"; + break; + case SDL_LOG_CATEGORY_AUDIO: + category_name = "audio"; + break; + case SDL_LOG_CATEGORY_VIDEO: + category_name = "video"; + break; + case SDL_LOG_CATEGORY_RENDER: + category_name = "render"; + break; + case SDL_LOG_CATEGORY_INPUT: + category_name = "input"; + break; + case SDL_LOG_CATEGORY_TEST: + category_name = "test"; + break; + default: + category_name = fmt::format("unknown({})", category); + break; + } + + auto log_level = Common::Log::LogLevel::LNOTICE; + switch (priority) + { + case SDL_LOG_PRIORITY_VERBOSE: + case SDL_LOG_PRIORITY_DEBUG: + log_level = Common::Log::LogLevel::LDEBUG; + break; + case SDL_LOG_PRIORITY_INFO: + log_level = Common::Log::LogLevel::LINFO; + break; + case SDL_LOG_PRIORITY_WARN: + log_level = Common::Log::LogLevel::LWARNING; + break; + case SDL_LOG_PRIORITY_ERROR: + log_level = Common::Log::LogLevel::LERROR; + break; + case SDL_LOG_PRIORITY_CRITICAL: + log_level = Common::Log::LogLevel::LNOTICE; + break; + } + + GENERIC_LOG_FMT(Common::Log::LogType::CONTROLLERINTERFACE, log_level, "{}: {}", + category_name, message); + }, + nullptr); +} + 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 +158,27 @@ void Init() SDL_InitSubSystem(SDL_INIT_JOYSTICK); SDL_QuitSubSystem(SDL_INIT_JOYSTICK); #endif + + EnableSDLLogging(); + +#if SDL_VERSION_ATLEAST(2, 0, 14) + // This is required on windows so that SDL's joystick code properly pumps window messages + SDL_SetHint(SDL_HINT_JOYSTICK_THREAD, "1"); +#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; @@ -125,11 +206,34 @@ void Init() } } +#ifdef _WIN32 + // This is a hack to workaround SDL_hidapi using window messages to detect device + // removal/arrival, yet no part of SDL pumps messages for it. It can hopefully be removed in the + // future when SDL fixes the issue. Note this is a separate issue from SDL_HINT_JOYSTICK_THREAD. + // Also note that SDL_WaitEvent may block while device detection window messages get queued up, + // causing some noticible stutter. This is just another reason it should be fixed properly by + // SDL... + const auto window_handle = + FindWindowEx(HWND_MESSAGE, nullptr, TEXT("SDL_HIDAPI_DEVICE_DETECTION"), nullptr); +#endif + SDL_Event e; while (SDL_WaitEvent(&e) != 0) { if (!HandleEventAndContinue(e)) return; + +#ifdef _WIN32 + MSG msg; + while (window_handle && PeekMessage(&msg, window_handle, 0, 0, PM_NOREMOVE)) + { + if (GetMessageA(&msg, window_handle, 0, 0) != 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#endif } }); @@ -172,10 +276,13 @@ 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" + // 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" + // XXX x360 controllers _should_ work on modern SDL2, so it's unclear why they're + // still broken. Perhaps it's because we're not pumping window messages, which SDL seems to + // expect. #ifdef _WIN32 // checking the name is probably good (and hacky) enough // but I'll double check with the num of buttons/axes @@ -183,7 +290,7 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index) Common::ToLower(&lcasename); if ((std::string::npos != lcasename.find("xbox 360")) && - (10 == SDL_JoystickNumButtons(joystick)) && (5 == SDL_JoystickNumAxes(joystick)) && + (11 == SDL_JoystickNumButtons(joystick)) && (6 == SDL_JoystickNumAxes(joystick)) && (1 == SDL_JoystickNumHats(joystick)) && (0 == SDL_JoystickNumBalls(joystick))) { // this device won't be used @@ -220,53 +327,110 @@ Joystick::Joystick(SDL_Joystick* const joystick, const int sdl_index) } #ifdef USE_SDL_HAPTIC - m_haptic = SDL_HapticOpenFromJoystick(m_joystick); - if (!m_haptic) - return; + if (SDL_JoystickIsHaptic(m_joystick)) + { + m_haptic = SDL_HapticOpenFromJoystick(m_joystick); + 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)); - // 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)); + } - // Periodic - for (auto waveform : - {SDL_HAPTIC_SINE, SDL_HAPTIC_TRIANGLE, SDL_HAPTIC_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHDOWN}) + // LeftRight + if (supported_effects & SDL_HAPTIC_LEFTRIGHT) + { + AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Strong)); + AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Weak)); + } + } + } +#endif + +#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 + if (SDL_IsGameController(sdl_index)) { - AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Strong)); - AddOutput(new LeftRightEffect(m_haptic, LeftRightEffect::Motor::Weak)); + m_controller = SDL_GameControllerOpen(sdl_index); + if (m_controller) + { + 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 } Joystick::~Joystick() { +#ifdef USE_SDL_GAMECONTROLLER + if (m_controller) + { + SDL_GameControllerClose(m_controller); + m_controller = nullptr; + } +#endif + #ifdef USE_SDL_HAPTIC if (m_haptic) { // stop/destroy all effects SDL_HapticStopAll(m_haptic); - // close haptic first + // close haptic before joystick SDL_HapticClose(m_haptic); + m_haptic = nullptr; } #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 +606,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 +669,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 |
