diff --git a/src/duckstation-qt/inputbindingwidgets.cpp b/src/duckstation-qt/inputbindingwidgets.cpp index cb60d25c9..166ee3330 100644 --- a/src/duckstation-qt/inputbindingwidgets.cpp +++ b/src/duckstation-qt/inputbindingwidgets.cpp @@ -1,6 +1,6 @@ #include "inputbindingwidgets.h" #include "core/settings.h" -#include "frontend-common/sdl_controller_interface.h" +#include "frontend-common/controller_interface.h" #include "qthostinterface.h" #include "qtutils.h" #include @@ -130,33 +130,41 @@ bool InputButtonBindingWidget::eventFilter(QObject* watched, QEvent* event) void InputButtonBindingWidget::hookControllerInput() { + ControllerInterface* controller_interface = m_host_interface->getControllerInterface(); + if (!controller_interface) + return; + m_host_interface->enableBackgroundControllerPolling(); - g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) { - if (ei.type == SDLControllerInterface::Hook::Type::Axis) + controller_interface->SetHook([this](const ControllerInterface::Hook& ei) { + if (ei.type == ControllerInterface::Hook::Type::Axis) { // wait until it's at least half pushed so we don't get confused between axises with small movement if (std::abs(ei.value) < 0.5f) - return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring; + return ControllerInterface::Hook::CallbackResult::ContinueMonitoring; // TODO: this probably should consider the "last value" QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index), Q_ARG(int, ei.button_or_axis_number), Q_ARG(bool, ei.value > 0)); - return SDLControllerInterface::Hook::CallbackResult::StopMonitoring; + return ControllerInterface::Hook::CallbackResult::StopMonitoring; } - else if (ei.type == SDLControllerInterface::Hook::Type::Button && ei.value > 0.0f) + else if (ei.type == ControllerInterface::Hook::Type::Button && ei.value > 0.0f) { QMetaObject::invokeMethod(this, "bindToControllerButton", Q_ARG(int, ei.controller_index), Q_ARG(int, ei.button_or_axis_number)); - return SDLControllerInterface::Hook::CallbackResult::StopMonitoring; + return ControllerInterface::Hook::CallbackResult::StopMonitoring; } - return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring; + return ControllerInterface::Hook::CallbackResult::ContinueMonitoring; }); } void InputButtonBindingWidget::unhookControllerInput() { - g_sdl_controller_interface.ClearHook(); + ControllerInterface* controller_interface = m_host_interface->getControllerInterface(); + if (!controller_interface) + return; + + controller_interface->ClearHook(); m_host_interface->disableBackgroundControllerPolling(); } @@ -200,26 +208,34 @@ InputAxisBindingWidget::~InputAxisBindingWidget() void InputAxisBindingWidget::hookControllerInput() { + ControllerInterface* controller_interface = m_host_interface->getControllerInterface(); + if (!controller_interface) + return; + m_host_interface->enableBackgroundControllerPolling(); - g_sdl_controller_interface.SetHook([this](const SDLControllerInterface::Hook& ei) { - if (ei.type == SDLControllerInterface::Hook::Type::Axis) + controller_interface->SetHook([this](const ControllerInterface::Hook& ei) { + if (ei.type == ControllerInterface::Hook::Type::Axis) { // wait until it's at least half pushed so we don't get confused between axises with small movement if (std::abs(ei.value) < 0.5f) - return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring; + return ControllerInterface::Hook::CallbackResult::ContinueMonitoring; QMetaObject::invokeMethod(this, "bindToControllerAxis", Q_ARG(int, ei.controller_index), Q_ARG(int, ei.button_or_axis_number)); - return SDLControllerInterface::Hook::CallbackResult::StopMonitoring; + return ControllerInterface::Hook::CallbackResult::StopMonitoring; } - return SDLControllerInterface::Hook::CallbackResult::ContinueMonitoring; + return ControllerInterface::Hook::CallbackResult::ContinueMonitoring; }); } void InputAxisBindingWidget::unhookControllerInput() { - g_sdl_controller_interface.ClearHook(); + ControllerInterface* controller_interface = m_host_interface->getControllerInterface(); + if (!controller_interface) + return; + + controller_interface->ClearHook(); m_host_interface->disableBackgroundControllerPolling(); } diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp index 51095b5b1..33dd8eb64 100644 --- a/src/duckstation-qt/qthostinterface.cpp +++ b/src/duckstation-qt/qthostinterface.cpp @@ -35,6 +35,7 @@ QtHostInterface::QtHostInterface(QObject* parent) { qRegisterMetaType(); + // TODO: This probably should wait until the thread finishes initializing. loadSettings(); createThread(); } @@ -45,6 +46,23 @@ QtHostInterface::~QtHostInterface() stopThread(); } +bool QtHostInterface::Initialize() +{ + if (!CommonHostInterface::Initialize()) + return false; + + if (m_controller_interface) + m_controller_interface->PollEvents(); + + updateInputMap(); + return true; +} + +void QtHostInterface::Shutdown() +{ + CommonHostInterface::Shutdown(); +} + void QtHostInterface::ReportError(const char* message) { HostInterface::ReportError(message); @@ -77,19 +95,19 @@ bool QtHostInterface::ConfirmMessage(const char* message) QVariant QtHostInterface::getSettingValue(const QString& name, const QVariant& default_value) { - std::lock_guard guard(m_qsettings_mutex); + std::lock_guard guard(m_qsettings_mutex); return m_qsettings.value(name, default_value); } void QtHostInterface::putSettingValue(const QString& name, const QVariant& value) { - std::lock_guard guard(m_qsettings_mutex); + std::lock_guard guard(m_qsettings_mutex); m_qsettings.setValue(name, value); } void QtHostInterface::removeSettingValue(const QString& name) { - std::lock_guard guard(m_qsettings_mutex); + std::lock_guard guard(m_qsettings_mutex); m_qsettings.remove(name); } @@ -101,10 +119,10 @@ void QtHostInterface::setDefaultSettings() return; } - std::lock_guard guard(m_qsettings_mutex); + std::lock_guard guard(m_qsettings_mutex); QtSettingsInterface si(m_qsettings); UpdateSettings([this, &si]() { m_settings.Load(si); }); - UpdateInputMap(si); + CommonHostInterface::UpdateInputMap(si); } void QtHostInterface::applySettings() @@ -115,10 +133,10 @@ void QtHostInterface::applySettings() return; } - std::lock_guard guard(m_qsettings_mutex); + std::lock_guard guard(m_qsettings_mutex); QtSettingsInterface si(m_qsettings); UpdateSettings([this, &si]() { m_settings.Load(si); }); - UpdateInputMap(si); + CommonHostInterface::UpdateInputMap(si); } void QtHostInterface::loadSettings() @@ -143,7 +161,7 @@ void QtHostInterface::refreshGameList(bool invalidate_cache /* = false */, bool { Assert(!isOnWorkerThread()); - std::lock_guard lock(m_qsettings_mutex); + std::lock_guard lock(m_qsettings_mutex); QtSettingsInterface si(m_qsettings); m_game_list->SetSearchDirectoriesFromSettings(si); @@ -348,13 +366,9 @@ void QtHostInterface::OnSystemStateSaved(bool global, s32 slot) emit stateSaved(QString::fromStdString(m_system->GetRunningCode()), global, slot); } -void QtHostInterface::OnControllerTypeChanged(u32 slot) +void QtHostInterface::UpdateInputMap() { - HostInterface::OnControllerTypeChanged(slot); - - // this assumes the settings mutex is already locked - as it comes from updateSettings(). - QtSettingsInterface si(m_qsettings); - UpdateInputMap(si); + updateInputMap(); } void QtHostInterface::updateInputMap() @@ -365,9 +379,9 @@ void QtHostInterface::updateInputMap() return; } - std::lock_guard lock(m_qsettings_mutex); + std::lock_guard lock(m_qsettings_mutex); QtSettingsInterface si(m_qsettings); - UpdateInputMap(si); + CommonHostInterface::UpdateInputMap(si); } void QtHostInterface::powerOffSystem() @@ -613,7 +627,7 @@ void QtHostInterface::enableBackgroundControllerPolling() return; } - if (m_background_controller_polling_enable_count++ > 0) + if (!m_controller_interface || m_background_controller_polling_enable_count++ > 0) return; if (!m_system || m_paused) @@ -621,7 +635,7 @@ void QtHostInterface::enableBackgroundControllerPolling() createBackgroundControllerPollTimer(); // drain the event queue so we don't get events late - g_sdl_controller_interface.PumpSDLEvents(); + m_controller_interface->PollEvents(); } } @@ -634,7 +648,7 @@ void QtHostInterface::disableBackgroundControllerPolling() } Assert(m_background_controller_polling_enable_count > 0); - if (--m_background_controller_polling_enable_count > 0) + if (!m_controller_interface || --m_background_controller_polling_enable_count > 0) return; if (!m_system || m_paused) @@ -643,7 +657,7 @@ void QtHostInterface::disableBackgroundControllerPolling() void QtHostInterface::doBackgroundControllerPoll() { - g_sdl_controller_interface.PumpSDLEvents(); + m_controller_interface->PollEvents(); } void QtHostInterface::createBackgroundControllerPollTimer() @@ -689,9 +703,8 @@ void QtHostInterface::threadEntryPoint() m_worker_thread_event_loop = new QEventLoop(); // set up controller interface and immediate poll to pick up the controller attached events - g_sdl_controller_interface.Initialize(this); - g_sdl_controller_interface.PumpSDLEvents(); - updateInputMap(); + if (!Initialize()) + Panic("Failed to initialize host interface"); // TODO: Event which flags the thread as ready while (!m_shutdown_flag.load()) @@ -711,14 +724,12 @@ void QtHostInterface::threadEntryPoint() m_system->Throttle(); m_worker_thread_event_loop->processEvents(QEventLoop::AllEvents); - g_sdl_controller_interface.PumpSDLEvents(); + if (m_controller_interface) + m_controller_interface->PollEvents(); } - m_system.reset(); - m_audio_stream.reset(); - - g_sdl_controller_interface.Shutdown(); - + Shutdown(); + delete m_worker_thread_event_loop; m_worker_thread_event_loop = nullptr; diff --git a/src/duckstation-qt/qthostinterface.h b/src/duckstation-qt/qthostinterface.h index d0c4b92fa..804110417 100644 --- a/src/duckstation-qt/qthostinterface.h +++ b/src/duckstation-qt/qthostinterface.h @@ -28,7 +28,7 @@ class QtDisplayWidget; Q_DECLARE_METATYPE(SystemBootParameters); -class QtHostInterface : public QObject, private CommonHostInterface +class QtHostInterface final : public QObject, private CommonHostInterface { Q_OBJECT @@ -36,6 +36,9 @@ public: explicit QtHostInterface(QObject* parent = nullptr); ~QtHostInterface(); + bool Initialize() override; + void Shutdown() override; + void ReportError(const char* message) override; void ReportMessage(const char* message) override; bool ConfirmMessage(const char* message) override; @@ -50,6 +53,7 @@ public: void refreshGameList(bool invalidate_cache = false, bool invalidate_database = false); ALWAYS_INLINE const HotkeyInfoList& getHotkeyInfoList() const { return GetHotkeyInfoList(); } + ALWAYS_INLINE ControllerInterface* getControllerInterface() const { return GetControllerInterface(); } ALWAYS_INLINE bool isOnWorkerThread() const { return QThread::currentThread() == m_worker_thread; } @@ -125,7 +129,8 @@ protected: void OnSystemPerformanceCountersUpdated() override; void OnRunningGameChanged() override; void OnSystemStateSaved(bool global, s32 slot) override; - void OnControllerTypeChanged(u32 slot) override; + + void UpdateInputMap() override; private: enum : u32 @@ -161,7 +166,7 @@ private: void wakeThread(); QSettings m_qsettings; - std::mutex m_qsettings_mutex; + std::recursive_mutex m_qsettings_mutex; MainWindow* m_main_window = nullptr; QtDisplayWidget* m_display_widget = nullptr; diff --git a/src/duckstation-sdl/sdl_host_interface.cpp b/src/duckstation-sdl/sdl_host_interface.cpp index fb9917922..ceb5ef3ce 100644 --- a/src/duckstation-sdl/sdl_host_interface.cpp +++ b/src/duckstation-sdl/sdl_host_interface.cpp @@ -32,7 +32,6 @@ SDLHostInterface::SDLHostInterface() SDLHostInterface::~SDLHostInterface() { - g_sdl_controller_interface.Shutdown(); if (m_display) { DestroyDisplay(); @@ -232,7 +231,6 @@ void SDLHostInterface::OnSystemCreated() HostInterface::OnSystemCreated(); UpdateKeyboardControllerMapping(); - g_sdl_controller_interface.SetDefaultBindings(); ClearImGuiFocus(); } @@ -254,7 +252,6 @@ void SDLHostInterface::OnControllerTypeChanged(u32 slot) HostInterface::OnControllerTypeChanged(slot); UpdateKeyboardControllerMapping(); - g_sdl_controller_interface.SetDefaultBindings(); } void SDLHostInterface::RunLater(std::function callback) @@ -309,12 +306,6 @@ std::unique_ptr SDLHostInterface::Create() return nullptr; } - if (!g_sdl_controller_interface.Initialize(intf.get())) - { - Log_ErrorPrintf("Failed to initialize controller interface."); - return nullptr; - } - intf->CreateImGuiContext(); if (!intf->CreateDisplay()) { @@ -365,7 +356,7 @@ bool SDLHostInterface::ConfirmMessage(const char* message) void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) { ImGui_ImplSDL2_ProcessEvent(event); - g_sdl_controller_interface.ProcessSDLEvent(event); + //g_sdl_controller_interface.ProcessSDLEvent(event); switch (event->type) { @@ -397,7 +388,7 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event) case SDL_CONTROLLERDEVICEADDED: case SDL_CONTROLLERDEVICEREMOVED: - g_sdl_controller_interface.SetDefaultBindings(); + //g_sdl_controller_interface.SetDefaultBindings(); break; case SDL_CONTROLLERBUTTONDOWN: @@ -1435,7 +1426,7 @@ void SDLHostInterface::Run() } } - g_sdl_controller_interface.UpdateControllerRumble(); + //g_sdl_controller_interface.UpdateControllerRumble(); // rendering { diff --git a/src/duckstation-sdl/sdl_host_interface.h b/src/duckstation-sdl/sdl_host_interface.h index 3589170b9..2cd47e0c3 100644 --- a/src/duckstation-sdl/sdl_host_interface.h +++ b/src/duckstation-sdl/sdl_host_interface.h @@ -3,6 +3,7 @@ #include "common/gl/texture.h" #include "core/host_display.h" #include "core/host_interface.h" +#include "frontend-common/sdl_controller_interface.h" #include #include #include @@ -121,7 +122,7 @@ private: std::unique_ptr m_app_icon_texture; KeyboardControllerActionMap m_keyboard_button_mapping; - + u32 m_run_later_event_id = 0; bool m_fullscreen = false; diff --git a/src/frontend-common/CMakeLists.txt b/src/frontend-common/CMakeLists.txt index 82bb1263c..63c901df5 100644 --- a/src/frontend-common/CMakeLists.txt +++ b/src/frontend-common/CMakeLists.txt @@ -1,6 +1,8 @@ add_library(frontend-common common_host_interface.cpp common_host_interface.h + controller_interface.cpp + controller_interface.h icon.cpp icon.h imgui_styles.cpp diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp index 50040e752..32b0ed2fc 100644 --- a/src/frontend-common/common_host_interface.cpp +++ b/src/frontend-common/common_host_interface.cpp @@ -3,6 +3,7 @@ #include "common/audio_stream.h" #include "common/log.h" #include "common/string_util.h" +#include "controller_interface.h" #include "core/controller.h" #include "core/game_list.h" #include "core/gpu.h" @@ -14,14 +15,41 @@ #include Log_SetChannel(CommonHostInterface); -CommonHostInterface::CommonHostInterface() : HostInterface() +CommonHostInterface::CommonHostInterface() = default; + +CommonHostInterface::~CommonHostInterface() = default; + +bool CommonHostInterface::Initialize() { RegisterGeneralHotkeys(); RegisterGraphicsHotkeys(); RegisterSaveStateHotkeys(); + + m_controller_interface = CreateControllerInterface(); + if (m_controller_interface && !m_controller_interface->Initialize(this)) + { + Log_WarningPrintf("Failed to initialize controller bindings are not possible."); + m_controller_interface.reset(); + } + else if (!m_controller_interface) + { + Log_WarningPrintf("No controller interface created, controller bindings are not possible."); + } + + return true; } -CommonHostInterface::~CommonHostInterface() = default; +void CommonHostInterface::Shutdown() +{ + m_system.reset(); + m_audio_stream.reset(); + + if (m_controller_interface) + { + m_controller_interface->Shutdown(); + m_controller_interface.reset(); + } +} void CommonHostInterface::SetFullscreen(bool enabled) {} @@ -47,6 +75,16 @@ std::unique_ptr CommonHostInterface::CreateAudioStream(AudioBackend } } +std::unique_ptr CommonHostInterface::CreateControllerInterface() +{ + // In the future we might want to use different controller interfaces. +#ifdef WITH_SDL2 + return std::make_unique(); +#else + return nullptr; +#endif +} + void CommonHostInterface::OnSystemCreated() { HostInterface::OnSystemCreated(); @@ -65,6 +103,13 @@ void CommonHostInterface::OnSystemPaused(bool paused) SetFullscreen(true); } +void CommonHostInterface::OnControllerTypeChanged(u32 slot) +{ + HostInterface::OnControllerTypeChanged(slot); + + UpdateInputMap(); +} + void CommonHostInterface::SetDefaultSettings(SettingsInterface& si) { HostInterface::SetDefaultSettings(si); @@ -117,9 +162,8 @@ bool CommonHostInterface::HandleHostKeyEvent(HostKeyCode key, bool pressed) void CommonHostInterface::UpdateInputMap(SettingsInterface& si) { m_keyboard_input_handlers.clear(); -#ifdef WITH_SDL2 - g_sdl_controller_interface.ClearControllerBindings(); -#endif + if (m_controller_interface) + m_controller_interface->ClearBindings(); UpdateControllerInputMap(si); UpdateHotkeyInputMap(si); @@ -232,9 +276,14 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const return true; } -#ifdef WITH_SDL2 if (StringUtil::StartsWith(device, "Controller")) { + if (!m_controller_interface) + { + Log_ErrorPrintf("No controller interface set, cannot bind '%s'", binding.c_str()); + return false; + } + const std::optional controller_index = StringUtil::FromChars(device.substr(10)); if (!controller_index || *controller_index < 0) { @@ -246,7 +295,7 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const { const std::optional button_index = StringUtil::FromChars(button.substr(6)); if (!button_index || - !g_sdl_controller_interface.BindControllerButton(*controller_index, *button_index, std::move(handler))) + !m_controller_interface->BindControllerButton(*controller_index, *button_index, std::move(handler))) { Log_WarningPrintf("Failed to bind controller button '%s' to button", binding.c_str()); return false; @@ -258,8 +307,8 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const { const std::optional axis_index = StringUtil::FromChars(button.substr(5)); const bool positive = (button[0] == '+'); - if (!axis_index || !g_sdl_controller_interface.BindControllerAxisToButton(*controller_index, *axis_index, - positive, std::move(handler))) + if (!axis_index || !m_controller_interface->BindControllerAxisToButton(*controller_index, *axis_index, positive, + std::move(handler))) { Log_WarningPrintf("Failed to bind controller axis '%s' to button", binding.c_str()); return false; @@ -271,7 +320,6 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str()); return false; } -#endif Log_WarningPrintf("Unknown input device in button binding '%s'", binding.c_str()); return false; @@ -280,9 +328,14 @@ bool CommonHostInterface::AddButtonToInputMap(const std::string& binding, const bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const std::string_view& device, const std::string_view& axis, InputAxisHandler handler) { -#ifdef WITH_SDL2 if (StringUtil::StartsWith(device, "Controller")) { + if (!m_controller_interface) + { + Log_ErrorPrintf("No controller interface set, cannot bind '%s'", binding.c_str()); + return false; + } + const std::optional controller_index = StringUtil::FromChars(device.substr(10)); if (!controller_index || *controller_index < 0) { @@ -294,7 +347,7 @@ bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const st { const std::optional axis_index = StringUtil::FromChars(axis.substr(4)); if (!axis_index || - !g_sdl_controller_interface.BindControllerAxis(*controller_index, *axis_index, std::move(handler))) + !m_controller_interface->BindControllerAxis(*controller_index, *axis_index, std::move(handler))) { Log_WarningPrintf("Failed to bind controller axis '%s' to axi", binding.c_str()); return false; @@ -306,7 +359,6 @@ bool CommonHostInterface::AddAxisToInputMap(const std::string& binding, const st Log_WarningPrintf("Malformed controller binding '%s' in button", binding.c_str()); return false; } -#endif Log_WarningPrintf("Unknown input device in axis binding '%s'", binding.c_str()); return false; diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h index 6afdeb3b1..0128552dc 100644 --- a/src/frontend-common/common_host_interface.h +++ b/src/frontend-common/common_host_interface.h @@ -11,9 +11,13 @@ #include #include +class ControllerInterface; + class CommonHostInterface : public HostInterface { public: + friend ControllerInterface; + using HostKeyCode = s32; using InputButtonHandler = std::function; @@ -29,8 +33,14 @@ public: using HotkeyInfoList = std::vector; + virtual bool Initialize(); + virtual void Shutdown(); + /// Returns a list of all available hotkeys. - const HotkeyInfoList& GetHotkeyInfoList() const { return m_hotkeys; } + ALWAYS_INLINE const HotkeyInfoList& GetHotkeyInfoList() const { return m_hotkeys; } + + /// Access to current controller interface. + ALWAYS_INLINE ControllerInterface* GetControllerInterface() const { return m_controller_interface.get(); } protected: CommonHostInterface(); @@ -40,28 +50,36 @@ protected: virtual void ToggleFullscreen(); virtual std::unique_ptr CreateAudioStream(AudioBackend backend) override; + virtual std::unique_ptr CreateControllerInterface(); virtual void OnSystemCreated() override; virtual void OnSystemPaused(bool paused) override; + virtual void OnControllerTypeChanged(u32 slot) override; virtual void SetDefaultSettings(SettingsInterface& si) override; virtual std::optional GetHostKeyCode(const std::string_view key_code) const; + virtual bool AddButtonToInputMap(const std::string& binding, const std::string_view& device, + const std::string_view& button, InputButtonHandler handler); + virtual bool AddAxisToInputMap(const std::string& binding, const std::string_view& device, + const std::string_view& axis, InputAxisHandler handler); + + /// Reloads the input map from config. Callable from controller interface. + virtual void UpdateInputMap() = 0; + void RegisterHotkey(String category, String name, String display_name, InputButtonHandler handler); bool HandleHostKeyEvent(HostKeyCode code, bool pressed); void UpdateInputMap(SettingsInterface& si); + std::unique_ptr m_controller_interface; + private: void RegisterGeneralHotkeys(); void RegisterGraphicsHotkeys(); void RegisterSaveStateHotkeys(); void UpdateControllerInputMap(SettingsInterface& si); void UpdateHotkeyInputMap(SettingsInterface& si); - virtual bool AddButtonToInputMap(const std::string& binding, const std::string_view& device, - const std::string_view& button, InputButtonHandler handler); - virtual bool AddAxisToInputMap(const std::string& binding, const std::string_view& device, - const std::string_view& axis, InputAxisHandler handler); HotkeyInfoList m_hotkeys; diff --git a/src/frontend-common/controller_interface.cpp b/src/frontend-common/controller_interface.cpp new file mode 100644 index 000000000..87da03282 --- /dev/null +++ b/src/frontend-common/controller_interface.cpp @@ -0,0 +1,93 @@ +#include "controller_interface.h" +#include "common/assert.h" +#include "common/log.h" +#include "core/controller.h" +#include "core/system.h" +#include +Log_SetChannel(ControllerInterface); + +ControllerInterface::ControllerInterface() = default; + +ControllerInterface::~ControllerInterface() = default; + +bool ControllerInterface::Initialize(CommonHostInterface* host_interface) +{ + m_host_interface = host_interface; + return true; +} + +void ControllerInterface::Shutdown() +{ + m_host_interface = nullptr; +} + +System* ControllerInterface::GetSystem() const +{ + return m_host_interface->GetSystem(); +} + +Controller* ControllerInterface::GetController(u32 slot) const +{ + System* system = GetSystem(); + return system ? system->GetController(slot) : nullptr; +} + +void ControllerInterface::SetHook(Hook::Callback callback) +{ + std::unique_lock lock(m_event_intercept_mutex); + Assert(!m_event_intercept_callback); + m_event_intercept_callback = std::move(callback); +} + +void ControllerInterface::ClearHook() +{ + std::unique_lock lock(m_event_intercept_mutex); + if (m_event_intercept_callback) + m_event_intercept_callback = {}; +} + +bool ControllerInterface::DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value) +{ + std::unique_lock lock(m_event_intercept_mutex); + if (!m_event_intercept_callback) + return false; + + const Hook ei{type, controller_index, button_or_axis_number, value}; + const Hook::CallbackResult action = m_event_intercept_callback(ei); + if (action == Hook::CallbackResult::StopMonitoring) + m_event_intercept_callback = {}; + + return true; +} + +void ControllerInterface::OnControllerConnected(int host_id) +{ + Log_InfoPrintf("Host controller %d connected, updating input map", host_id); + m_host_interface->UpdateInputMap(); +} + +void ControllerInterface::OnControllerDisconnected(int host_id) +{ + Log_InfoPrintf("Host controller %d disconnected, updating input map", host_id); + m_host_interface->UpdateInputMap(); +} + +void ControllerInterface::ClearBindings() {} + +bool ControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) +{ + return false; +} + +bool ControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback) +{ + return false; +} + +bool ControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction, + ButtonCallback callback) +{ + return false; +} + +void ControllerInterface::UpdateControllerRumble() {} diff --git a/src/frontend-common/controller_interface.h b/src/frontend-common/controller_interface.h new file mode 100644 index 000000000..e95863abf --- /dev/null +++ b/src/frontend-common/controller_interface.h @@ -0,0 +1,80 @@ +#pragma once +#include "common_host_interface.h" +#include "core/types.h" +#include +#include +#include +#include + +class HostInterface; +class System; +class Controller; + +class ControllerInterface +{ +public: + enum : int + { + MAX_NUM_AXISES = 7, + MAX_NUM_BUTTONS = 15 + }; + + using AxisCallback = CommonHostInterface::InputAxisHandler; + using ButtonCallback = CommonHostInterface::InputButtonHandler; + + ControllerInterface(); + virtual ~ControllerInterface(); + + virtual bool Initialize(CommonHostInterface* host_interface); + virtual void Shutdown(); + + // Removes all bindings. Call before setting new bindings. + virtual void ClearBindings() = 0; + + // Binding to events. If a binding for this axis/button already exists, returns false. + virtual bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) = 0; + virtual bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) = 0; + virtual bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, + ButtonCallback callback) = 0; + + virtual void PollEvents() = 0; + virtual void UpdateControllerRumble() = 0; + + // Input monitoring for external access. + struct Hook + { + enum class Type + { + Axis, + Button + }; + + enum class CallbackResult + { + StopMonitoring, + ContinueMonitoring + }; + + using Callback = std::function; + + Type type; + int controller_index; + int button_or_axis_number; + float value; // 0/1 for buttons, -1..1 for axises + }; + void SetHook(Hook::Callback callback); + void ClearHook(); + +protected: + System* GetSystem() const; + Controller* GetController(u32 slot) const; + bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value); + + void OnControllerConnected(int host_id); + void OnControllerDisconnected(int host_id); + + CommonHostInterface* m_host_interface = nullptr; + + std::mutex m_event_intercept_mutex; + Hook::Callback m_event_intercept_callback; +}; diff --git a/src/frontend-common/frontend-common.vcxproj b/src/frontend-common/frontend-common.vcxproj index 60d2b0caf..964328726 100644 --- a/src/frontend-common/frontend-common.vcxproj +++ b/src/frontend-common/frontend-common.vcxproj @@ -56,6 +56,7 @@ + @@ -65,6 +66,7 @@ + diff --git a/src/frontend-common/frontend-common.vcxproj.filters b/src/frontend-common/frontend-common.vcxproj.filters index 4d94c6622..4d5930475 100644 --- a/src/frontend-common/frontend-common.vcxproj.filters +++ b/src/frontend-common/frontend-common.vcxproj.filters @@ -8,6 +8,7 @@ + @@ -17,6 +18,7 @@ + diff --git a/src/frontend-common/sdl_controller_interface.cpp b/src/frontend-common/sdl_controller_interface.cpp index ebb0173e0..ce7b6b25d 100644 --- a/src/frontend-common/sdl_controller_interface.cpp +++ b/src/frontend-common/sdl_controller_interface.cpp @@ -9,8 +9,6 @@ #include Log_SetChannel(SDLControllerInterface); -SDLControllerInterface g_sdl_controller_interface; - SDLControllerInterface::SDLControllerInterface() = default; SDLControllerInterface::~SDLControllerInterface() @@ -18,8 +16,11 @@ SDLControllerInterface::~SDLControllerInterface() Assert(m_controllers.empty()); } -bool SDLControllerInterface::Initialize(HostInterface* host_interface) +bool SDLControllerInterface::Initialize(CommonHostInterface* host_interface) { + if (!ControllerInterface::Initialize(host_interface)) + return false; + FrontendCommon::EnsureSDLInitialized(); if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0) @@ -29,26 +30,25 @@ bool SDLControllerInterface::Initialize(HostInterface* host_interface) } // we should open the controllers as the connected events come in, so no need to do any more here - m_host_interface = host_interface; - m_initialized = true; + m_sdl_subsystem_initialized = true; return true; } void SDLControllerInterface::Shutdown() { - while (!m_controllers.empty()) - CloseGameController(m_controllers.begin()->first); + ControllerInterface::Shutdown(); - if (m_initialized) + while (!m_controllers.empty()) + CloseGameController(m_controllers.begin()->joystick_id); + + if (m_sdl_subsystem_initialized) { SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC); - m_initialized = false; + m_sdl_subsystem_initialized = false; } - - m_host_interface = nullptr; } -void SDLControllerInterface::PumpSDLEvents() +void SDLControllerInterface::PollEvents() { for (;;) { @@ -90,244 +90,140 @@ bool SDLControllerInterface::ProcessSDLEvent(const SDL_Event* event) } } -System* SDLControllerInterface::GetSystem() const +SDLControllerInterface::ControllerDataVector::iterator +SDLControllerInterface::GetControllerDataForController(void* controller) { - return m_host_interface->GetSystem(); + return std::find_if(m_controllers.begin(), m_controllers.end(), + [controller](const ControllerData& cd) { return cd.controller == controller; }); } -Controller* SDLControllerInterface::GetController(u32 slot) const +SDLControllerInterface::ControllerDataVector::iterator SDLControllerInterface::GetControllerDataForJoystickId(int id) { - System* system = GetSystem(); - return system ? system->GetController(slot) : nullptr; + return std::find_if(m_controllers.begin(), m_controllers.end(), + [id](const ControllerData& cd) { return cd.joystick_id == id; }); } -void SDLControllerInterface::SetHook(Hook::Callback callback) +SDLControllerInterface::ControllerDataVector::iterator SDLControllerInterface::GetControllerDataForPlayerId(int id) { - std::unique_lock lock(m_event_intercept_mutex); - Assert(!m_event_intercept_callback); - m_event_intercept_callback = std::move(callback); -} - -void SDLControllerInterface::ClearHook() -{ - std::unique_lock lock(m_event_intercept_mutex); - if (m_event_intercept_callback) - m_event_intercept_callback = {}; -} - -bool SDLControllerInterface::DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value) -{ - std::unique_lock lock(m_event_intercept_mutex); - if (!m_event_intercept_callback) - return false; - - const Hook ei{type, controller_index, button_or_axis_number, value}; - const Hook::CallbackResult action = m_event_intercept_callback(ei); - if (action == Hook::CallbackResult::StopMonitoring) - m_event_intercept_callback = {}; - - return true; + return std::find_if(m_controllers.begin(), m_controllers.end(), + [id](const ControllerData& cd) { return cd.player_id == id; }); } bool SDLControllerInterface::OpenGameController(int index) { - if (m_controllers.find(index) != m_controllers.end()) - CloseGameController(index); - SDL_GameController* gcontroller = SDL_GameControllerOpen(index); - if (!gcontroller) + SDL_Joystick* joystick = gcontroller ? SDL_GameControllerGetJoystick(gcontroller) : nullptr; + if (!gcontroller || !joystick) { Log_WarningPrintf("Failed to open controller %d", index); + if (gcontroller) + SDL_GameControllerClose(gcontroller); + return false; } - Log_InfoPrintf("Opened controller %d: %s", index, SDL_GameControllerName(gcontroller)); + int player_index = SDL_GameControllerGetPlayerIndex(gcontroller); + int joystick_id = SDL_JoystickInstanceID(joystick); + + Log_InfoPrintf("Opened controller %d (instance id %d, player id %d): %s", index, joystick_id, player_index, + SDL_GameControllerName(gcontroller)); ControllerData cd = {}; cd.controller = gcontroller; + cd.player_id = player_index; + cd.joystick_id = joystick_id; - SDL_Joystick* joystick = SDL_GameControllerGetJoystick(gcontroller); - if (joystick) - { - SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick); - if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) == 0) - cd.haptic = haptic; - else - SDL_HapticClose(haptic); - } + SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick); + if (SDL_HapticRumbleSupported(haptic) && SDL_HapticRumbleInit(haptic) == 0) + cd.haptic = haptic; + else if (haptic) + SDL_HapticClose(haptic); if (cd.haptic) Log_InfoPrintf("Rumble is supported on '%s'", SDL_GameControllerName(gcontroller)); else Log_WarningPrintf("Rumble is not supported on '%s'", SDL_GameControllerName(gcontroller)); - m_controllers.emplace(index, cd); + m_controllers.push_back(std::move(cd)); + OnControllerConnected(player_index); return true; } void SDLControllerInterface::CloseGameControllers() { while (!m_controllers.empty()) - CloseGameController(m_controllers.begin()->first); + CloseGameController(m_controllers.begin()->player_id); } -bool SDLControllerInterface::CloseGameController(int index) +bool SDLControllerInterface::CloseGameController(int joystick_index) { - auto it = m_controllers.find(index); + auto it = GetControllerDataForJoystickId(joystick_index); if (it == m_controllers.end()) return false; - if (it->second.haptic) - SDL_HapticClose(static_cast(it->second.haptic)); + const int player_index = it->player_id; - SDL_GameControllerClose(static_cast(it->second.controller)); + if (it->haptic) + SDL_HapticClose(static_cast(it->haptic)); + + SDL_GameControllerClose(static_cast(it->controller)); m_controllers.erase(it); + + OnControllerDisconnected(player_index); return true; } -void SDLControllerInterface::ClearControllerBindings() +void SDLControllerInterface::ClearBindings() { for (auto& it : m_controllers) { - for (AxisCallback& ac : it.second.axis_mapping) + for (AxisCallback& ac : it.axis_mapping) ac = {}; - for (ButtonCallback& bc : it.second.button_mapping) + for (ButtonCallback& bc : it.button_mapping) bc = {}; } } bool SDLControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) { - auto it = m_controllers.find(controller_index); + auto it = GetControllerDataForPlayerId(controller_index); if (it == m_controllers.end()) return false; if (axis_number < 0 || axis_number >= MAX_NUM_AXISES) return false; - it->second.axis_mapping[axis_number] = std::move(callback); + it->axis_mapping[axis_number] = std::move(callback); return true; } bool SDLControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback) { - auto it = m_controllers.find(controller_index); + auto it = GetControllerDataForPlayerId(controller_index); if (it == m_controllers.end()) return false; if (button_number < 0 || button_number >= MAX_NUM_BUTTONS) return false; - it->second.button_mapping[button_number] = std::move(callback); + it->button_mapping[button_number] = std::move(callback); return true; } bool SDLControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback) { - auto it = m_controllers.find(controller_index); + auto it = GetControllerDataForPlayerId(controller_index); if (it == m_controllers.end()) return false; if (axis_number < 0 || axis_number >= MAX_NUM_AXISES) return false; - it->second.axis_button_mapping[axis_number][BoolToUInt8(direction)] = std::move(callback); + it->axis_button_mapping[axis_number][BoolToUInt8(direction)] = std::move(callback); return true; } -void SDLControllerInterface::SetDefaultBindings() -{ - ClearControllerBindings(); - - const ControllerType type = m_host_interface->GetSettings().controller_types[0]; - if (type == ControllerType::None || m_controllers.empty()) - return; - - const int first_controller_index = m_controllers.begin()->first; - -#define SET_AXIS_MAP(axis, name) \ - do \ - { \ - std::optional code = Controller::GetAxisCodeByName(type, name); \ - if (code) \ - { \ - const s32 code_value = code.value(); \ - BindControllerAxis(first_controller_index, axis, [this, code_value](float value) { \ - Controller* controller = GetController(0); \ - if (controller) \ - controller->SetAxisState(code_value, value); \ - }); \ - } \ - } while (0) - -#define SET_BUTTON_MAP(button, name) \ - do \ - { \ - std::optional code = Controller::GetButtonCodeByName(type, name); \ - if (code) \ - { \ - const s32 code_value = code.value(); \ - BindControllerButton(first_controller_index, button, [this, code_value](bool pressed) { \ - Controller* controller = GetController(0); \ - if (controller) \ - controller->SetButtonState(code_value, pressed); \ - }); \ - } \ - } while (0) - -#define SET_AXIS_BUTTON_MAP(axis, direction, name) \ - do \ - { \ - std::optional code = Controller::GetButtonCodeByName(type, name); \ - if (code) \ - { \ - const s32 code_value = code.value(); \ - BindControllerAxisToButton(first_controller_index, axis, direction, [this, code_value](bool pressed) { \ - Controller* controller = GetController(0); \ - if (controller) \ - controller->SetButtonState(code_value, pressed); \ - }); \ - } \ - } while (0) - - SET_AXIS_MAP(SDL_CONTROLLER_AXIS_LEFTX, "LeftX"); - SET_AXIS_MAP(SDL_CONTROLLER_AXIS_LEFTY, "LeftY"); - SET_AXIS_MAP(SDL_CONTROLLER_AXIS_RIGHTX, "RightX"); - SET_AXIS_MAP(SDL_CONTROLLER_AXIS_RIGHTY, "RightY"); - SET_AXIS_MAP(SDL_CONTROLLER_AXIS_TRIGGERLEFT, "LeftTrigger"); - SET_AXIS_MAP(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, "RightTrigger"); - - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_UP, "Up"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_DOWN, "Down"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_LEFT, "Left"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_DPAD_RIGHT, "Right"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_Y, "Triangle"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_A, "Cross"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_X, "Square"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_B, "Circle"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_LEFTSHOULDER, "L1"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, "R1"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_LEFTSTICK, "L3"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_RIGHTSTICK, "R3"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_START, "Start"); - SET_BUTTON_MAP(SDL_CONTROLLER_BUTTON_BACK, "Select"); - - // fallback axis -> button mappings - SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTX, false, "Left"); - SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTX, true, "Right"); - SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTY, false, "Up"); - SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_LEFTY, true, "Down"); - SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_TRIGGERLEFT, true, "L2"); - SET_AXIS_BUTTON_MAP(SDL_CONTROLLER_AXIS_TRIGGERRIGHT, true, "R2"); - -#undef SET_AXIS_MAP -#undef SET_BUTTON_MAP -#undef SET_AXIS_BUTTON_MAP - - // TODO: L2/R2 -> buttons -} - bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev) { // TODO: Make deadzone customizable. @@ -339,12 +235,11 @@ bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev) if (DoEventHook(Hook::Type::Axis, ev->caxis.which, ev->caxis.axis, value)) return true; - auto it = m_controllers.find(ev->caxis.which); + auto it = GetControllerDataForJoystickId(ev->caxis.which); if (it == m_controllers.end()) return false; - const ControllerData& cd = it->second; - const AxisCallback& cb = cd.axis_mapping[ev->caxis.axis]; + const AxisCallback& cb = it->axis_mapping[ev->caxis.axis]; if (cb) { cb(value); @@ -354,8 +249,8 @@ bool SDLControllerInterface::HandleControllerAxisEvent(const SDL_Event* ev) // set the other direction to false so large movements don't leave the opposite on const bool outside_deadzone = (std::abs(value) >= deadzone); const bool positive = (value >= 0.0f); - const ButtonCallback& other_button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(!positive)]; - const ButtonCallback& button_cb = cd.axis_button_mapping[ev->caxis.axis][BoolToUInt8(positive)]; + const ButtonCallback& other_button_cb = it->axis_button_mapping[ev->caxis.axis][BoolToUInt8(!positive)]; + const ButtonCallback& button_cb = it->axis_button_mapping[ev->caxis.axis][BoolToUInt8(positive)]; if (button_cb) { button_cb(outside_deadzone); @@ -383,11 +278,11 @@ bool SDLControllerInterface::HandleControllerButtonEvent(const SDL_Event* ev) if (DoEventHook(Hook::Type::Button, ev->cbutton.which, ev->cbutton.button, pressed ? 1.0f : 0.0f)) return true; - auto it = m_controllers.find(ev->caxis.which); + auto it = GetControllerDataForJoystickId(ev->caxis.which); if (it == m_controllers.end()) return false; - const ButtonCallback& cb = it->second.button_mapping[ev->cbutton.button]; + const ButtonCallback& cb = it->button_mapping[ev->cbutton.button]; if (!cb) return false; @@ -397,14 +292,14 @@ bool SDLControllerInterface::HandleControllerButtonEvent(const SDL_Event* ev) void SDLControllerInterface::UpdateControllerRumble() { - for (auto& it : m_controllers) + for (auto& cd : m_controllers) { - ControllerData& cd = it.second; - if (!cd.haptic) + // TODO: FIXME proper binding + if (!cd.haptic || cd.player_id < 0 || cd.player_id >= 2) continue; float new_strength = 0.0f; - Controller* controller = GetController(cd.controller_index); + Controller* controller = GetController(cd.player_id); if (controller) { const u32 motor_count = controller->GetVibrationMotorCount(); diff --git a/src/frontend-common/sdl_controller_interface.h b/src/frontend-common/sdl_controller_interface.h index 54837aa58..f3ba1a45a 100644 --- a/src/frontend-common/sdl_controller_interface.h +++ b/src/frontend-common/sdl_controller_interface.h @@ -1,92 +1,43 @@ #pragma once #include "core/types.h" +#include "controller_interface.h" #include #include -#include +#include #include -class HostInterface; -class System; -class Controller; - union SDL_Event; -class SDLControllerInterface +class SDLControllerInterface final : public ControllerInterface { public: - enum : int - { - MAX_NUM_AXISES = 7, - MAX_NUM_BUTTONS = 15 - }; - - using AxisCallback = std::function; - using ButtonCallback = std::function; - SDLControllerInterface(); ~SDLControllerInterface(); - bool Initialize(HostInterface* host_interface); - void Shutdown(); + bool Initialize(CommonHostInterface* host_interface) override; + void Shutdown() override; // Removes all bindings. Call before setting new bindings. - void ClearControllerBindings(); + void ClearBindings() override; // Binding to events. If a binding for this axis/button already exists, returns false. - bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback); - bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback); - bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback); + bool BindControllerAxis(int controller_index, int axis_number, AxisCallback callback) override; + bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override; + bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction, ButtonCallback callback) override; - // Default bindings, used by SDL frontend. - void SetDefaultBindings(); - - void PumpSDLEvents(); + void PollEvents() override; bool ProcessSDLEvent(const SDL_Event* event); - void UpdateControllerRumble(); - - // Input monitoring for external access. - struct Hook - { - enum class Type - { - Axis, - Button - }; - - enum class CallbackResult - { - StopMonitoring, - ContinueMonitoring - }; - - using Callback = std::function; - - Type type; - int controller_index; - int button_or_axis_number; - float value; // 0/1 for buttons, -1..1 for axises - }; - void SetHook(Hook::Callback callback); - void ClearHook(); + void UpdateControllerRumble() override; private: - System* GetSystem() const; - Controller* GetController(u32 slot) const; - bool DoEventHook(Hook::Type type, int controller_index, int button_or_axis_number, float value); - - bool OpenGameController(int index); - bool CloseGameController(int index); - void CloseGameControllers(); - bool HandleControllerAxisEvent(const SDL_Event* event); - bool HandleControllerButtonEvent(const SDL_Event* event); - struct ControllerData { void* controller; void* haptic; - u32 controller_index; + int joystick_id; + int player_id; float last_rumble_strength; std::array axis_mapping; @@ -94,14 +45,22 @@ private: std::array, MAX_NUM_AXISES> axis_button_mapping; }; - HostInterface* m_host_interface = nullptr; + using ControllerDataVector = std::vector; - std::map m_controllers; + ControllerDataVector::iterator GetControllerDataForController(void* controller); + ControllerDataVector::iterator GetControllerDataForJoystickId(int id); + ControllerDataVector::iterator GetControllerDataForPlayerId(int id); + + bool OpenGameController(int index); + bool CloseGameController(int joystick_index); + void CloseGameControllers(); + bool HandleControllerAxisEvent(const SDL_Event* event); + bool HandleControllerButtonEvent(const SDL_Event* event); + + ControllerDataVector m_controllers; std::mutex m_event_intercept_mutex; Hook::Callback m_event_intercept_callback; - bool m_initialized = false; + bool m_sdl_subsystem_initialized = false; }; - -extern SDLControllerInterface g_sdl_controller_interface;