InputManager: Support multiple mice via raw input

Only on Windows for now.
This commit is contained in:
Stenzek
2024-08-23 22:31:59 +10:00
parent 8b3fd538ea
commit 9e3507e0f4
37 changed files with 1480 additions and 1040 deletions

View File

@ -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);

View File

@ -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<u8, NUM_MOTORS>;

View File

@ -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);

View File

@ -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

View File

@ -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)
{
}

View File

@ -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<Controller> Create(ControllerType type, u32 index);

View File

@ -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);
}

View File

@ -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

View File

@ -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<u32>(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<u8>(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<u32>(prev_pointer_index) < InputManager::MAX_SOFTWARE_CURSORS)
{
ImGuiManager::ClearSoftwareCursor(prev_pointer_index);

View File

@ -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;
};

View File

@ -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<float>(g_gpu_device->GetWindowWidth()),
static_cast<float>(g_gpu_device->GetWindowHeight()));
return true;
}
@ -322,7 +325,10 @@ void Host::UpdateDisplayWindow()
return;
}
ImGuiManager::WindowResized();
const float f_width = static_cast<float>(g_gpu_device->GetWindowWidth());
const float f_height = static_cast<float>(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<float>(g_gpu_device->GetWindowWidth());
const float f_height = static_cast<float>(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())

View File

@ -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,
};

View File

@ -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<u32>(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<u8>(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<u32>(prev_pointer_index) < InputManager::MAX_SOFTWARE_CURSORS)
{
ImGuiManager::ClearSoftwareCursor(prev_pointer_index);

View File

@ -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;

View File

@ -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),

View File

@ -100,7 +100,7 @@ public:
u32 GetButtonStateBits() const override;
std::optional<u32> 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

View File

@ -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<u8>(std::min<u32>(si.GetIntValue(section, "VibrationBias", 8), 255));

View File

@ -69,7 +69,7 @@ public:
u32 GetButtonStateBits() const override;
std::optional<u32> 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<u8, NUM_MOTORS>;

View File

@ -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,

View File

@ -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

View File

@ -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<MemoryCard> 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 = 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);
}
}

View File

@ -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();