From 669cbacc29d02e02349521725f82aed512658a8f Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Tue, 27 Jan 2026 20:43:55 +0300 Subject: [PATCH] Moved mouse gyro to its own struct, adjusted mouse to gyro behavior to be more correct Moved mouse gyro to its own struct, adjusted mouse to gyro behavior to be more correct --- rpcs3/Input/pad_thread.cpp | 65 +++++++++++++++++--------- rpcs3/Input/pad_thread.h | 26 +++++++++++ rpcs3/rpcs3qt/gs_frame.cpp | 96 +++++++++++++++++--------------------- 3 files changed, 111 insertions(+), 76 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 83abc1fd6b..01ebdda0bb 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -34,13 +34,6 @@ LOG_CHANNEL(sys_log, "SYS"); -// Mouse-based motion sensor emulation state. -std::atomic g_mouse_gyro_rmb{false}; // Whether right mouse button is currently held (gyro active) -std::atomic g_mouse_gyro_dx{0}; // Accumulated mouse X delta -std::atomic g_mouse_gyro_dy{0}; // Accumulated mouse Y delta -std::atomic g_mouse_gyro_wheel{0}; // Accumulated mouse wheel delta -std::atomic g_mouse_gyro_reset{false}; // One-shot reset request on right mouse button release - extern void pad_state_notify_state_change(usz index, u32 state); extern bool is_input_allowed(); extern std::string g_input_config_override; @@ -88,6 +81,9 @@ void pad_thread::Init() { std::lock_guard lock(pad::g_pad_mutex); + // Reset mouse-based gyro state + m_mouse_gyro.clear(); + // Cache old settings if possible std::array pad_settings; for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads @@ -614,38 +610,33 @@ void pad_thread::operator()() // The Qt frontend accumulates deltas while RMB is held. if (Emu.IsRunning()) { - const bool reset = g_mouse_gyro_reset.exchange(false); + const bool reset = m_mouse_gyro.reset; - const s32 dx = g_mouse_gyro_dx.exchange(0); - const s32 dy = g_mouse_gyro_dy.exchange(0); - const s32 wh = g_mouse_gyro_wheel.exchange(0); + const s32 gyro_x = m_mouse_gyro.gyro_x; + const s32 gyro_y = m_mouse_gyro.gyro_y; + const s32 gyro_z = m_mouse_gyro.gyro_z; - if (dx || dy || wh || reset) + if (gyro_x || gyro_y || gyro_z || reset) { - const auto clamp_u16_0_1023 = [](s32 v) -> u16 - { - return static_cast(std::clamp(v, 0, 1023)); - }; + auto& pad = m_pads[0]; - for (const auto& pad : m_pads) + if (pad && pad->is_connected()) { - if (!pad || !pad->is_connected()) - continue; - if (reset) { // RMB released → reset motion pad->m_sensors[0].m_value = DEFAULT_MOTION_X; pad->m_sensors[1].m_value = DEFAULT_MOTION_Y; pad->m_sensors[2].m_value = DEFAULT_MOTION_Z; + m_mouse_gyro.clear(); } else { // RMB held → accumulate motion // Axes have been chosen as tested in Sly 4 minigames. Top-down view motion uses X/Z axes. - pad->m_sensors[0].m_value = clamp_u16_0_1023(static_cast(pad->m_sensors[0].m_value) + dx); // Mouse X → Motion X - pad->m_sensors[1].m_value = clamp_u16_0_1023(static_cast(pad->m_sensors[1].m_value) + wh); // Mouse Wheel → Motion Y - pad->m_sensors[2].m_value = clamp_u16_0_1023(static_cast(pad->m_sensors[2].m_value) + dy); // Mouse Y → Motion Z + pad->m_sensors[0].m_value = gyro_x; // Mouse X → Motion X + pad->m_sensors[1].m_value = gyro_y; // Mouse Wheel → Motion Y + pad->m_sensors[2].m_value = gyro_z; // Mouse Y → Motion Z } } } @@ -985,3 +976,31 @@ void pad_thread::open_home_menu() (result ? input_log.error : input_log.notice)("opened home menu with result %d", s32{result}); } } + +void pad_thread::mouse_gyro_rmb_down() +{ + m_mouse_gyro.rmb = true; +} + +void pad_thread::mouse_gyro_rmb_up() +{ + m_mouse_gyro.rmb = false; + m_mouse_gyro.reset = true; +} + +void pad_thread::mouse_gyro_set_xz(s32 off_x, s32 off_y) +{ + if (!m_mouse_gyro.rmb) + return; + + m_mouse_gyro.gyro_x = static_cast(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1)); + m_mouse_gyro.gyro_z = static_cast(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1)); +} + +void pad_thread::mouse_gyro_set_y(s32 steps) +{ + if (!m_mouse_gyro.rmb) + return; + + m_mouse_gyro.gyro_y = static_cast(std::clamp(m_mouse_gyro.gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1)); +} diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index 7b0e0b79fb..1619f32f5c 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -41,6 +41,11 @@ public: static auto constexpr thread_name = "Pad Thread"sv; + void mouse_gyro_rmb_down(); + void mouse_gyro_rmb_up(); + void mouse_gyro_set_xz(s32 off_x, s32 off_y); + void mouse_gyro_set_y(s32 steps); + protected: void Init(); void InitLddPad(u32 handle, const u32* port_status); @@ -67,6 +72,27 @@ private: bool m_resume_emulation_flag = false; bool m_ps_button_pressed = false; atomic_t m_home_menu_open = false; + + // Mouse-based motion sensor emulation state. + struct mouse_gyro_state + { + bool rmb = false; // Whether right mouse button is currently held (gyro active) + s32 gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center + s32 gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta + s32 gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center + bool reset = false; // One-shot reset request on right mouse button release + + void clear() + { + rmb = false; + gyro_x = DEFAULT_MOTION_X; + gyro_y = DEFAULT_MOTION_Y; + gyro_z = DEFAULT_MOTION_Z; + reset = false; + } + }; + + mouse_gyro_state m_mouse_gyro; }; namespace pad diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 902563ad2c..fb219578ce 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -19,6 +19,7 @@ #include "Emu/RSX/Overlays/overlay_message.h" #include "Emu/Io/interception.h" #include "Emu/Io/recording_config.h" +#include "Input/pad_thread.h" #include #include @@ -62,13 +63,6 @@ namespace pad extern atomic_t g_home_menu_requested; } -// Mouse-based motion sensor emulation state (defined in pad_thread) -extern std::atomic g_mouse_gyro_rmb; -extern std::atomic g_mouse_gyro_dx; -extern std::atomic g_mouse_gyro_dy; -extern std::atomic g_mouse_gyro_wheel; -extern std::atomic g_mouse_gyro_reset; - gs_frame::gs_frame(QScreen* screen, const QRect& geometry, const QIcon& appIcon, std::shared_ptr gui_settings, bool force_fullscreen) : QWindow() , m_initial_geometry(geometry) @@ -1226,67 +1220,63 @@ bool gs_frame::event(QEvent* ev) // Hardcoded mouse-based motion input. // Captures mouse events while the game window is focused. - // Accumulates deltas for motion sensor emulation when RMB is held. + // Updates motion sensor values via mouse position and mouse wheel while RMB is held. // Intentionally independent of chosen pad configuration. if (Emu.IsRunning()) { - switch (ev->type()) + if (auto* pad_thr = pad::get_pad_thread(true)) { - case QEvent::MouseButtonPress: - { - auto* e = static_cast(ev); - if (e->button() == Qt::RightButton) + switch (ev->type()) { - // Enable mouse-driven gyro emulation while RMB is held. - g_mouse_gyro_rmb.store(true); + case QEvent::MouseButtonPress: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Enable mouse-driven gyro emulation while RMB is held. + pad_thr->mouse_gyro_rmb_down(); + } + break; } - break; - } - case QEvent::MouseButtonRelease: - { - auto* e = static_cast(ev); - if (e->button() == Qt::RightButton) + case QEvent::MouseButtonRelease: { - // Disable gyro emulation and request a one-shot motion reset. - g_mouse_gyro_rmb.store(false); - g_mouse_gyro_reset.store(true); + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Disable gyro emulation and request a one-shot motion reset. + pad_thr->mouse_gyro_rmb_up(); + } + break; } - break; - } - case QEvent::MouseMove: - { - // Track relative mouse movement using a persistent last cursor position. - static QPoint last; - auto* e = static_cast(ev); - const QPoint cur = e->pos(); + case QEvent::MouseMove: + { + auto* e = static_cast(ev); - // Initialize reference position on first event. - if (last.isNull()) - last = cur; + // Track cursor offset from window center. + const QPoint center(width() / 2, height() / 2); + const QPoint cur = e->position().toPoint(); - const s32 dx = cur.x() - last.x(); - const s32 dy = cur.y() - last.y(); - last = cur; + const s32 off_x = cur.x() - center.x() + DEFAULT_MOTION_X; + const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z; - // Accumulate deltas while gyro emulation is active. - if (g_mouse_gyro_rmb.load()) - { - g_mouse_gyro_dx.fetch_add(dx); - g_mouse_gyro_dy.fetch_add(dy); + // Determine motion from relative mouse position while gyro emulation is active. + pad_thr->mouse_gyro_set_xz(off_x, off_y); + + break; } - break; - } - case QEvent::Wheel: - { - // Accumulate mouse wheel steps while gyro emulation is active. - auto* e = static_cast(ev); - if (g_mouse_gyro_rmb.load()) + case QEvent::Wheel: { + auto* e = static_cast(ev); + + // Track mouse wheels steps. const s32 steps = e->angleDelta().y() / 120; - g_mouse_gyro_wheel.fetch_add(steps); + + // Accumulate mouse wheel steps while gyro emulation is active. + pad_thr->mouse_gyro_set_y(steps); + + break; + } } - break; - } } }