diff --git a/pcsx2/Frontend/InputManager.cpp b/pcsx2/Frontend/InputManager.cpp index afa16697d4..104e2f8c18 100644 --- a/pcsx2/Frontend/InputManager.cpp +++ b/pcsx2/Frontend/InputManager.cpp @@ -535,8 +535,7 @@ std::vector InputManager::GetHotkeyList() for (const HotkeyInfo* hotkey = hotkey_list; hotkey->name != nullptr; hotkey++) ret.push_back(hotkey); } - std::sort(ret.begin(), ret.end(), [](const HotkeyInfo* left, const HotkeyInfo* right) - { + std::sort(ret.begin(), ret.end(), [](const HotkeyInfo* left, const HotkeyInfo* right) { // category -> display name if (const int res = StringUtil::Strcasecmp(left->category, right->category); res != 0) return (res < 0); @@ -577,9 +576,9 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch if (!bindings.empty()) { // we use axes for all pad bindings to simplify things, and because they are pressure sensitive - AddBindings(bindings, InputAxisEventHandler{ [pad_index, bind_index, bind_names](float value) { + AddBindings(bindings, InputAxisEventHandler{[pad_index, bind_index, bind_names](float value) { PAD::SetControllerState(pad_index, bind_index, value); - } }); + }}); } } } @@ -593,7 +592,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch bool has_any_bindings = false; switch (vibcaps) { - case PAD::VibrationCapabilities::LargeSmallMotors: + case PAD::VibrationCapabilities::LargeSmallMotors: { const std::string large_binding(si.GetStringValue(section.c_str(), "LargeMotor")); const std::string small_binding(si.GetStringValue(section.c_str(), "SmallMotor")); @@ -602,15 +601,15 @@ void InputManager::AddPadBindings(SettingsInterface& si, u32 pad_index, const ch } break; - case PAD::VibrationCapabilities::SingleMotor: + case PAD::VibrationCapabilities::SingleMotor: { const std::string binding(si.GetStringValue(section.c_str(), "Motor")); has_any_bindings |= ParseBindingAndGetSource(binding, &vib.motors[0].binding, &vib.motors[0].source); } break; - default: - break; + default: + break; } if (has_any_bindings) @@ -660,7 +659,8 @@ bool InputManager::InvokeEvents(InputBindingKey key, float value) binding->current_mask = new_mask; // invert if we're negative, since the handler expects 0..1 - const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value : 0.0f); + const float value_to_pass = (negative ? ((value < 0.0f) ? -value : 0.0f) : (value > 0.0f) ? value : + 0.0f); // axes are fired regardless of a state change, unless they're zero // for buttons, we can use the state of the last chord key, because it'll be 1 on press, @@ -825,10 +825,7 @@ bool InputManager::DoEventHook(InputBindingKey key, float value) return false; const InputInterceptHook::CallbackResult action = m_event_intercept_callback(key, value); - if (action == InputInterceptHook::CallbackResult::StopMonitoring) - m_event_intercept_callback = {}; - - return true; + return (action == InputInterceptHook::CallbackResult::StopProcessingEvent); } // ------------------------------------------------------------------------ @@ -884,6 +881,48 @@ void InputManager::PollSources() UpdateContinuedVibration(); } + +std::vector> InputManager::EnumerateDevices() +{ + std::vector> ret; + + ret.emplace_back("Keyboard", "Keyboard"); + ret.emplace_back("Mouse", "Mouse"); + + for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++) + { + if (s_input_sources[i]) + { + std::vector> devs(s_input_sources[i]->EnumerateDevices()); + if (ret.empty()) + ret = std::move(devs); + else + std::move(devs.begin(), devs.end(), std::back_inserter(ret)); + } + } + + return ret; +} + +std::vector InputManager::EnumerateMotors() +{ + std::vector ret; + + for (u32 i = FIRST_EXTERNAL_INPUT_SOURCE; i < LAST_EXTERNAL_INPUT_SOURCE; i++) + { + if (s_input_sources[i]) + { + std::vector devs(s_input_sources[i]->EnumerateMotors()); + if (ret.empty()) + ret = std::move(devs); + else + std::move(devs.begin(), devs.end(), std::back_inserter(ret)); + } + } + + return ret; +} + template static void UpdateInputSourceState(SettingsInterface& si, InputSourceType type, bool default_state) { diff --git a/pcsx2/Frontend/InputManager.h b/pcsx2/Frontend/InputManager.h index 326f82fa91..c1d707ad2a 100644 --- a/pcsx2/Frontend/InputManager.h +++ b/pcsx2/Frontend/InputManager.h @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -100,8 +101,8 @@ struct InputInterceptHook { enum class CallbackResult { - StopMonitoring, - ContinueMonitoring + StopProcessingEvent, + ContinueProcessingEvent }; using Callback = std::function; @@ -171,6 +172,12 @@ namespace InputManager /// Returns a list of all hotkeys. std::vector GetHotkeyList(); + /// Enumerates available devices. Returns a pair of the prefix (e.g. SDL-0) and the device name. + std::vector> EnumerateDevices(); + + /// Enumerates available vibration motors at the time of call. + std::vector EnumerateMotors(); + /// Re-parses the config and registers all hotkey and pad bindings. void ReloadBindings(SettingsInterface& si); @@ -209,3 +216,12 @@ namespace InputManager /// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes. void PauseVibration(); } // namespace InputManager + +namespace Host +{ + /// Called when a new input device is connected. + void OnInputDeviceConnected(const std::string_view& identifier, const std::string_view& device_name); + + /// Called when an input device is disconnected. + void OnInputDeviceDisconnected(const std::string_view& identifier); +} \ No newline at end of file diff --git a/pcsx2/Frontend/InputSource.h b/pcsx2/Frontend/InputSource.h index 4aa5b9f685..a2e024db5e 100644 --- a/pcsx2/Frontend/InputSource.h +++ b/pcsx2/Frontend/InputSource.h @@ -17,6 +17,7 @@ #include #include +#include #include #include "common/Pcsx2Defs.h" @@ -40,6 +41,9 @@ public: const std::string_view& device, const std::string_view& binding) = 0; virtual std::string ConvertKeyToString(InputBindingKey key) = 0; + /// Enumerates available devices. Returns a pair of the prefix (e.g. SDL-0) and the device name. + virtual std::vector> EnumerateDevices() = 0; + /// Enumerates available vibration motors at the time of call. virtual std::vector EnumerateMotors() = 0; diff --git a/pcsx2/Frontend/SDLInputSource.cpp b/pcsx2/Frontend/SDLInputSource.cpp index f2f253e8c5..b93c4aff36 100644 --- a/pcsx2/Frontend/SDLInputSource.cpp +++ b/pcsx2/Frontend/SDLInputSource.cpp @@ -93,6 +93,11 @@ void SDLInputSource::UpdateSettings(SettingsInterface& si) } } +void SDLInputSource::Shutdown() +{ + ShutdownSubsystem(); +} + void SDLInputSource::LoadSettings(SettingsInterface& si) { m_controller_enhanced_mode = si.GetBoolValue("InputSources", "SDLControllerEnhancedMode", false); @@ -145,6 +150,24 @@ void SDLInputSource::PollEvents() } } +std::vector> SDLInputSource::EnumerateDevices() +{ + std::vector> ret; + + for (const ControllerData& cd : m_controllers) + { + std::string id(StringUtil::StdStringFromFormat("SDL-%d", cd.player_id)); + + const char* name = SDL_GameControllerName(cd.game_controller); + if (name) + ret.emplace_back(std::move(id), name); + else + ret.emplace_back(std::move(id), "Unknown Device"); + } + + return ret; +} + std::optional SDLInputSource::ParseKeyString( const std::string_view& device, const std::string_view& binding) { @@ -391,6 +414,9 @@ bool SDLInputSource::OpenGameController(int index) Console.Warning("(SDLInputSource) Rumble is not supported on '%s'", SDL_GameControllerName(gcontroller)); m_controllers.push_back(std::move(cd)); + + const char* name = SDL_GameControllerName(cd.game_controller); + Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("SDL-%d", player_id), name ? name : "Unknown Device"); return true; } @@ -404,7 +430,11 @@ bool SDLInputSource::CloseGameController(int joystick_index) SDL_HapticClose(static_cast(it->haptic)); SDL_GameControllerClose(static_cast(it->game_controller)); + + const int player_id = it->player_id; m_controllers.erase(it); + + Host::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("SDL-%d", player_id)); return true; } diff --git a/pcsx2/Frontend/SDLInputSource.h b/pcsx2/Frontend/SDLInputSource.h index da4ab7ff72..ac473c1e28 100644 --- a/pcsx2/Frontend/SDLInputSource.h +++ b/pcsx2/Frontend/SDLInputSource.h @@ -34,6 +34,7 @@ public: void Shutdown() override; void PollEvents() override; + std::vector> EnumerateDevices() override; std::vector EnumerateMotors() override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override; diff --git a/pcsx2/Frontend/XInputSource.cpp b/pcsx2/Frontend/XInputSource.cpp index 0d394d08b3..d2186efff6 100644 --- a/pcsx2/Frontend/XInputSource.cpp +++ b/pcsx2/Frontend/XInputSource.cpp @@ -116,6 +116,12 @@ void XInputSource::UpdateSettings(SettingsInterface& si) void XInputSource::Shutdown() { + for (u32 i = 0; i < NUM_CONTROLLERS; i++) + { + if (m_controllers[i].connected) + HandleControllerDisconnection(i); + } + #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) if (m_xinput_module) { @@ -154,6 +160,22 @@ void XInputSource::PollEvents() } } +std::vector> XInputSource::EnumerateDevices() +{ + std::vector> ret; + + for (u32 i = 0; i < NUM_CONTROLLERS; i++) + { + if (!m_controllers[i].connected) + continue; + + ret.emplace_back(StringUtil::StdStringFromFormat("XInput-%u", i), + StringUtil::StdStringFromFormat("XInput Controller %u", i)); + } + + return ret; +} + std::optional XInputSource::ParseKeyString( const std::string_view& device, const std::string_view& binding) { @@ -276,12 +298,17 @@ void XInputSource::HandleControllerConnection(u32 index) cd.connected = true; cd.has_large_motor = caps.Vibration.wLeftMotorSpeed != 0; cd.has_small_motor = caps.Vibration.wRightMotorSpeed != 0; + + Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("XInput-%u", index), + StringUtil::StdStringFromFormat("XInput Controller %u", index)); } void XInputSource::HandleControllerDisconnection(u32 index) { Console.WriteLn("XInput controller %u disconnected.", index); m_controllers[index] = {}; + + Host::OnInputDeviceDisconnected(StringUtil::StdStringFromFormat("XInput-%u", index)); } void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state) diff --git a/pcsx2/Frontend/XInputSource.h b/pcsx2/Frontend/XInputSource.h index 57d35cab44..0d3ab3d6ec 100644 --- a/pcsx2/Frontend/XInputSource.h +++ b/pcsx2/Frontend/XInputSource.h @@ -34,6 +34,7 @@ public: void Shutdown() override; void PollEvents() override; + std::vector> EnumerateDevices() override; std::vector EnumerateMotors() override; void UpdateMotorState(InputBindingKey key, float intensity) override; void UpdateMotorState(InputBindingKey large_key, InputBindingKey small_key, float large_intensity, float small_intensity) override;