From 330381c9393a261f22a62495956078c044502b95 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 23 Aug 2024 22:31:59 +1000 Subject: [PATCH] InputManager: Support multiple mice via raw input Only on Windows for now. --- src/core/analog_controller.cpp | 4 +- src/core/analog_controller.h | 2 +- src/core/analog_joystick.cpp | 4 +- src/core/analog_joystick.h | 2 +- src/core/controller.cpp | 2 +- src/core/controller.h | 2 +- src/core/digital_controller.cpp | 4 +- src/core/digital_controller.h | 2 +- src/core/guncon.cpp | 15 +- src/core/guncon.h | 3 +- src/core/host.cpp | 14 +- src/core/input_types.h | 4 +- src/core/justifier.cpp | 15 +- src/core/justifier.h | 3 +- src/core/negcon.cpp | 4 +- src/core/negcon.h | 2 +- src/core/negcon_rumble.cpp | 4 +- src/core/negcon_rumble.h | 2 +- src/core/playstation_mouse.cpp | 6 +- src/core/playstation_mouse.h | 2 +- src/core/system.cpp | 11 +- src/core/system.h | 3 + .../controllerbindingwidget_guncon.ui | 899 ++++++++++-------- .../controllerbindingwidget_justifier.ui | 899 ++++++++++-------- .../controllerbindingwidget_mouse.ui | 311 +++--- .../controllerbindingwidgets.cpp | 3 +- src/duckstation-qt/displaywidget.cpp | 26 +- src/duckstation-qt/inputbindingdialog.cpp | 2 + src/duckstation-qt/inputbindingwidgets.cpp | 14 +- src/duckstation-qt/qthost.cpp | 14 + src/duckstation-qt/qthost.h | 1 + src/util/imgui_manager.cpp | 17 +- src/util/imgui_manager.h | 2 +- src/util/input_manager.cpp | 113 ++- src/util/input_manager.h | 15 +- src/util/win32_raw_input_source.cpp | 125 ++- src/util/win32_raw_input_source.h | 7 +- 37 files changed, 1499 insertions(+), 1059 deletions(-) diff --git a/src/core/analog_controller.cpp b/src/core/analog_controller.cpp index c43e6fd8b..38133bb9f 100644 --- a/src/core/analog_controller.cpp +++ b/src/core/analog_controller.cpp @@ -885,9 +885,9 @@ const Controller::ControllerInfo AnalogController::INFO = {ControllerType::Analo s_settings, Controller::VibrationCapabilities::LargeSmallMotors}; -void AnalogController::LoadSettings(SettingsInterface& si, const char* section) +void AnalogController::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_force_analog_on_reset = si.GetBoolValue(section, "ForceAnalogOnReset", true); m_analog_dpad_in_digital_mode = si.GetBoolValue(section, "AnalogDPadInDigitalMode", true); m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f); diff --git a/src/core/analog_controller.h b/src/core/analog_controller.h index b744fbf79..e978f35e3 100644 --- a/src/core/analog_controller.h +++ b/src/core/analog_controller.h @@ -80,7 +80,7 @@ public: void ResetTransferState() override; bool Transfer(const u8 data_in, u8* data_out) override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; private: using MotorState = std::array; diff --git a/src/core/analog_joystick.cpp b/src/core/analog_joystick.cpp index f9dc7eb7d..4aa4804fc 100644 --- a/src/core/analog_joystick.cpp +++ b/src/core/analog_joystick.cpp @@ -413,9 +413,9 @@ const Controller::ControllerInfo AnalogJoystick::INFO = {ControllerType::AnalogJ s_settings, Controller::VibrationCapabilities::NoVibration}; -void AnalogJoystick::LoadSettings(SettingsInterface& si, const char* section) +void AnalogJoystick::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_analog_deadzone = std::clamp(si.GetFloatValue(section, "AnalogDeadzone", DEFAULT_STICK_DEADZONE), 0.0f, 1.0f); m_analog_sensitivity = std::clamp(si.GetFloatValue(section, "AnalogSensitivity", DEFAULT_STICK_SENSITIVITY), 0.01f, 3.0f); diff --git a/src/core/analog_joystick.h b/src/core/analog_joystick.h index a33deb3cd..456aa3c9d 100644 --- a/src/core/analog_joystick.h +++ b/src/core/analog_joystick.h @@ -77,7 +77,7 @@ public: void ResetTransferState() override; bool Transfer(const u8 data_in, u8* data_out) override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; private: enum class TransferState : u8 diff --git a/src/core/controller.cpp b/src/core/controller.cpp index 84edd0c91..ee266f621 100644 --- a/src/core/controller.cpp +++ b/src/core/controller.cpp @@ -91,7 +91,7 @@ u32 Controller::GetInputOverlayIconColor() const return 0xFFFFFFFFu; } -void Controller::LoadSettings(SettingsInterface& si, const char* section) +void Controller::LoadSettings(SettingsInterface& si, const char* section, bool initial) { } diff --git a/src/core/controller.h b/src/core/controller.h index 3499b9fc0..003695c00 100644 --- a/src/core/controller.h +++ b/src/core/controller.h @@ -93,7 +93,7 @@ public: virtual u32 GetInputOverlayIconColor() const; /// Loads/refreshes any per-controller settings. - virtual void LoadSettings(SettingsInterface& si, const char* section); + virtual void LoadSettings(SettingsInterface& si, const char* section, bool initial); /// Creates a new controller of the specified type. static std::unique_ptr Create(ControllerType type, u32 index); diff --git a/src/core/digital_controller.cpp b/src/core/digital_controller.cpp index 43377e47b..6445d0759 100644 --- a/src/core/digital_controller.cpp +++ b/src/core/digital_controller.cpp @@ -187,9 +187,9 @@ const Controller::ControllerInfo DigitalController::INFO = {ControllerType::Digi s_settings, Controller::VibrationCapabilities::NoVibration}; -void DigitalController::LoadSettings(SettingsInterface& si, const char* section) +void DigitalController::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_popn_controller_mode = si.GetBoolValue(section, "ForcePopnControllerMode", false); } diff --git a/src/core/digital_controller.h b/src/core/digital_controller.h index 48d631a8f..c4c689b59 100644 --- a/src/core/digital_controller.h +++ b/src/core/digital_controller.h @@ -50,7 +50,7 @@ public: void ResetTransferState() override; bool Transfer(const u8 data_in, u8* data_out) override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; private: enum class TransferState : u8 diff --git a/src/core/guncon.cpp b/src/core/guncon.cpp index 8f3702eda..5de8e5f27 100644 --- a/src/core/guncon.cpp +++ b/src/core/guncon.cpp @@ -207,8 +207,8 @@ bool GunCon::Transfer(const u8 data_in, u8* data_out) void GunCon::UpdatePosition() { float display_x, display_y; - const auto& [window_x, window_y] = - (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() : InputManager::GetPointerAbsolutePosition(0); + const auto& [window_x, window_y] = (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() : + InputManager::GetPointerAbsolutePosition(m_cursor_index); g_gpu->ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y); // are we within the active display area? @@ -245,7 +245,7 @@ bool GunCon::CanUseSoftwareCursor() const u32 GunCon::GetSoftwarePointerIndex() const { - return m_has_relative_binds ? (InputManager::MAX_POINTER_DEVICES + m_index) : 0; + return m_has_relative_binds ? (InputManager::MAX_POINTER_DEVICES + m_index) : m_cursor_index; } void GunCon::UpdateSoftwarePointerPosition() @@ -273,6 +273,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { } // clang-format off + {"Pointer", TRANSLATE_NOOP("GunCon", "Pointer/Aiming"), ICON_PF_MOUSE, static_cast(GunCon::Binding::ButtonCount), InputBindingInfo::Type::AbsolutePointer, GenericInputBinding::Unknown}, BUTTON("Trigger", TRANSLATE_NOOP("GunCon", "Trigger"), ICON_PF_CROSS, GunCon::Binding::Trigger, GenericInputBinding::R2), BUTTON("ShootOffscreen", TRANSLATE_NOOP("GunCon", "Shoot Offscreen"), nullptr, GunCon::Binding::ShootOffscreen, GenericInputBinding::L2), BUTTON("A", TRANSLATE_NOOP("GunCon", "A"), ICON_PF_BUTTON_A, GunCon::Binding::A, GenericInputBinding::Cross), @@ -306,9 +307,9 @@ const Controller::ControllerInfo GunCon::INFO = { ControllerType::GunCon, "GunCon", TRANSLATE_NOOP("ControllerType", "GunCon"), nullptr, s_binding_info, s_settings, Controller::VibrationCapabilities::NoVibration}; -void GunCon::LoadSettings(SettingsInterface& si, const char* section) +void GunCon::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_x_scale = si.GetFloatValue(section, "XScale", 1.0f); @@ -334,13 +335,15 @@ void GunCon::LoadSettings(SettingsInterface& si, const char* section) m_has_relative_binds = (si.ContainsValue(section, "RelativeLeft") || si.ContainsValue(section, "RelativeRight") || si.ContainsValue(section, "RelativeUp") || si.ContainsValue(section, "RelativeDown")); + m_cursor_index = + static_cast(InputManager::GetIndexFromPointerBinding(si.GetStringValue(section, "Pointer")).value_or(0)); const s32 new_pointer_index = GetSoftwarePointerIndex(); if (prev_pointer_index != new_pointer_index || m_cursor_path != cursor_path || m_cursor_scale != cursor_scale || m_cursor_color != cursor_color) { - if (prev_pointer_index != new_pointer_index && + if (!initial && prev_pointer_index != new_pointer_index && static_cast(prev_pointer_index) < InputManager::MAX_SOFTWARE_CURSORS) { ImGuiManager::ClearSoftwareCursor(prev_pointer_index); diff --git a/src/core/guncon.h b/src/core/guncon.h index 3a596a7b2..ed8a5ebab 100644 --- a/src/core/guncon.h +++ b/src/core/guncon.h @@ -37,7 +37,7 @@ public: void Reset() override; bool DoState(StateWrapper& sw, bool apply_input_state) override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; float GetBindState(u32 index) const override; void SetBindState(u32 index, float value) override; @@ -80,6 +80,7 @@ private: u16 m_position_y = 0; bool m_shoot_offscreen = false; bool m_has_relative_binds = false; + u8 m_cursor_index = 0; TransferState m_transfer_state = TransferState::Idle; }; diff --git a/src/core/host.cpp b/src/core/host.cpp index ce55996d6..2069f2044 100644 --- a/src/core/host.cpp +++ b/src/core/host.cpp @@ -12,6 +12,7 @@ #include "util/gpu_device.h" #include "util/imgui_manager.h" +#include "util/input_manager.h" #include "common/assert.h" #include "common/error.h" @@ -308,6 +309,8 @@ bool Host::CreateGPUDevice(RenderAPI api, Error* error) return false; } + InputManager::SetDisplayWindowSize(static_cast(g_gpu_device->GetWindowWidth()), + static_cast(g_gpu_device->GetWindowHeight())); return true; } @@ -322,7 +325,10 @@ void Host::UpdateDisplayWindow() return; } - ImGuiManager::WindowResized(); + const float f_width = static_cast(g_gpu_device->GetWindowWidth()); + const float f_height = static_cast(g_gpu_device->GetWindowHeight()); + ImGuiManager::WindowResized(f_width, f_height); + InputManager::SetDisplayWindowSize(f_width, f_height); if (System::IsValid()) { @@ -343,7 +349,11 @@ void Host::ResizeDisplayWindow(s32 width, s32 height, float scale) DEV_LOG("Display window resized to {}x{}", width, height); g_gpu_device->ResizeWindow(width, height, scale); - ImGuiManager::WindowResized(); + + const float f_width = static_cast(g_gpu_device->GetWindowWidth()); + const float f_height = static_cast(g_gpu_device->GetWindowHeight()); + ImGuiManager::WindowResized(f_width, f_height); + InputManager::SetDisplayWindowSize(f_width, f_height); // If we're paused, re-present the current frame at the new window size. if (System::IsValid()) diff --git a/src/core/input_types.h b/src/core/input_types.h index 8e8957454..41b72cca8 100644 --- a/src/core/input_types.h +++ b/src/core/input_types.h @@ -15,7 +15,8 @@ struct InputBindingInfo Axis, HalfAxis, Motor, - Pointer, // Receive relative mouse movement events, bind_index is offset by the axis. + Pointer, // Receive relative mouse movement events, bind_index is offset by the axis. + AbsolutePointer, // Allows selection of specific pointers, but defaults to the first. Macro, }; @@ -68,4 +69,3 @@ enum class GenericInputBinding : u8 Count, }; - diff --git a/src/core/justifier.cpp b/src/core/justifier.cpp index 0332d7cbe..d216bc39e 100644 --- a/src/core/justifier.cpp +++ b/src/core/justifier.cpp @@ -213,8 +213,8 @@ void Justifier::UpdatePosition() } float display_x, display_y; - const auto [window_x, window_y] = - (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() : InputManager::GetPointerAbsolutePosition(0); + const auto [window_x, window_y] = (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() : + InputManager::GetPointerAbsolutePosition(m_cursor_index); g_gpu->ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y); // are we within the active display area? @@ -308,7 +308,7 @@ bool Justifier::CanUseSoftwareCursor() const u32 Justifier::GetSoftwarePointerIndex() const { - return m_has_relative_binds ? (InputManager::MAX_POINTER_DEVICES + m_index) : 0; + return m_has_relative_binds ? (InputManager::MAX_POINTER_DEVICES + m_index) : m_cursor_index; } void Justifier::UpdateSoftwarePointerPosition() @@ -336,6 +336,7 @@ static const Controller::ControllerBindingInfo s_binding_info[] = { } // clang-format off + {"Pointer", TRANSLATE_NOOP("Justifier", "Pointer/Aiming"), ICON_PF_MOUSE, static_cast(Justifier::Binding::ButtonCount), InputBindingInfo::Type::AbsolutePointer, GenericInputBinding::Unknown}, BUTTON("Trigger", TRANSLATE_NOOP("Justifier", "Trigger"), ICON_PF_CROSS, Justifier::Binding::Trigger, GenericInputBinding::R2), BUTTON("ShootOffscreen", TRANSLATE_NOOP("Justifier", "Shoot Offscreen"), nullptr, Justifier::Binding::ShootOffscreen, GenericInputBinding::L2), BUTTON("Start", TRANSLATE_NOOP("Justifier", "Start"), ICON_PF_START, Justifier::Binding::Start, GenericInputBinding::Cross), @@ -395,9 +396,9 @@ const Controller::ControllerInfo Justifier::INFO = {ControllerType::Justifier, s_settings, Controller::VibrationCapabilities::NoVibration}; -void Justifier::LoadSettings(SettingsInterface& si, const char* section) +void Justifier::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_x_scale = si.GetFloatValue(section, "XScale", 1.0f); @@ -423,13 +424,15 @@ void Justifier::LoadSettings(SettingsInterface& si, const char* section) m_has_relative_binds = (si.ContainsValue(section, "RelativeLeft") || si.ContainsValue(section, "RelativeRight") || si.ContainsValue(section, "RelativeUp") || si.ContainsValue(section, "RelativeDown")); + m_cursor_index = + static_cast(InputManager::GetIndexFromPointerBinding(si.GetStringValue(section, "Pointer")).value_or(0)); const s32 new_pointer_index = GetSoftwarePointerIndex(); if (prev_pointer_index != new_pointer_index || m_cursor_path != cursor_path || m_cursor_scale != cursor_scale || m_cursor_color != cursor_color) { - if (prev_pointer_index != new_pointer_index && + if (!initial && prev_pointer_index != new_pointer_index && static_cast(prev_pointer_index) < InputManager::MAX_SOFTWARE_CURSORS) { ImGuiManager::ClearSoftwareCursor(prev_pointer_index); diff --git a/src/core/justifier.h b/src/core/justifier.h index fff679c16..6d09f8af5 100644 --- a/src/core/justifier.h +++ b/src/core/justifier.h @@ -40,7 +40,7 @@ public: void Reset() override; bool DoState(StateWrapper& sw, bool apply_input_state) override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; float GetBindState(u32 index) const override; void SetBindState(u32 index, float value) override; @@ -100,6 +100,7 @@ private: TimingEvent m_irq_event; bool m_has_relative_binds = false; + u8 m_cursor_index = 0; float m_relative_pos[4] = {}; std::string m_cursor_path; diff --git a/src/core/negcon.cpp b/src/core/negcon.cpp index 66e27c904..515fea317 100644 --- a/src/core/negcon.cpp +++ b/src/core/negcon.cpp @@ -345,9 +345,9 @@ const Controller::ControllerInfo NeGcon::INFO = { ControllerType::NeGcon, "NeGcon", TRANSLATE_NOOP("ControllerType", "NeGcon"), ICON_PF_GAMEPAD, s_binding_info, s_settings, Controller::VibrationCapabilities::NoVibration}; -void NeGcon::LoadSettings(SettingsInterface& si, const char* section) +void NeGcon::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_steering_modifier = { .deadzone = si.GetFloatValue(section, "SteeringDeadzone", DEFAULT_STEERING_MODIFIER.deadzone), .saturation = si.GetFloatValue(section, "SteeringSaturation", DEFAULT_STEERING_MODIFIER.saturation), diff --git a/src/core/negcon.h b/src/core/negcon.h index e7fd61392..0b3e195a8 100644 --- a/src/core/negcon.h +++ b/src/core/negcon.h @@ -100,7 +100,7 @@ public: u32 GetButtonStateBits() const override; std::optional GetAnalogInputBytes() const override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; private: enum class TransferState : u8 diff --git a/src/core/negcon_rumble.cpp b/src/core/negcon_rumble.cpp index 6bad03bee..a8c95e10c 100644 --- a/src/core/negcon_rumble.cpp +++ b/src/core/negcon_rumble.cpp @@ -766,9 +766,9 @@ const Controller::ControllerInfo NeGconRumble::INFO = {ControllerType::NeGconRum s_settings, Controller::VibrationCapabilities::LargeSmallMotors}; -void NeGconRumble::LoadSettings(SettingsInterface& si, const char* section) +void NeGconRumble::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_steering_deadzone = si.GetFloatValue(section, "SteeringDeadzone", 0.10f); m_steering_sensitivity = si.GetFloatValue(section, "SteeringSensitivity", 1.00f); m_rumble_bias = static_cast(std::min(si.GetIntValue(section, "VibrationBias", 8), 255)); diff --git a/src/core/negcon_rumble.h b/src/core/negcon_rumble.h index 48f542fcd..32bfb32da 100644 --- a/src/core/negcon_rumble.h +++ b/src/core/negcon_rumble.h @@ -69,7 +69,7 @@ public: u32 GetButtonStateBits() const override; std::optional GetAnalogInputBytes() const override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; private: using MotorState = std::array; diff --git a/src/core/playstation_mouse.cpp b/src/core/playstation_mouse.cpp index 7ebd35ee7..efde92c7e 100644 --- a/src/core/playstation_mouse.cpp +++ b/src/core/playstation_mouse.cpp @@ -181,9 +181,9 @@ bool PlayStationMouse::Transfer(const u8 data_in, u8* data_out) } } -void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section) +void PlayStationMouse::LoadSettings(SettingsInterface& si, const char* section, bool initial) { - Controller::LoadSettings(si, section); + Controller::LoadSettings(si, section, initial); m_sensitivity_x = si.GetFloatValue(section, "SensitivityX", 1.0f); m_sensitivity_y = si.GetFloatValue(section, "SensitivityY", 1.0f); @@ -219,7 +219,7 @@ static const SettingInfo s_settings[] = { const Controller::ControllerInfo PlayStationMouse::INFO = {ControllerType::PlayStationMouse, "PlayStationMouse", - TRANSLATE_NOOP("ControllerType", "PlayStation Mouse"), + TRANSLATE_NOOP("ControllerType", "Mouse"), ICON_PF_MOUSE, s_binding_info, s_settings, diff --git a/src/core/playstation_mouse.h b/src/core/playstation_mouse.h index 4e53ce225..2583d0c75 100644 --- a/src/core/playstation_mouse.h +++ b/src/core/playstation_mouse.h @@ -39,7 +39,7 @@ public: void ResetTransferState() override; bool Transfer(const u8 data_in, u8* data_out) override; - void LoadSettings(SettingsInterface& si, const char* section) override; + void LoadSettings(SettingsInterface& si, const char* section, bool initial) override; private: enum class TransferState : u8 diff --git a/src/core/system.cpp b/src/core/system.cpp index df047a188..e5a445036 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -176,7 +176,6 @@ static void UpdateRunningGame(const std::string_view path, CDImage* image, bool static bool CheckForSBIFile(CDImage* image, Error* error); static void UpdateControllers(); -static void UpdateControllerSettings(); static void ResetControllers(); static void UpdatePerGameMemoryCards(); static std::unique_ptr GetMemoryCardForSlot(u32 slot, MemoryCardType type); @@ -1459,6 +1458,7 @@ void System::PauseSystem(bool paused) FullscreenUI::OnSystemPaused(); InputManager::PauseVibration(); + InputManager::UpdateHostMouseMode(); Achievements::OnSystemPaused(true); @@ -1478,6 +1478,8 @@ void System::PauseSystem(bool paused) { FullscreenUI::OnSystemResumed(); + InputManager::UpdateHostMouseMode(); + Achievements::OnSystemPaused(false); if (g_settings.inhibit_screensaver) @@ -1732,6 +1734,8 @@ bool System::BootSystem(SystemBootParameters parameters, Error* error) FullscreenUI::OnSystemStarted(); + InputManager::UpdateHostMouseMode(); + if (g_settings.inhibit_screensaver) PlatformMisc::SuspendScreensaver(); @@ -1891,6 +1895,7 @@ void System::DestroySystem() FullscreenUI::OnSystemDestroyed(); InputManager::PauseVibration(); + InputManager::UpdateHostMouseMode(); if (g_settings.inhibit_screensaver) PlatformMisc::ResumeScreensaver(); @@ -3548,7 +3553,7 @@ void System::UpdateControllers() std::unique_ptr controller = Controller::Create(type, i); if (controller) { - controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str()); + controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str(), true); Pad::SetController(i, std::move(controller)); } } @@ -3563,7 +3568,7 @@ void System::UpdateControllerSettings() { Controller* controller = Pad::GetController(i); if (controller) - controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str()); + controller->LoadSettings(*Host::GetSettingsInterface(), Controller::GetSettingsSection(i).c_str(), false); } } diff --git a/src/core/system.h b/src/core/system.h index 14e587159..3a776dc0c 100644 --- a/src/core/system.h +++ b/src/core/system.h @@ -247,6 +247,9 @@ void ReloadInputSources(); /// Reloads input bindings. void ReloadInputBindings(); +/// Reloads only controller settings. +void UpdateControllerSettings(); + bool BootSystem(SystemBootParameters parameters, Error* error); void PauseSystem(bool paused); void ResetSystem(); diff --git a/src/duckstation-qt/controllerbindingwidget_guncon.ui b/src/duckstation-qt/controllerbindingwidget_guncon.ui index 882b43e46..ddc01c654 100644 --- a/src/duckstation-qt/controllerbindingwidget_guncon.ui +++ b/src/duckstation-qt/controllerbindingwidget_guncon.ui @@ -6,8 +6,8 @@ 0 0 - 1010 - 418 + 1124 + 485 @@ -22,395 +22,229 @@ 400 - - - - - Trigger - - - - - - Fire Offscreen - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - Fire - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Side Buttons - - - - - - B - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - A - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - - - - Relative Aiming - - - - - - Down - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Left - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Up - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Right - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - - - - - - Qt::Orientation::Vertical + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Pointer Setup - - - 20 - 40 - + + + + + <html><head/><body><p>By default, lightguns will use the mouse pointer. To use the mouse, you <span style=" font-weight:700;">do not</span> need to configure any bindings apart from the trigger and buttons. Aiming only needs to be set when you want to use multiple mice.</p><p>If you want to use a controller, or lightgun which simulates a controller instead of a mouse, then you should bind it to Relative Aiming. Otherwise, Relative Aiming should be <span style=" font-weight:700;">left unbound</span>.</p></body></html> + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + true + + + + + + + + + + Relative Aiming - + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + - - + + Qt::Orientation::Vertical - 20 - 40 + 0 + 0 + + + + @@ -418,13 +252,13 @@ - 40 - 20 + 35 + 263 - + @@ -442,51 +276,301 @@ - :/controllers/guncon.svg + :/controllers/guncon.svg true - + Qt::Orientation::Horizontal - 40 - 20 + 36 + 263 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 - - - - Pointer Setup - - - - - - <p>By default, lightguns will use the mouse pointer. To use the mouse, you <strong>do not</strong> need to configure any bindings apart from the trigger and buttons.</p> - -<p>If you want to use a controller, or lightgun which simulates a controller instead of a mouse, then you should bind it to Relative Aiming. Otherwise, Relative Aiming should be <strong>left unbound</strong>.</p> + + + + + + Aiming + + + + 6 + + + 6 - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + 6 - - true + + 6 - - - - + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + Trigger + + + + + + Fire Offscreen + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + Fire + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + + + + Side Buttons + + + + + + B + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + A + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + @@ -498,7 +582,8 @@ - + + diff --git a/src/duckstation-qt/controllerbindingwidget_justifier.ui b/src/duckstation-qt/controllerbindingwidget_justifier.ui index 623619d76..7444d6cbd 100644 --- a/src/duckstation-qt/controllerbindingwidget_justifier.ui +++ b/src/duckstation-qt/controllerbindingwidget_justifier.ui @@ -6,8 +6,8 @@ 0 0 - 1010 - 418 + 1124 + 485 @@ -22,395 +22,229 @@ 400 - - - - - Trigger - - - - - - Fire Offscreen - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - Fire - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - - - - Qt::Orientation::Vertical - - - - 20 - 40 - - - - - - - - Side Buttons - - - - - - Back - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - Start - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 150 - 0 - - - - - 150 - 16777215 - - - - PushButton - - - - - - - - - - - - - Relative Aiming - - - - - - Down - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Left - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Up - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Right - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - - - - - - Qt::Orientation::Vertical + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Pointer Setup - - - 20 - 40 - + + + + + <html><head/><body><p>By default, lightguns will use the mouse pointer. To use the mouse, you <span style=" font-weight:700;">do not</span> need to configure any bindings apart from the trigger and buttons. Aiming only needs to be set when you want to use multiple mice.</p><p>If you want to use a controller, or lightgun which simulates a controller instead of a mouse, then you should bind it to Relative Aiming. Otherwise, Relative Aiming should be <span style=" font-weight:700;">left unbound</span>.</p></body></html> + + + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + + true + + + + + + + + + + Relative Aiming - + + + + + Down + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Up + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 100 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + - - + + Qt::Orientation::Vertical - 20 - 40 + 0 + 0 + + + + @@ -418,13 +252,13 @@ - 40 - 20 + 35 + 263 - + @@ -442,51 +276,301 @@ - :/controllers/justifier.svg + :/controllers/justifier.svg true - + Qt::Orientation::Horizontal - 40 - 20 + 36 + 263 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 - - - - Pointer Setup - - - - - - <p>By default, lightguns will use the mouse pointer. To use the mouse, you <strong>do not</strong> need to configure any bindings apart from the trigger and buttons.</p> - -<p>If you want to use a controller, or lightgun which simulates a controller instead of a mouse, then you should bind it to Relative Aiming. Otherwise, Relative Aiming should be <strong>left unbound</strong>.</p> + + + + + + Aiming + + + + 6 + + + 6 - - Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop + + 6 - - true + + 6 - - - - + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + Trigger + + + + + + Fire Offscreen + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + Fire + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + + + + Side Buttons + + + + + + Back + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + Start + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 0 + 0 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + @@ -498,7 +582,8 @@ - + + diff --git a/src/duckstation-qt/controllerbindingwidget_mouse.ui b/src/duckstation-qt/controllerbindingwidget_mouse.ui index e4e8a8019..04a0e497c 100644 --- a/src/duckstation-qt/controllerbindingwidget_mouse.ui +++ b/src/duckstation-qt/controllerbindingwidget_mouse.ui @@ -7,7 +7,7 @@ 0 0 1100 - 534 + 567 @@ -29,111 +29,23 @@ - - - - - 0 - 0 - + + + + Qt::Orientation::Horizontal - + - 0 - 0 + 40 + 20 - - Buttons - - - - - - Right - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - - - Left - - - - 6 - - - 6 - - - 6 - - - 6 - - - - - - 100 - 0 - - - - - 100 - 16777215 - - - - PushButton - - - - - - - - + - + - Qt::Horizontal + Qt::Orientation::Horizontal @@ -143,7 +55,7 @@ - + @@ -155,30 +67,17 @@ - :/controllers/mouse.svg + :/controllers/mouse.svg false - - + + - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Qt::Vertical + Qt::Orientation::Vertical @@ -188,6 +87,181 @@ + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + Pointer + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + PushButton + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Buttons + + + + + + Left + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + Right + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 150 + 0 + + + + + 100 + 16777215 + + + + PushButton + + + + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + @@ -198,7 +272,8 @@ - + + diff --git a/src/duckstation-qt/controllerbindingwidgets.cpp b/src/duckstation-qt/controllerbindingwidgets.cpp index 21b20fd14..10cb21e59 100644 --- a/src/duckstation-qt/controllerbindingwidgets.cpp +++ b/src/duckstation-qt/controllerbindingwidgets.cpp @@ -478,7 +478,8 @@ void ControllerBindingWidget::bindBindingWidgets(QWidget* parent) for (const Controller::ControllerBindingInfo& bi : m_controller_info->bindings) { if (bi.type == InputBindingInfo::Type::Axis || bi.type == InputBindingInfo::Type::HalfAxis || - bi.type == InputBindingInfo::Type::Button || bi.type == InputBindingInfo::Type::Pointer) + bi.type == InputBindingInfo::Type::Button || bi.type == InputBindingInfo::Type::Pointer || + bi.type == InputBindingInfo::Type::AbsolutePointer) { InputBindingWidget* widget = parent->findChild(QString::fromUtf8(bi.name)); if (!widget) diff --git a/src/duckstation-qt/displaywidget.cpp b/src/duckstation-qt/displaywidget.cpp index f9e5be2e1..f5e7ed19a 100644 --- a/src/duckstation-qt/displaywidget.cpp +++ b/src/duckstation-qt/displaywidget.cpp @@ -100,11 +100,9 @@ void DisplayWidget::updateRelativeMode(bool enabled) if (enabled) { + m_relative_mouse_enabled = true; #ifdef _WIN32 - m_relative_mouse_enabled = !clip_cursor; m_clip_mouse_enabled = clip_cursor; -#else - m_relative_mouse_enabled = true; #endif m_relative_mouse_start_pos = QCursor::pos(); updateCenterPos(); @@ -263,12 +261,10 @@ bool DisplayWidget::event(QEvent* event) case QEvent::MouseMove: { - const QMouseEvent* mouse_event = static_cast(event); - if (!m_relative_mouse_enabled) { const qreal dpr = QtUtils::GetDevicePixelRatioForWidget(this); - const QPoint mouse_pos = mouse_event->pos(); + const QPoint mouse_pos = static_cast(event)->pos(); const float scaled_x = static_cast(static_cast(mouse_pos.x()) * dpr); const float scaled_y = static_cast(static_cast(mouse_pos.y()) * dpr); @@ -298,10 +294,13 @@ bool DisplayWidget::event(QEvent* event) } #endif - if (dx != 0.0f) - InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::X, dx); - if (dy != 0.0f) - InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::Y, dy); + if (!InputManager::IsUsingRawInput()) + { + if (dx != 0.0f) + InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::X, dx); + if (dy != 0.0f) + InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::Y, dy); + } } return true; @@ -311,8 +310,11 @@ bool DisplayWidget::event(QEvent* event) case QEvent::MouseButtonDblClick: case QEvent::MouseButtonRelease: { - const u32 button_index = CountTrailingZeros(static_cast(static_cast(event)->button())); - emit windowMouseButtonEvent(static_cast(button_index), event->type() != QEvent::MouseButtonRelease); + if (!m_relative_mouse_enabled || !InputManager::IsUsingRawInput()) + { + const u32 button_index = CountTrailingZeros(static_cast(static_cast(event)->button())); + emit windowMouseButtonEvent(static_cast(button_index), event->type() != QEvent::MouseButtonRelease); + } // don't toggle fullscreen when we're bound.. that wouldn't end well. if (event->type() == QEvent::MouseButtonDblClick && diff --git a/src/duckstation-qt/inputbindingdialog.cpp b/src/duckstation-qt/inputbindingdialog.cpp index b74bf3573..52a8ab31d 100644 --- a/src/duckstation-qt/inputbindingdialog.cpp +++ b/src/duckstation-qt/inputbindingdialog.cpp @@ -269,6 +269,8 @@ void InputBindingDialog::saveListToSettings() else Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); Host::CommitBaseSettingChanges(); + if (m_bind_type == InputBindingInfo::Type::Pointer || m_bind_type == InputBindingInfo::Type::AbsolutePointer) + g_emu_thread->updateControllerSettings(); g_emu_thread->reloadInputBindings(); } } diff --git a/src/duckstation-qt/inputbindingwidgets.cpp b/src/duckstation-qt/inputbindingwidgets.cpp index 636c65d35..03f34392e 100644 --- a/src/duckstation-qt/inputbindingwidgets.cpp +++ b/src/duckstation-qt/inputbindingwidgets.cpp @@ -44,8 +44,9 @@ InputBindingWidget::~InputBindingWidget() bool InputBindingWidget::isMouseMappingEnabled(SettingsInterface* sif) { - return sif ? sif->GetBoolValue("UI", "EnableMouseMapping", false) : - Host::GetBaseBoolSettingValue("UI", "EnableMouseMapping", false); + return (sif ? sif->GetBoolValue("UI", "EnableMouseMapping", false) : + Host::GetBaseBoolSettingValue("UI", "EnableMouseMapping", false)) && + !InputManager::IsUsingRawInput(); } void InputBindingWidget::initialize(SettingsInterface* sif, InputBindingInfo::Type bind_type, std::string section_name, @@ -100,7 +101,7 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event) const QEvent::Type event_type = event->type(); // if the key is being released, set the input - if (event_type == QEvent::KeyRelease || event_type == QEvent::MouseButtonRelease) + if (event_type == QEvent::KeyRelease || (event_type == QEvent::MouseButtonRelease && m_mouse_mapping_enabled)) { setNewBinding(); stopListeningForInput(); @@ -112,7 +113,8 @@ bool InputBindingWidget::eventFilter(QObject* watched, QEvent* event) m_new_bindings.push_back(InputManager::MakeHostKeyboardKey(QtUtils::KeyEventToCode(key_event))); return true; } - else if (event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick) + else if ((event_type == QEvent::MouseButtonPress || event_type == QEvent::MouseButtonDblClick) && + m_mouse_mapping_enabled) { // double clicks get triggered if we click bind, then click again quickly. const u32 button_index = CountTrailingZeros(static_cast(static_cast(event)->button())); @@ -225,6 +227,8 @@ void InputBindingWidget::setNewBinding() { Host::SetBaseStringSettingValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); Host::CommitBaseSettingChanges(); + if (m_bind_type == InputBindingInfo::Type::Pointer || m_bind_type == InputBindingInfo::Type::AbsolutePointer) + g_emu_thread->updateControllerSettings(); g_emu_thread->reloadInputBindings(); } } @@ -246,6 +250,8 @@ void InputBindingWidget::clearBinding() { Host::DeleteBaseSettingValue(m_section_name.c_str(), m_key_name.c_str()); Host::CommitBaseSettingChanges(); + if (m_bind_type == InputBindingInfo::Type::Pointer || m_bind_type == InputBindingInfo::Type::AbsolutePointer) + g_emu_thread->updateControllerSettings(); g_emu_thread->reloadInputBindings(); } reloadBinding(); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 58d479e9d..c5dfd43c8 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -753,6 +753,20 @@ void EmuThread::updateEmuFolders() EmuFolders::Update(); } +void EmuThread::updateControllerSettings() +{ + if (!isOnThread()) + { + QMetaObject::invokeMethod(this, &EmuThread::updateControllerSettings, Qt::QueuedConnection); + return; + } + + if (!System::IsValid()) + return; + + System::UpdateControllerSettings(); +} + void EmuThread::startFullscreenUI() { if (!isOnThread()) diff --git a/src/duckstation-qt/qthost.h b/src/duckstation-qt/qthost.h index f344ab3db..6648c67cf 100644 --- a/src/duckstation-qt/qthost.h +++ b/src/duckstation-qt/qthost.h @@ -159,6 +159,7 @@ public Q_SLOTS: void applySettings(bool display_osd_messages = false); void reloadGameSettings(bool display_osd_messages = false); void updateEmuFolders(); + void updateControllerSettings(); void reloadInputSources(); void reloadInputBindings(); void reloadInputDevices(); diff --git a/src/util/imgui_manager.cpp b/src/util/imgui_manager.cpp index 03a410df1..75e2cab5f 100644 --- a/src/util/imgui_manager.cpp +++ b/src/util/imgui_manager.cpp @@ -233,14 +233,11 @@ float ImGuiManager::GetWindowHeight() return s_window_height; } -void ImGuiManager::WindowResized() +void ImGuiManager::WindowResized(float width, float height) { - const u32 new_width = g_gpu_device ? g_gpu_device->GetWindowWidth() : 0; - const u32 new_height = g_gpu_device ? g_gpu_device->GetWindowHeight() : 0; - - s_window_width = static_cast(new_width); - s_window_height = static_cast(new_height); - ImGui::GetIO().DisplaySize = ImVec2(s_window_width, s_window_height); + s_window_width = width; + s_window_height = height; + ImGui::GetIO().DisplaySize = ImVec2(width, height); // Scale might have changed as a result of window resize. RequestScaleUpdate(); @@ -1053,7 +1050,7 @@ void ImGuiManager::DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair< void ImGuiManager::RenderSoftwareCursors() { // This one's okay to race, worst that happens is we render the wrong number of cursors for a frame. - const u32 pointer_count = InputManager::MAX_POINTER_DEVICES; + const u32 pointer_count = InputManager::GetPointerCount(); for (u32 i = 0; i < pointer_count; i++) DrawSoftwareCursor(s_software_cursors[i], InputManager::GetPointerAbsolutePosition(i)); @@ -1076,8 +1073,8 @@ void ImGuiManager::SetSoftwareCursor(u32 index, std::string image_path, float im UpdateSoftwareCursorTexture(index); // Hide the system cursor when we activate a software cursor. - if (is_hiding_or_showing && index == 0) - InputManager::UpdateHostMouseMode(); + if (is_hiding_or_showing && index <= InputManager::MAX_POINTER_DEVICES) + InputManager::UpdateRelativeMouseMode(); } bool ImGuiManager::HasSoftwareCursor(u32 index) diff --git a/src/util/imgui_manager.h b/src/util/imgui_manager.h index c7751f997..71dc1ea85 100644 --- a/src/util/imgui_manager.h +++ b/src/util/imgui_manager.h @@ -35,7 +35,7 @@ float GetWindowWidth(); float GetWindowHeight(); /// Updates internal state when the window is size. -void WindowResized(); +void WindowResized(float width, float height); /// Updates scaling of the on-screen elements. void RequestScaleUpdate(); diff --git a/src/util/input_manager.cpp b/src/util/input_manager.cpp index 0720e48b0..542ca4561 100644 --- a/src/util/input_manager.cpp +++ b/src/util/input_manager.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) +// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) #include "input_manager.h" #include "common/assert.h" @@ -108,6 +108,7 @@ static std::vector SplitChord(std::string_view binding); static bool SplitBinding(std::string_view binding, std::string_view* source, std::string_view* sub_binding); static void PrettifyInputBindingPart(std::string_view binding, SmallString& ret, bool& changed); static void AddBindings(const std::vector& bindings, const InputEventHandler& handler); +static void UpdatePointerCount(); static bool IsAxisHandler(const InputEventHandler& handler); static float ApplySingleBindingScale(float sensitivity, float deadzone, float value); @@ -180,11 +181,19 @@ static std::array(InputPointerAxis::Count)>, I static std::array(InputPointerAxis::Count)>, InputManager::MAX_POINTER_DEVICES> s_pointer_state; +static u32 s_pointer_count = 0; static std::array(InputPointerAxis::Count)> s_pointer_axis_scale; using PointerMoveCallback = std::function; static std::vector> s_pointer_move_callbacks; +// Window size, used for clamping the mouse position in raw input modes. +static std::array s_window_size = {}; +static bool s_relative_mouse_mode = false; +static bool s_relative_mouse_mode_active = false; +static bool s_hide_host_mouse_cursor = false; +static bool s_hide_host_mouse_cusor_active = false; + // ------------------------------------------------------------------------ // Binding Parsing // ------------------------------------------------------------------------ @@ -293,12 +302,12 @@ bool InputManager::ParseBindingAndGetSource(std::string_view binding, InputBindi std::string InputManager::ConvertInputBindingKeyToString(InputBindingInfo::Type binding_type, InputBindingKey key) { - if (binding_type == InputBindingInfo::Type::Pointer) + if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::AbsolutePointer) { // pointer and device bindings don't have a data part if (key.source_type == InputSourceType::Pointer) { - return GetPointerDeviceName(key.data); + return GetPointerDeviceName(key.source_index); } else if (key.source_type < InputSourceType::Count && s_input_sources[static_cast(key.source_type)]) { @@ -346,7 +355,7 @@ std::string InputManager::ConvertInputBindingKeysToString(InputBindingInfo::Type const InputBindingKey* keys, size_t num_keys) { // can't have a chord of devices/pointers - if (binding_type == InputBindingInfo::Type::Pointer) + if (binding_type == InputBindingInfo::Type::Pointer || binding_type == InputBindingInfo::Type::AbsolutePointer) { // so only take the first if (num_keys > 0) @@ -598,10 +607,10 @@ static std::array(InputSourceType::Count)> s_input #ifdef _WIN32 "DInput", "XInput", - "RawInput", #endif #ifndef __ANDROID__ "SDL", + "RawInput", #else "Android", #endif @@ -631,13 +640,13 @@ bool InputManager::GetInputSourceDefaultEnabled(InputSourceType type) case InputSourceType::XInput: return false; - case InputSourceType::RawInput: - return false; #endif #ifndef __ANDROID__ case InputSourceType::SDL: return true; + case InputSourceType::RawInput: + return false; #else case InputSourceType::Android: return true; @@ -844,6 +853,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, const std::string& sect break; case InputBindingInfo::Type::Pointer: + case InputBindingInfo::Type::AbsolutePointer: { auto cb = [pad_index, base = bi.bind_index](InputBindingKey key, float value) { if (!System::IsValid()) @@ -867,7 +877,7 @@ void InputManager::AddPadBindings(SettingsInterface& si, const std::string& sect if (!key.has_value()) continue; - s_pointer_move_callbacks.emplace_back(0, cb); + s_pointer_move_callbacks.emplace_back(key.value(), cb); } } } @@ -1171,7 +1181,7 @@ void InputManager::GenerateRelativeMouseEvents() { const bool system_running = System::IsRunning(); - for (u32 device = 0; device < MAX_POINTER_DEVICES; device++) + for (u32 device = 0; device < s_pointer_count; device++) { for (u32 axis = 0; axis < static_cast(static_cast(InputPointerAxis::Count)); axis++) { @@ -1208,14 +1218,44 @@ void InputManager::GenerateRelativeMouseEvents() } } +void InputManager::UpdatePointerCount() +{ + if (!IsUsingRawInput()) + { + s_pointer_count = 1; + return; + } + +#ifndef __ANDROID__ + InputSource* ris = GetInputSourceInterface(InputSourceType::RawInput); + DebugAssert(ris); + + s_pointer_count = 0; + for (const std::pair& it : ris->EnumerateDevices()) + { + if (it.first.starts_with("Pointer-")) + s_pointer_count++; + } +#endif +} + +u32 InputManager::GetPointerCount() +{ + return s_pointer_count; +} + std::pair InputManager::GetPointerAbsolutePosition(u32 index) { + DebugAssert(index < s_host_pointer_positions.size()); return std::make_pair(s_host_pointer_positions[index][static_cast(InputPointerAxis::X)], s_host_pointer_positions[index][static_cast(InputPointerAxis::Y)]); } void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y) { + if (index >= MAX_POINTER_DEVICES || s_relative_mouse_mode_active) [[unlikely]] + return; + const float dx = x - std::exchange(s_host_pointer_positions[index][static_cast(InputPointerAxis::X)], x); const float dy = y - std::exchange(s_host_pointer_positions[index][static_cast(InputPointerAxis::Y)], y); @@ -1236,18 +1276,26 @@ void InputManager::UpdatePointerAbsolutePosition(u32 index, float x, float y) void InputManager::UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input) { - if (raw_input != IsUsingRawInput()) + if (index >= MAX_POINTER_DEVICES || !s_relative_mouse_mode_active) return; s_host_pointer_positions[index][static_cast(axis)] += d; s_pointer_state[index][static_cast(axis)].delta.fetch_add(static_cast(d * 65536.0f), std::memory_order_release); - if (index == 0 && axis <= InputPointerAxis::Y) - ImGuiManager::UpdateMousePosition(s_host_pointer_positions[0][0], s_host_pointer_positions[0][1]); + // We need to clamp the position ourselves in relative mode. + if (axis <= InputPointerAxis::Y) + { + s_host_pointer_positions[index][static_cast(axis)] = + std::clamp(s_host_pointer_positions[index][static_cast(axis)], 0.0f, s_window_size[static_cast(axis)]); + + // Imgui also needs to be updated, since the absolute position won't be set above. + if (index == 0) + ImGuiManager::UpdateMousePosition(s_host_pointer_positions[0][0], s_host_pointer_positions[0][1]); + } } -void InputManager::UpdateHostMouseMode() +void InputManager::UpdateRelativeMouseMode() { // Check for relative mode bindings, and enable if there's anything using it. bool has_relative_mode_bindings = !s_pointer_move_callbacks.empty(); @@ -1265,8 +1313,29 @@ void InputManager::UpdateHostMouseMode() } } - const bool has_software_cursor = ImGuiManager::HasSoftwareCursor(0); - Host::SetMouseMode(has_relative_mode_bindings, has_relative_mode_bindings || has_software_cursor); + const bool hide_mouse_cursor = has_relative_mode_bindings || ImGuiManager::HasSoftwareCursor(0); + if (s_relative_mouse_mode == has_relative_mode_bindings && s_hide_host_mouse_cursor == hide_mouse_cursor) + return; + + s_relative_mouse_mode = has_relative_mode_bindings; + s_hide_host_mouse_cursor = hide_mouse_cursor; + UpdateRelativeMouseMode(); +} + +void InputManager::UpdateHostMouseMode() +{ + const bool can_change = System::IsRunning(); + const bool wanted_relative_mouse_mode = (s_relative_mouse_mode && can_change); + const bool wanted_hide_host_mouse_cursor = (s_hide_host_mouse_cursor && can_change); + if (wanted_relative_mouse_mode == s_relative_mouse_mode_active && + wanted_hide_host_mouse_cursor == s_hide_host_mouse_cusor_active) + { + return; + } + + s_relative_mouse_mode_active = wanted_relative_mouse_mode; + s_hide_host_mouse_cusor_active = wanted_hide_host_mouse_cursor; + Host::SetMouseMode(wanted_relative_mouse_mode, wanted_hide_host_mouse_cursor); } bool InputManager::IsUsingRawInput() @@ -1278,6 +1347,12 @@ bool InputManager::IsUsingRawInput() #endif } +void InputManager::SetDisplayWindowSize(float width, float height) +{ + s_window_size[0] = width; + s_window_size[1] = height; +} + void InputManager::SetDefaultSourceConfig(SettingsInterface& si) { si.ClearSection("InputSources"); @@ -1454,11 +1529,13 @@ std::vector InputManager::GetInputProfileNames() void InputManager::OnInputDeviceConnected(std::string_view identifier, std::string_view device_name) { + INFO_LOG("Device '{}' connected: '{}'", identifier, device_name); Host::OnInputDeviceConnected(identifier, device_name); } void InputManager::OnInputDeviceDisconnected(InputBindingKey key, std::string_view identifier) { + INFO_LOG("Device '{}' disconnected", identifier); Host::OnInputDeviceDisconnected(key, identifier); } @@ -1771,7 +1848,7 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind 1.0f); } - UpdateHostMouseMode(); + UpdateRelativeMouseMode(); } // ------------------------------------------------------------------------ @@ -1788,6 +1865,8 @@ bool InputManager::ReloadDevices() changed |= s_input_sources[i]->ReloadDevices(); } + UpdatePointerCount(); + return changed; } @@ -1972,4 +2051,6 @@ void InputManager::ReloadSources(SettingsInterface& si, std::unique_lock -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) +// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) #pragma once @@ -27,10 +27,10 @@ enum class InputSourceType : u32 #ifdef _WIN32 DInput, XInput, - RawInput, #endif #ifndef __ANDROID__ SDL, + RawInput, #else Android, #endif @@ -174,12 +174,12 @@ namespace InputManager { static constexpr double VIBRATION_UPDATE_INTERVAL_SECONDS = 0.5; // 500ms /// Maximum number of host mouse devices. -static constexpr u32 MAX_POINTER_DEVICES = 1; +static constexpr u32 MAX_POINTER_DEVICES = 8; static constexpr u32 MAX_POINTER_BUTTONS = 3; /// Maximum number of software cursors. We allocate an extra two for controllers with /// positioning data from the controller instead of a mouse. -static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_BUTTONS + 2; +static constexpr u32 MAX_SOFTWARE_CURSORS = MAX_POINTER_DEVICES + 2; /// Number of macro buttons per controller. static constexpr u32 NUM_MACRO_BUTTONS_PER_CONTROLLER = 4; @@ -311,6 +311,9 @@ void SetPadVibrationIntensity(u32 pad_index, float large_or_single_motor_intensi /// The pad vibration state will internally remain, so that when emulation is unpaused, the effect resumes. void PauseVibration(); +/// Returns the number of currently-connected pointer devices. +u32 GetPointerCount(); + /// Reads absolute pointer position. std::pair GetPointerAbsolutePosition(u32 index); @@ -322,6 +325,7 @@ void UpdatePointerAbsolutePosition(u32 index, float x, float y); void UpdatePointerRelativeDelta(u32 index, InputPointerAxis axis, float d, bool raw_input = false); /// Updates host mouse mode (relative/cursor hiding). +void UpdateRelativeMouseMode(); void UpdateHostMouseMode(); /// Sets the state of the specified macro button. @@ -330,6 +334,9 @@ void SetMacroButtonState(u32 pad, u32 index, bool state); /// Returns true if the raw input source is being used. bool IsUsingRawInput(); +/// Updates InputManager's view of the window size, used for clamping raw input coordinates. +void SetDisplayWindowSize(float width, float height); + /// Restores default configuration. void SetDefaultSourceConfig(SettingsInterface& si); diff --git a/src/util/win32_raw_input_source.cpp b/src/util/win32_raw_input_source.cpp index 1f38d03ee..0194fc27b 100644 --- a/src/util/win32_raw_input_source.cpp +++ b/src/util/win32_raw_input_source.cpp @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) #include "win32_raw_input_source.h" #include "common/assert.h" @@ -8,7 +8,9 @@ #include "core/host.h" #include "core/system.h" #include "input_manager.h" + #include +#include #include #include @@ -71,7 +73,11 @@ void Win32RawInputSource::PollEvents() std::vector> Win32RawInputSource::EnumerateDevices() { - return {}; + std::vector> ret; + for (u32 pointer_index = 0; pointer_index < static_cast(m_mice.size()); pointer_index++) + ret.emplace_back(InputManager::GetPointerDeviceName(pointer_index), GetMouseDeviceName(pointer_index)); + + return ret; } void Win32RawInputSource::UpdateMotorState(InputBindingKey key, float intensity) @@ -117,7 +123,8 @@ bool Win32RawInputSource::RegisterDummyClass() wc.hInstance = GetModuleHandleW(nullptr); wc.lpfnWndProc = DummyWindowProc; wc.lpszClassName = WINDOW_CLASS_NAME; - return (RegisterClassW(&wc) != 0); + s_window_class_registered = (RegisterClassW(&wc) != 0); + return s_window_class_registered; } bool Win32RawInputSource::CreateDummyWindow() @@ -160,12 +167,58 @@ LRESULT CALLBACK Win32RawInputSource::DummyWindowProc(HWND hwnd, UINT msg, WPARA return DefWindowProcW(hwnd, msg, wParam, lParam); } +std::string Win32RawInputSource::GetMouseDeviceName(u32 index) +{ +#if 0 + // Doesn't work for mice :( + const HANDLE device = m_mice[index].device; + std::wstring wdevice_name; + + UINT size = 0; + if (GetRawInputDeviceInfoW(device, RIDI_DEVICENAME, nullptr, &size) == static_cast(-1)) + goto error; + + wdevice_name.resize(size); + + UINT written_size = GetRawInputDeviceInfoW(device, RIDI_DEVICENAME, wdevice_name.data(), &size); + if (written_size == static_cast(-1)) + goto error; + + wdevice_name.resize(written_size); + if (wdevice_name.empty()) + goto error; + + const HANDLE hFile = CreateFileW(wdevice_name.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, + OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + goto error; + + wchar_t product_string[256]; + if (!HidD_GetProductString(hFile, product_string, sizeof(product_string))) + { + CloseHandle(hFile); + goto error; + } + + CloseHandle(hFile); + + return StringUtil::WideStringToUTF8String(product_string); + +error: + return "Unknown Device"; +#else + return fmt::format("Raw Input Pointer {}", index); +#endif +} + bool Win32RawInputSource::OpenDevices() { UINT num_devices = 0; if (GetRawInputDeviceList(nullptr, &num_devices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1) || num_devices == 0) + { return false; + } std::vector devices(num_devices); if (GetRawInputDeviceList(devices.data(), &num_devices, sizeof(RAWINPUTDEVICELIST)) == static_cast(-1)) @@ -174,28 +227,37 @@ bool Win32RawInputSource::OpenDevices() for (const RAWINPUTDEVICELIST& rid : devices) { -#if 0 - if (rid.dwType == RIM_TYPEKEYBOARD) - m_num_keyboards++; -#endif if (rid.dwType == RIM_TYPEMOUSE) - m_mice.push_back({rid.hDevice, 0u, 0, 0}); + { + // Make sure it's a real mouse with buttons. + // My goal with this was to stop my silly Corsair keyboard from showing up as a mouse... but it reports 32 + // buttons. + RID_DEVICE_INFO devinfo = { + .cbSize = sizeof(devinfo), + .dwType = RIM_TYPEMOUSE, + }; + UINT devinfo_size = sizeof(devinfo); + if (GetRawInputDeviceInfoW(rid.hDevice, RIDI_DEVICEINFO, &devinfo, &devinfo_size) <= 0 || + devinfo.mouse.dwNumberOfButtons == 0) + { + continue; + } + + m_mice.push_back({.device = rid.hDevice, .button_state = 0, .last_x = 0, .last_y = 0}); + } } - DEV_LOG("Found {} keyboards and {} mice", m_num_keyboards, m_mice.size()); + DEV_LOG("Found {} mice", m_mice.size()); - // Grab all keyboard/mouse input. - if (m_num_keyboards > 0) - { - const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD, 0, m_dummy_window}; - if (!RegisterRawInputDevices(&rrid, 1, sizeof(rrid))) - return false; - } + // Grab all mouse input. if (!m_mice.empty()) { const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE, 0, m_dummy_window}; if (!RegisterRawInputDevices(&rrid, 1, sizeof(rrid))) return false; + + for (u32 i = 0; i < static_cast(m_mice.size()); i++) + InputManager::OnInputDeviceConnected(InputManager::GetPointerDeviceName(i), GetMouseDeviceName(i)); } return true; @@ -203,17 +265,16 @@ bool Win32RawInputSource::OpenDevices() void Win32RawInputSource::CloseDevices() { - if (m_num_keyboards > 0) - { - const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_MOUSE, RIDEV_REMOVE, m_dummy_window}; - RegisterRawInputDevices(&rrid, 1, sizeof(rrid)); - m_num_keyboards = 0; - } - if (!m_mice.empty()) { const RAWINPUTDEVICE rrid = {HID_USAGE_PAGE_GENERIC, HID_USAGE_GENERIC_KEYBOARD, RIDEV_REMOVE, m_dummy_window}; RegisterRawInputDevices(&rrid, 1, sizeof(rrid)); + + for (u32 i = 0; i < static_cast(m_mice.size()); i++) + { + InputManager::OnInputDeviceDisconnected(InputManager::MakePointerAxisKey(i, InputPointerAxis::X), + InputManager::GetPointerDeviceName(i)); + } m_mice.clear(); } } @@ -222,9 +283,9 @@ bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event) { if (event->header.dwType == RIM_TYPEMOUSE) { - const u32 mouse_index = 0; - for (MouseState& state : m_mice) + for (u32 pointer_index = 0; pointer_index < static_cast(m_mice.size()); pointer_index++) { + MouseState& state = m_mice[pointer_index]; if (state.device != event->header.hDevice) continue; @@ -244,10 +305,6 @@ bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event) (rm.usButtonFlags & (rm.usButtonFlags ^ std::exchange(state.button_state, rm.usButtonFlags))) & ALL_BUTTON_MASKS; - // when the VM isn't running, allow events to run as normal (so we don't break the UI) - if (System::GetState() != System::State::Running) - return false; - while (button_mask != 0) { unsigned long bit_index; @@ -255,17 +312,17 @@ bool Win32RawInputSource::ProcessRawInputEvent(const RAWINPUT* event) // these are ordered down..up for each button const u32 button_number = bit_index >> 1; - const bool button_pressed = (bit_index & 1u) != 0; - InputManager::InvokeEvents(InputManager::MakePointerButtonKey(mouse_index, button_number), + const bool button_pressed = (bit_index & 1u) == 0; + InputManager::InvokeEvents(InputManager::MakePointerButtonKey(pointer_index, button_number), static_cast(button_pressed), GenericInputBinding::Unknown); button_mask &= ~(1u << bit_index); } if (dx != 0) - InputManager::UpdatePointerRelativeDelta(mouse_index, InputPointerAxis::X, static_cast(dx), true); + InputManager::UpdatePointerRelativeDelta(pointer_index, InputPointerAxis::X, static_cast(dx), true); if (dy != 0) - InputManager::UpdatePointerRelativeDelta(mouse_index, InputPointerAxis::Y, static_cast(dy), true); + InputManager::UpdatePointerRelativeDelta(pointer_index, InputPointerAxis::Y, static_cast(dy), true); return true; } diff --git a/src/util/win32_raw_input_source.h b/src/util/win32_raw_input_source.h index af7400e4e..1f65b8ee7 100644 --- a/src/util/win32_raw_input_source.h +++ b/src/util/win32_raw_input_source.h @@ -1,5 +1,5 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin +// SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0) #pragma once #include "common/windows_headers.h" @@ -46,6 +46,8 @@ private: static bool RegisterDummyClass(); static LRESULT CALLBACK DummyWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + static std::string GetMouseDeviceName(u32 index); + bool CreateDummyWindow(); void DestroyDummyWindow(); bool OpenDevices(); @@ -54,7 +56,6 @@ private: bool ProcessRawInputEvent(const RAWINPUT* event); HWND m_dummy_window = {}; - u32 m_num_keyboards = 0; std::vector m_mice; };