From 05531db92fe2d94cc166803c9cff54e872afb8db Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Mon, 26 Jan 2026 14:19:56 +0300 Subject: [PATCH 01/24] Add mouse-based gyro emulation This change adds a hardcoded mouse-based motion sensor emulation feature, inspired by how Cemu handles mouse-driven gyro input. While the game window is focused, holding the right mouse button enables gyro emulation: - Mouse X movement feeds Motion X - Mouse Y movement feeds Motion Z - Mouse Wheel feeds Motion Y The axis mapping and behavior were tested with the "Spark Runner" minigame in Sly Cooper: Thieves in Time and Bentley's Hackpack. In accordance with this minigame, a top-down view motion control scheme relies on the X/Z axes. While the right mouse button is being held, mouse deltas are captured via the Qt native event filter and accumulated in the frontend, then consumed by the pad thread. On right mouse button release, motion values are reset to the neutral center to avoid residual drift. This input path is intentionally independent of pad configuration and works even when a keyboard-only profile is selected. This implementation thus resolves issue #13883 by allowing motion-only gameplay without requiring a physical motion-capable controller. --- rpcs3/Input/pad_thread.cpp | 53 +++++++++++++++++++++++ rpcs3/rpcs3qt/gui_application.cpp | 71 ++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 1 deletion(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index afc5a73fce..4a2eb83530 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -34,6 +34,14 @@ LOG_CHANNEL(sys_log, "SYS"); +// Mouse-based motion sensor emulation state. +// Written from the Qt native event handler and consumed by pad_thread. +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; @@ -603,6 +611,51 @@ void pad_thread::operator()() apply_copilots(); + // Inject hardcoded mouse-based motion deltas into pad sensors for gyro emulation. + // The Qt frontend accumulates deltas while RMB is held. + if (Emu.IsRunning()) + { + const bool reset = g_mouse_gyro_reset.exchange(false, std::memory_order_relaxed); + + const s32 dx = g_mouse_gyro_dx.exchange(0, std::memory_order_relaxed); + const s32 dy = g_mouse_gyro_dy.exchange(0, std::memory_order_relaxed); + const s32 wh = g_mouse_gyro_wheel.exchange(0, std::memory_order_relaxed); + + if (dx || dy || wh || reset) + { + auto clamp_u16_0_1023 = [](s32 v) -> u16 + { + return static_cast(std::clamp(v, 0, 1023)); + }; + + for (const auto& pad : m_pads) + { + if (!pad || !pad->is_connected()) + continue; + + if (reset) + { + // RMB released → reset motion + // 512 is the neutral value within the 0-1023 motion range. + pad->m_sensors[0].m_value = 512; + pad->m_sensors[1].m_value = 512; + pad->m_sensors[2].m_value = 512; + } + 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 + } + } + } + } + if (Emu.IsRunning()) { update_pad_states(); diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 113f5a6ebb..26795e2b13 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -70,6 +70,13 @@ LOG_CHANNEL(gui_log, "GUI"); +// 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; + std::unique_ptr g_raw_mouse_handler; s32 gui_application::m_language_id = static_cast(CELL_SYSUTIL_LANG_ENGLISH_US); @@ -1356,7 +1363,9 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co if (eventType == "windows_generic_MSG") { - if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP || msg->message == WM_DEVICECHANGE)) + if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP || msg->message == WM_DEVICECHANGE + || msg->message == WM_MOUSEMOVE || msg->message == WM_MOUSEWHEEL + || msg->message == WM_LBUTTONDOWN || msg->message == WM_LBUTTONUP || msg->message == WM_RBUTTONDOWN || msg->message == WM_RBUTTONUP)) { if (msg->message == WM_DEVICECHANGE && (msg->wParam == DBT_DEVICEARRIVAL || msg->wParam == DBT_DEVICEREMOVECOMPLETE)) { @@ -1367,6 +1376,66 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co return false; } + // Hardcoded mouse-based motion input. + // Captures native mouse events while the game window is focused. + // Accumulates deltas for motion sensor emulation when RMB is held. + // Intentionally independent of chosen pad configuration. + if (Emu.IsRunning() && GetForegroundWindow() == msg->hwnd) + { + switch (msg->message) + { + case WM_RBUTTONDOWN: + // Enable mouse-driven gyro emulation while RMB is held. + g_mouse_gyro_rmb.store(true, std::memory_order_relaxed); + break; + + case WM_RBUTTONUP: + // Disable gyro emulation and request a one-shot motion reset. + g_mouse_gyro_rmb.store(false, std::memory_order_relaxed); + g_mouse_gyro_reset.store(true, std::memory_order_relaxed); + break; + + case WM_MOUSEMOVE: + { + // Track relative mouse movement using a persistent last cursor position. + static POINT last{}; + POINT cur; + cur.x = static_cast(LOWORD(msg->lParam)); + cur.y = static_cast(HIWORD(msg->lParam)); + + // Initialize reference position on first event. + if (last.x == 0 && last.y == 0) + last = cur; + + const s32 dx = cur.x - last.x; + const s32 dy = cur.y - last.y; + last = cur; + + // Accumulate deltas only while gyro emulation is active. + if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) + { + g_mouse_gyro_dx.fetch_add(dx, std::memory_order_relaxed); + g_mouse_gyro_dy.fetch_add(dy, std::memory_order_relaxed); + } + break; + } + + case WM_MOUSEWHEEL: + { + // Accumulate mouse wheel steps as motion input as well. + if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) + { + const s32 steps = GET_WHEEL_DELTA_WPARAM(msg->wParam) / WHEEL_DELTA; + g_mouse_gyro_wheel.fetch_add(steps, std::memory_order_relaxed); + } + break; + } + + default: + break; + } + } + if (auto* handler = g_fxo->try_get(); handler && handler->type == mouse_handler::raw) { static_cast(handler)->handle_native_event(*msg); From 25471966761f5428bdac4673b239943070f14831 Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:46:07 +0300 Subject: [PATCH 02/24] Commit suggestion Co-authored-by: Megamouse --- rpcs3/Input/pad_thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 4a2eb83530..8284424113 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -623,7 +623,7 @@ void pad_thread::operator()() if (dx || dy || wh || reset) { - auto clamp_u16_0_1023 = [](s32 v) -> u16 + const auto clamp_u16_0_1023 = [](s32 v) -> u16 { return static_cast(std::clamp(v, 0, 1023)); }; From 1508f404705e2f0c2ecf63cfbbc40f1f2c20e98b Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:46:18 +0300 Subject: [PATCH 03/24] Commit suggestion Co-authored-by: Megamouse --- rpcs3/Input/pad_thread.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 8284424113..7bf2735b0d 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -636,10 +636,9 @@ void pad_thread::operator()() if (reset) { // RMB released → reset motion - // 512 is the neutral value within the 0-1023 motion range. - pad->m_sensors[0].m_value = 512; - pad->m_sensors[1].m_value = 512; - pad->m_sensors[2].m_value = 512; + 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; } else { From fe51b19f496c231d316baa529c701cf0e08ac4fe Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Tue, 27 Jan 2026 12:28:24 +0300 Subject: [PATCH 04/24] Resolved moving to gs_frame Moved events to gs_frame --- rpcs3/rpcs3qt/gs_frame.cpp | 74 +++++++++++++++++++++++++++++++ rpcs3/rpcs3qt/gui_application.cpp | 71 +---------------------------- 2 files changed, 75 insertions(+), 70 deletions(-) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index db66ce68a4..012e3ba097 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -62,6 +62,13 @@ 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) @@ -1216,6 +1223,73 @@ bool gs_frame::event(QEvent* ev) // This will make the cursor visible again if it was hidden by the mouse idle timeout handle_cursor(visibility(), false, false, true); } + + // Hardcoded mouse-based motion input. + // Captures mouse events while the game window is focused. + // Accumulates deltas for motion sensor emulation when RMB is held. + // Intentionally independent of chosen pad configuration. + if (Emu.IsRunning()) + { + switch (ev->type()) + { + case QEvent::MouseButtonPress: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Enable mouse-driven gyro emulation while RMB is held. + g_mouse_gyro_rmb.store(true, std::memory_order_relaxed); + } + break; + } + case QEvent::MouseButtonRelease: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Disable gyro emulation and request a one-shot motion reset. + g_mouse_gyro_rmb.store(false, std::memory_order_relaxed); + g_mouse_gyro_reset.store(true, std::memory_order_relaxed); + } + 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(); + + // Initialize reference position on first event. + if (last.isNull()) + last = cur; + + const s32 dx = cur.x() - last.x(); + const s32 dy = cur.y() - last.y(); + last = cur; + + // Accumulate deltas while gyro emulation is active. + if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) + { + g_mouse_gyro_dx.fetch_add(dx, std::memory_order_relaxed); + g_mouse_gyro_dy.fetch_add(dy, std::memory_order_relaxed); + } + break; + } + case QEvent::Wheel: + { + // Accumulate mouse wheel steps while gyro emulation is active. + auto* e = static_cast(ev); + if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) + { + const s32 steps = e->angleDelta().y() / 120; + g_mouse_gyro_wheel.fetch_add(steps, std::memory_order_relaxed); + } + break; + } + } + } + return QWindow::event(ev); } diff --git a/rpcs3/rpcs3qt/gui_application.cpp b/rpcs3/rpcs3qt/gui_application.cpp index 26795e2b13..113f5a6ebb 100644 --- a/rpcs3/rpcs3qt/gui_application.cpp +++ b/rpcs3/rpcs3qt/gui_application.cpp @@ -70,13 +70,6 @@ LOG_CHANNEL(gui_log, "GUI"); -// 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; - std::unique_ptr g_raw_mouse_handler; s32 gui_application::m_language_id = static_cast(CELL_SYSUTIL_LANG_ENGLISH_US); @@ -1363,9 +1356,7 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co if (eventType == "windows_generic_MSG") { - if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP || msg->message == WM_DEVICECHANGE - || msg->message == WM_MOUSEMOVE || msg->message == WM_MOUSEWHEEL - || msg->message == WM_LBUTTONDOWN || msg->message == WM_LBUTTONUP || msg->message == WM_RBUTTONDOWN || msg->message == WM_RBUTTONUP)) + if (MSG* msg = static_cast(message); msg && (msg->message == WM_INPUT || msg->message == WM_KEYDOWN || msg->message == WM_KEYUP || msg->message == WM_DEVICECHANGE)) { if (msg->message == WM_DEVICECHANGE && (msg->wParam == DBT_DEVICEARRIVAL || msg->wParam == DBT_DEVICEREMOVECOMPLETE)) { @@ -1376,66 +1367,6 @@ bool gui_application::native_event_filter::nativeEventFilter([[maybe_unused]] co return false; } - // Hardcoded mouse-based motion input. - // Captures native mouse events while the game window is focused. - // Accumulates deltas for motion sensor emulation when RMB is held. - // Intentionally independent of chosen pad configuration. - if (Emu.IsRunning() && GetForegroundWindow() == msg->hwnd) - { - switch (msg->message) - { - case WM_RBUTTONDOWN: - // Enable mouse-driven gyro emulation while RMB is held. - g_mouse_gyro_rmb.store(true, std::memory_order_relaxed); - break; - - case WM_RBUTTONUP: - // Disable gyro emulation and request a one-shot motion reset. - g_mouse_gyro_rmb.store(false, std::memory_order_relaxed); - g_mouse_gyro_reset.store(true, std::memory_order_relaxed); - break; - - case WM_MOUSEMOVE: - { - // Track relative mouse movement using a persistent last cursor position. - static POINT last{}; - POINT cur; - cur.x = static_cast(LOWORD(msg->lParam)); - cur.y = static_cast(HIWORD(msg->lParam)); - - // Initialize reference position on first event. - if (last.x == 0 && last.y == 0) - last = cur; - - const s32 dx = cur.x - last.x; - const s32 dy = cur.y - last.y; - last = cur; - - // Accumulate deltas only while gyro emulation is active. - if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) - { - g_mouse_gyro_dx.fetch_add(dx, std::memory_order_relaxed); - g_mouse_gyro_dy.fetch_add(dy, std::memory_order_relaxed); - } - break; - } - - case WM_MOUSEWHEEL: - { - // Accumulate mouse wheel steps as motion input as well. - if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) - { - const s32 steps = GET_WHEEL_DELTA_WPARAM(msg->wParam) / WHEEL_DELTA; - g_mouse_gyro_wheel.fetch_add(steps, std::memory_order_relaxed); - } - break; - } - - default: - break; - } - } - if (auto* handler = g_fxo->try_get(); handler && handler->type == mouse_handler::raw) { static_cast(handler)->handle_native_event(*msg); From 886fc6282fbbbfd1067f670a6ed81d842b9ae679 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Tue, 27 Jan 2026 12:31:33 +0300 Subject: [PATCH 05/24] Style cleanup Merged some lines --- rpcs3/Input/pad_thread.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 7bf2735b0d..8b1fbdaf1b 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -35,7 +35,6 @@ LOG_CHANNEL(sys_log, "SYS"); // Mouse-based motion sensor emulation state. -// Written from the Qt native event handler and consumed by pad_thread. 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 @@ -644,12 +643,9 @@ void pad_thread::operator()() { // 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 = 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 } } } From 1b5cb9a4eb4fa169fd57f6233c174fe85ff05f30 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Tue, 27 Jan 2026 12:54:57 +0300 Subject: [PATCH 06/24] Adjusted unnecessary memory order Adjusted unnecessary memory order --- rpcs3/Input/pad_thread.cpp | 8 ++++---- rpcs3/rpcs3qt/gs_frame.cpp | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 8b1fbdaf1b..83abc1fd6b 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -614,11 +614,11 @@ 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, std::memory_order_relaxed); + const bool reset = g_mouse_gyro_reset.exchange(false); - const s32 dx = g_mouse_gyro_dx.exchange(0, std::memory_order_relaxed); - const s32 dy = g_mouse_gyro_dy.exchange(0, std::memory_order_relaxed); - const s32 wh = g_mouse_gyro_wheel.exchange(0, std::memory_order_relaxed); + 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); if (dx || dy || wh || reset) { diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 012e3ba097..902563ad2c 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1238,7 +1238,7 @@ bool gs_frame::event(QEvent* ev) if (e->button() == Qt::RightButton) { // Enable mouse-driven gyro emulation while RMB is held. - g_mouse_gyro_rmb.store(true, std::memory_order_relaxed); + g_mouse_gyro_rmb.store(true); } break; } @@ -1248,8 +1248,8 @@ bool gs_frame::event(QEvent* ev) if (e->button() == Qt::RightButton) { // Disable gyro emulation and request a one-shot motion reset. - g_mouse_gyro_rmb.store(false, std::memory_order_relaxed); - g_mouse_gyro_reset.store(true, std::memory_order_relaxed); + g_mouse_gyro_rmb.store(false); + g_mouse_gyro_reset.store(true); } break; } @@ -1269,10 +1269,10 @@ bool gs_frame::event(QEvent* ev) last = cur; // Accumulate deltas while gyro emulation is active. - if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) + if (g_mouse_gyro_rmb.load()) { - g_mouse_gyro_dx.fetch_add(dx, std::memory_order_relaxed); - g_mouse_gyro_dy.fetch_add(dy, std::memory_order_relaxed); + g_mouse_gyro_dx.fetch_add(dx); + g_mouse_gyro_dy.fetch_add(dy); } break; } @@ -1280,10 +1280,10 @@ bool gs_frame::event(QEvent* ev) { // Accumulate mouse wheel steps while gyro emulation is active. auto* e = static_cast(ev); - if (g_mouse_gyro_rmb.load(std::memory_order_relaxed)) + if (g_mouse_gyro_rmb.load()) { const s32 steps = e->angleDelta().y() / 120; - g_mouse_gyro_wheel.fetch_add(steps, std::memory_order_relaxed); + g_mouse_gyro_wheel.fetch_add(steps); } break; } From 669cbacc29d02e02349521725f82aed512658a8f Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Tue, 27 Jan 2026 20:43:55 +0300 Subject: [PATCH 07/24] 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; - } } } From dd92c7bf51f7f5c2b414a0340e1387ec42d43416 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Tue, 27 Jan 2026 21:10:02 +0300 Subject: [PATCH 08/24] Some comment fixes Some comment fixes --- rpcs3/Input/pad_thread.cpp | 5 +++-- rpcs3/rpcs3qt/gs_frame.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 01ebdda0bb..af73aa7a57 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -606,8 +606,8 @@ void pad_thread::operator()() apply_copilots(); - // Inject hardcoded mouse-based motion deltas into pad sensors for gyro emulation. - // The Qt frontend accumulates deltas while RMB is held. + // Inject mouse-based motion sensor values into pad sensors for gyro emulation. + // The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held. if (Emu.IsRunning()) { const bool reset = m_mouse_gyro.reset; @@ -618,6 +618,7 @@ void pad_thread::operator()() if (gyro_x || gyro_y || gyro_z || reset) { + // Mouse-based gyro input is intentionally bound to Player 1 only. auto& pad = m_pads[0]; if (pad && pad->is_connected()) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index fb219578ce..34eff7090b 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1268,7 +1268,7 @@ bool gs_frame::event(QEvent* ev) { auto* e = static_cast(ev); - // Track mouse wheels steps. + // Track mouse wheel steps. const s32 steps = e->angleDelta().y() / 120; // Accumulate mouse wheel steps while gyro emulation is active. From a873f46309860b3c07a67e54e94a7d78d15765b6 Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Fri, 30 Jan 2026 13:47:50 +0300 Subject: [PATCH 09/24] Update Co-authored-by: Megamouse --- rpcs3/Input/pad_thread.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index 1619f32f5c..f4d66cb24b 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -76,11 +76,11 @@ private: // 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 + std::atomic rmb {false}; // Whether right mouse button is currently held (gyro active) + std::atomic gyro_x {DEFAULT_MOTION_X}; // Accumulated from mouse X position relative to center + std::atomic gyro_y {DEFAULT_MOTION_Y}; // Accumulated from mouse wheel delta + std::atomic gyro_z {DEFAULT_MOTION_Z}; // Accumulated from mouse Y position relative to center + std::atomic reset {false}; // One-shot reset request on right mouse button release void clear() { From 7cfbea8a3a164a5ae01162009899aeba96778349 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 15:36:41 +0300 Subject: [PATCH 10/24] Moved mouse_gyro_state into its own class Moved mouse_gyro_state into its own class --- rpcs3/Input/mouse_gyro_state.cpp | 63 +++++++++++++++++++++++++++++ rpcs3/Input/mouse_gyro_state.h | 33 +++++++++++++++ rpcs3/Input/pad_thread.cpp | 69 ++------------------------------ rpcs3/Input/pad_thread.h | 27 +------------ rpcs3/rpcs3.vcxproj | 2 + rpcs3/rpcs3.vcxproj.filters | 6 +++ rpcs3/rpcs3qt/gs_frame.cpp | 8 ++-- 7 files changed, 114 insertions(+), 94 deletions(-) create mode 100644 rpcs3/Input/mouse_gyro_state.cpp create mode 100644 rpcs3/Input/mouse_gyro_state.h diff --git a/rpcs3/Input/mouse_gyro_state.cpp b/rpcs3/Input/mouse_gyro_state.cpp new file mode 100644 index 0000000000..05d9e50421 --- /dev/null +++ b/rpcs3/Input/mouse_gyro_state.cpp @@ -0,0 +1,63 @@ +#include "mouse_gyro_state.h" + +void mouse_gyro_state::clear() +{ + active = false; + reset = false; + gyro_x = DEFAULT_MOTION_X; + gyro_y = DEFAULT_MOTION_Y; + gyro_z = DEFAULT_MOTION_Z; +} + +void mouse_gyro_state::set_gyro_active() +{ + active = true; +} + +void mouse_gyro_state::set_gyro_reset() +{ + active = false; + reset = true; +} + +void mouse_gyro_state::set_gyro_xz(s32 off_x, s32 off_y) +{ + if (!active) + return; + + gyro_x = static_cast(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1)); + gyro_z = static_cast(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1)); +} + +void mouse_gyro_state::set_gyro_y(s32 steps) +{ + if (!active) + return; + + gyro_y = static_cast(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1)); +} + +void mouse_gyro_state::gyro_run(const std::shared_ptr& pad) +{ + if (!pad || !pad->is_connected()) + return; + + // Inject mouse-based motion sensor values into pad sensors for gyro emulation. + // The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held. + 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; + 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 = 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 + } +} diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_state.h new file mode 100644 index 0000000000..bd4c870ff4 --- /dev/null +++ b/rpcs3/Input/mouse_gyro_state.h @@ -0,0 +1,33 @@ +#pragma once + +#include "util/types.hpp" +#include "Emu/Io/pad_types.h" + +#include + +// Mouse-based motion sensor emulation state. +class mouse_gyro_state +{ +private: + bool active = false; // Whether right mouse button is currently held (gyro active) + bool reset = false; // One-shot reset request on right mouse button release + 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 + +public: + void clear(); + + bool is_gyro_active() const { return active; } + bool needs_gyro_reset() const { return reset; } + s32 get_gyro_x() const { return gyro_x; } + s32 get_gyro_y() const { return gyro_y; } + s32 get_gyro_z() const { return gyro_z; } + + void set_gyro_active(); + void set_gyro_reset(); + void set_gyro_xz(s32 off_x, s32 off_y); + void set_gyro_y(s32 steps); + + void gyro_run(const std::shared_ptr& pad); +}; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index af73aa7a57..27c1d33ee0 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -606,46 +606,13 @@ void pad_thread::operator()() apply_copilots(); - // Inject mouse-based motion sensor values into pad sensors for gyro emulation. - // The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held. - if (Emu.IsRunning()) - { - const bool reset = m_mouse_gyro.reset; - - 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 (gyro_x || gyro_y || gyro_z || reset) - { - // Mouse-based gyro input is intentionally bound to Player 1 only. - auto& pad = m_pads[0]; - - if (pad && pad->is_connected()) - { - 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 = 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 - } - } - } - } - if (Emu.IsRunning()) { update_pad_states(); + + // Inject mouse-based motion sensor values into pad sensors for gyro emulation. + // Intentionally bound to Player 1 only. + m_mouse_gyro.gyro_run(m_pads[0]); } m_info.now_connect = connected_devices + num_ldd_pad; @@ -977,31 +944,3 @@ 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 f4d66cb24b..894d93aaa5 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -5,6 +5,7 @@ #include "Emu/Io/pad_types.h" #include "Emu/Io/pad_config.h" #include "Emu/Io/pad_config_types.h" +#include "Input/mouse_gyro_state.h" #include "Utilities/mutex.h" #include @@ -41,10 +42,7 @@ 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); + mouse_gyro_state m_mouse_gyro; protected: void Init(); @@ -72,27 +70,6 @@ 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 - { - std::atomic rmb {false}; // Whether right mouse button is currently held (gyro active) - std::atomic gyro_x {DEFAULT_MOTION_X}; // Accumulated from mouse X position relative to center - std::atomic gyro_y {DEFAULT_MOTION_Y}; // Accumulated from mouse wheel delta - std::atomic gyro_z {DEFAULT_MOTION_Z}; // Accumulated from mouse Y position relative to center - std::atomic 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/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index 5fbf0491e9..cca31dd52b 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -194,6 +194,7 @@ + @@ -1079,6 +1080,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath) + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 100a9b1d8f..797b216a3f 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1272,6 +1272,9 @@ Generated Files\Release + + Io + @@ -1511,6 +1514,9 @@ Io\camera + + Io + diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 34eff7090b..061fb3c6cf 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1234,7 +1234,7 @@ bool gs_frame::event(QEvent* ev) if (e->button() == Qt::RightButton) { // Enable mouse-driven gyro emulation while RMB is held. - pad_thr->mouse_gyro_rmb_down(); + pad_thr->m_mouse_gyro.set_gyro_active(); } break; } @@ -1244,7 +1244,7 @@ bool gs_frame::event(QEvent* ev) if (e->button() == Qt::RightButton) { // Disable gyro emulation and request a one-shot motion reset. - pad_thr->mouse_gyro_rmb_up(); + pad_thr->m_mouse_gyro.set_gyro_reset(); } break; } @@ -1260,7 +1260,7 @@ bool gs_frame::event(QEvent* ev) const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z; // Determine motion from relative mouse position while gyro emulation is active. - pad_thr->mouse_gyro_set_xz(off_x, off_y); + pad_thr->m_mouse_gyro.set_gyro_xz(off_x, off_y); break; } @@ -1272,7 +1272,7 @@ bool gs_frame::event(QEvent* ev) const s32 steps = e->angleDelta().y() / 120; // Accumulate mouse wheel steps while gyro emulation is active. - pad_thr->mouse_gyro_set_y(steps); + pad_thr->m_mouse_gyro.set_gyro_y(steps); break; } From 8534edaef90ae8a3af8a32a1df79de4c3ea88161 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 15:56:58 +0300 Subject: [PATCH 11/24] Moved qt event part to class too Moved qt event part to class too --- rpcs3/Input/mouse_gyro_state.cpp | 66 ++++++++++++++++++++++++++++++++ rpcs3/Input/mouse_gyro_state.h | 4 +- rpcs3/Input/pad_thread.cpp | 3 +- rpcs3/rpcs3qt/gs_frame.cpp | 55 +------------------------- 4 files changed, 72 insertions(+), 56 deletions(-) diff --git a/rpcs3/Input/mouse_gyro_state.cpp b/rpcs3/Input/mouse_gyro_state.cpp index 05d9e50421..ed3c2d90cd 100644 --- a/rpcs3/Input/mouse_gyro_state.cpp +++ b/rpcs3/Input/mouse_gyro_state.cpp @@ -1,5 +1,12 @@ #include "mouse_gyro_state.h" +#include +#include +#include +#include + +#include + void mouse_gyro_state::clear() { active = false; @@ -37,6 +44,65 @@ void mouse_gyro_state::set_gyro_y(s32 steps) gyro_y = static_cast(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1)); } +void mouse_gyro_state::gyro_detect(QEvent* ev, const QWindow& win) +{ + // Hardcoded mouse-based motion input. + // Captures mouse events while the game window is focused. + // Updates motion sensor values via mouse position and mouse wheel while RMB is held. + // Intentionally independent of chosen pad configuration. + switch (ev->type()) + { + case QEvent::MouseButtonPress: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Enable mouse-driven gyro emulation while RMB is held. + set_gyro_active(); + } + break; + } + case QEvent::MouseButtonRelease: + { + auto* e = static_cast(ev); + if (e->button() == Qt::RightButton) + { + // Disable gyro emulation and request a one-shot motion reset. + set_gyro_reset(); + } + break; + } + case QEvent::MouseMove: + { + auto* e = static_cast(ev); + + // Track cursor offset from window center. + const QPoint center(win.width() / 2, win.height() / 2); + const QPoint cur = e->position().toPoint(); + + const s32 off_x = cur.x() - center.x() + DEFAULT_MOTION_X; + const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z; + + // Determine motion from relative mouse position while gyro emulation is active. + set_gyro_xz(off_x, off_y); + + break; + } + case QEvent::Wheel: + { + auto* e = static_cast(ev); + + // Track mouse wheel steps. + const s32 steps = e->angleDelta().y() / 120; + + // Accumulate mouse wheel steps while gyro emulation is active. + set_gyro_y(steps); + + break; + } + } +} + void mouse_gyro_state::gyro_run(const std::shared_ptr& pad) { if (!pad || !pad->is_connected()) diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_state.h index bd4c870ff4..21b52d5b9c 100644 --- a/rpcs3/Input/mouse_gyro_state.h +++ b/rpcs3/Input/mouse_gyro_state.h @@ -3,7 +3,8 @@ #include "util/types.hpp" #include "Emu/Io/pad_types.h" -#include +class QEvent; +class QWindow; // Mouse-based motion sensor emulation state. class mouse_gyro_state @@ -29,5 +30,6 @@ public: void set_gyro_xz(s32 off_x, s32 off_y); void set_gyro_y(s32 steps); + void gyro_detect(QEvent* ev, const QWindow& win); void gyro_run(const std::shared_ptr& pad); }; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 27c1d33ee0..7120992b52 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -612,7 +612,8 @@ void pad_thread::operator()() // Inject mouse-based motion sensor values into pad sensors for gyro emulation. // Intentionally bound to Player 1 only. - m_mouse_gyro.gyro_run(m_pads[0]); + auto& main_pad = m_pads[0]; + m_mouse_gyro.gyro_run(main_pad); } m_info.now_connect = connected_devices + num_ldd_pad; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 061fb3c6cf..b2d2622394 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1219,64 +1219,11 @@ bool gs_frame::event(QEvent* ev) } // Hardcoded mouse-based motion input. - // Captures mouse events while the game window is focused. - // Updates motion sensor values via mouse position and mouse wheel while RMB is held. - // Intentionally independent of chosen pad configuration. if (Emu.IsRunning()) { if (auto* pad_thr = pad::get_pad_thread(true)) { - switch (ev->type()) - { - case QEvent::MouseButtonPress: - { - auto* e = static_cast(ev); - if (e->button() == Qt::RightButton) - { - // Enable mouse-driven gyro emulation while RMB is held. - pad_thr->m_mouse_gyro.set_gyro_active(); - } - break; - } - case QEvent::MouseButtonRelease: - { - auto* e = static_cast(ev); - if (e->button() == Qt::RightButton) - { - // Disable gyro emulation and request a one-shot motion reset. - pad_thr->m_mouse_gyro.set_gyro_reset(); - } - break; - } - case QEvent::MouseMove: - { - auto* e = static_cast(ev); - - // Track cursor offset from window center. - const QPoint center(width() / 2, height() / 2); - const QPoint cur = e->position().toPoint(); - - const s32 off_x = cur.x() - center.x() + DEFAULT_MOTION_X; - const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z; - - // Determine motion from relative mouse position while gyro emulation is active. - pad_thr->m_mouse_gyro.set_gyro_xz(off_x, off_y); - - break; - } - case QEvent::Wheel: - { - auto* e = static_cast(ev); - - // Track mouse wheel steps. - const s32 steps = e->angleDelta().y() / 120; - - // Accumulate mouse wheel steps while gyro emulation is active. - pad_thr->m_mouse_gyro.set_gyro_y(steps); - - break; - } - } + pad_thr->m_mouse_gyro.gyro_detect(ev, *this); } } From 860ee81b545c9c708951a05445a6f874fd8db3d4 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 16:07:30 +0300 Subject: [PATCH 12/24] Made atomic_t Made atomic_t --- rpcs3/Input/mouse_gyro_state.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_state.h index 21b52d5b9c..bd8537da34 100644 --- a/rpcs3/Input/mouse_gyro_state.h +++ b/rpcs3/Input/mouse_gyro_state.h @@ -1,6 +1,7 @@ #pragma once #include "util/types.hpp" +#include "util/atomic.hpp" #include "Emu/Io/pad_types.h" class QEvent; @@ -10,11 +11,11 @@ class QWindow; class mouse_gyro_state { private: - bool active = false; // Whether right mouse button is currently held (gyro active) - bool reset = false; // One-shot reset request on right mouse button release - 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 + atomic_t active = false; // Whether right mouse button is currently held (gyro active) + atomic_t reset = false; // One-shot reset request on right mouse button release + atomic_t gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center + atomic_t gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta + atomic_t gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center public: void clear(); From 37ffbe8eb7102e0ef11a572e8ce1c95b7911d44d Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:36:23 +0300 Subject: [PATCH 13/24] Commit suggestion Commit suggestion Co-authored-by: Megamouse --- rpcs3/rpcs3qt/gs_frame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index b2d2622394..e93d08ae0a 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1218,7 +1218,7 @@ bool gs_frame::event(QEvent* ev) handle_cursor(visibility(), false, false, true); } - // Hardcoded mouse-based motion input. + // Mouse-based motion input. if (Emu.IsRunning()) { if (auto* pad_thr = pad::get_pad_thread(true)) From c711f24d7475f46e6a0e7a88a49b7b9a35a5a5eb Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:36:34 +0300 Subject: [PATCH 14/24] Commit suggestion Commit suggestion Co-authored-by: Megamouse --- rpcs3/Input/pad_thread.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 7120992b52..27c1d33ee0 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -612,8 +612,7 @@ void pad_thread::operator()() // Inject mouse-based motion sensor values into pad sensors for gyro emulation. // Intentionally bound to Player 1 only. - auto& main_pad = m_pads[0]; - m_mouse_gyro.gyro_run(main_pad); + m_mouse_gyro.gyro_run(m_pads[0]); } m_info.now_connect = connected_devices + num_ldd_pad; From ca11efceebf0706a11e38aab54f5785895e6df2f Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:36:45 +0300 Subject: [PATCH 15/24] Commit suggestion Commit suggestion Co-authored-by: Megamouse --- rpcs3/Input/mouse_gyro_state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_state.h index bd8537da34..8da17ae694 100644 --- a/rpcs3/Input/mouse_gyro_state.h +++ b/rpcs3/Input/mouse_gyro_state.h @@ -32,5 +32,5 @@ public: void set_gyro_y(s32 steps); void gyro_detect(QEvent* ev, const QWindow& win); - void gyro_run(const std::shared_ptr& pad); + void apply_gyro(const std::shared_ptr& pad); }; From c3c85bb3d1d7d147b896a7b09a578b6bbb46b855 Mon Sep 17 00:00:00 2001 From: Windsurf7 <70599421+Windsurf7@users.noreply.github.com> Date: Fri, 30 Jan 2026 16:36:55 +0300 Subject: [PATCH 16/24] Commit suggestion Commit suggestion Co-authored-by: Megamouse --- rpcs3/Input/mouse_gyro_state.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rpcs3/Input/mouse_gyro_state.cpp b/rpcs3/Input/mouse_gyro_state.cpp index ed3c2d90cd..fa082193aa 100644 --- a/rpcs3/Input/mouse_gyro_state.cpp +++ b/rpcs3/Input/mouse_gyro_state.cpp @@ -100,6 +100,10 @@ void mouse_gyro_state::gyro_detect(QEvent* ev, const QWindow& win) break; } + default: + { + break; + } } } From 673571b0b3ff5dca50d33b9a5845616bf8821d20 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 16:37:23 +0300 Subject: [PATCH 17/24] Commit suggestion Commit suggestion --- rpcs3/Input/mouse_gyro_state.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_state.h index bd8537da34..6385af185c 100644 --- a/rpcs3/Input/mouse_gyro_state.h +++ b/rpcs3/Input/mouse_gyro_state.h @@ -20,12 +20,6 @@ private: public: void clear(); - bool is_gyro_active() const { return active; } - bool needs_gyro_reset() const { return reset; } - s32 get_gyro_x() const { return gyro_x; } - s32 get_gyro_y() const { return gyro_y; } - s32 get_gyro_z() const { return gyro_z; } - void set_gyro_active(); void set_gyro_reset(); void set_gyro_xz(s32 off_x, s32 off_y); From 46a46f74d4969cc9f8607e382dad5a837ff7b75f Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 16:40:07 +0300 Subject: [PATCH 18/24] Commit suggestion Commit suggestion --- rpcs3/Input/mouse_gyro_state.cpp | 4 ++-- rpcs3/Input/mouse_gyro_state.h | 8 ++++---- rpcs3/Input/pad_thread.cpp | 2 +- rpcs3/rpcs3qt/gs_frame.cpp | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rpcs3/Input/mouse_gyro_state.cpp b/rpcs3/Input/mouse_gyro_state.cpp index fa082193aa..319ad56942 100644 --- a/rpcs3/Input/mouse_gyro_state.cpp +++ b/rpcs3/Input/mouse_gyro_state.cpp @@ -44,7 +44,7 @@ void mouse_gyro_state::set_gyro_y(s32 steps) gyro_y = static_cast(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1)); } -void mouse_gyro_state::gyro_detect(QEvent* ev, const QWindow& win) +void mouse_gyro_state::handle_event(QEvent* ev, const QWindow& win) { // Hardcoded mouse-based motion input. // Captures mouse events while the game window is focused. @@ -107,7 +107,7 @@ void mouse_gyro_state::gyro_detect(QEvent* ev, const QWindow& win) } } -void mouse_gyro_state::gyro_run(const std::shared_ptr& pad) +void mouse_gyro_state::apply_gyro(const std::shared_ptr& pad) { if (!pad || !pad->is_connected()) return; diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_state.h index 44ab25952f..f5001e57b6 100644 --- a/rpcs3/Input/mouse_gyro_state.h +++ b/rpcs3/Input/mouse_gyro_state.h @@ -17,14 +17,14 @@ private: atomic_t gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta atomic_t gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center -public: - void clear(); - void set_gyro_active(); void set_gyro_reset(); void set_gyro_xz(s32 off_x, s32 off_y); void set_gyro_y(s32 steps); - void gyro_detect(QEvent* ev, const QWindow& win); +public: + void clear(); + + void handle_event(QEvent* ev, const QWindow& win); void apply_gyro(const std::shared_ptr& pad); }; diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 27c1d33ee0..53d7f3f178 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -612,7 +612,7 @@ void pad_thread::operator()() // Inject mouse-based motion sensor values into pad sensors for gyro emulation. // Intentionally bound to Player 1 only. - m_mouse_gyro.gyro_run(m_pads[0]); + m_mouse_gyro.apply_gyro(m_pads[0]); } m_info.now_connect = connected_devices + num_ldd_pad; diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index e93d08ae0a..9d0ce9fbf8 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1223,7 +1223,7 @@ bool gs_frame::event(QEvent* ev) { if (auto* pad_thr = pad::get_pad_thread(true)) { - pad_thr->m_mouse_gyro.gyro_detect(ev, *this); + pad_thr->m_mouse_gyro.handle_event(ev, *this); } } From cab5b14e5cf94cc59eec452bdcb969000d967852 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 16:44:17 +0300 Subject: [PATCH 19/24] Changed class name Changed class name --- ...use_gyro_state.cpp => mouse_gyro_handler.cpp} | 16 ++++++++-------- .../{mouse_gyro_state.h => mouse_gyro_handler.h} | 2 +- rpcs3/Input/pad_thread.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) rename rpcs3/Input/{mouse_gyro_state.cpp => mouse_gyro_handler.cpp} (87%) rename rpcs3/Input/{mouse_gyro_state.h => mouse_gyro_handler.h} (97%) diff --git a/rpcs3/Input/mouse_gyro_state.cpp b/rpcs3/Input/mouse_gyro_handler.cpp similarity index 87% rename from rpcs3/Input/mouse_gyro_state.cpp rename to rpcs3/Input/mouse_gyro_handler.cpp index 319ad56942..d59878f1ec 100644 --- a/rpcs3/Input/mouse_gyro_state.cpp +++ b/rpcs3/Input/mouse_gyro_handler.cpp @@ -1,4 +1,4 @@ -#include "mouse_gyro_state.h" +#include "mouse_gyro_handler.h" #include #include @@ -7,7 +7,7 @@ #include -void mouse_gyro_state::clear() +void mouse_gyro_handler::clear() { active = false; reset = false; @@ -16,18 +16,18 @@ void mouse_gyro_state::clear() gyro_z = DEFAULT_MOTION_Z; } -void mouse_gyro_state::set_gyro_active() +void mouse_gyro_handler::set_gyro_active() { active = true; } -void mouse_gyro_state::set_gyro_reset() +void mouse_gyro_handler::set_gyro_reset() { active = false; reset = true; } -void mouse_gyro_state::set_gyro_xz(s32 off_x, s32 off_y) +void mouse_gyro_handler::set_gyro_xz(s32 off_x, s32 off_y) { if (!active) return; @@ -36,7 +36,7 @@ void mouse_gyro_state::set_gyro_xz(s32 off_x, s32 off_y) gyro_z = static_cast(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1)); } -void mouse_gyro_state::set_gyro_y(s32 steps) +void mouse_gyro_handler::set_gyro_y(s32 steps) { if (!active) return; @@ -44,7 +44,7 @@ void mouse_gyro_state::set_gyro_y(s32 steps) gyro_y = static_cast(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1)); } -void mouse_gyro_state::handle_event(QEvent* ev, const QWindow& win) +void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win) { // Hardcoded mouse-based motion input. // Captures mouse events while the game window is focused. @@ -107,7 +107,7 @@ void mouse_gyro_state::handle_event(QEvent* ev, const QWindow& win) } } -void mouse_gyro_state::apply_gyro(const std::shared_ptr& pad) +void mouse_gyro_handler::apply_gyro(const std::shared_ptr& pad) { if (!pad || !pad->is_connected()) return; diff --git a/rpcs3/Input/mouse_gyro_state.h b/rpcs3/Input/mouse_gyro_handler.h similarity index 97% rename from rpcs3/Input/mouse_gyro_state.h rename to rpcs3/Input/mouse_gyro_handler.h index f5001e57b6..8874ed1c97 100644 --- a/rpcs3/Input/mouse_gyro_state.h +++ b/rpcs3/Input/mouse_gyro_handler.h @@ -8,7 +8,7 @@ class QEvent; class QWindow; // Mouse-based motion sensor emulation state. -class mouse_gyro_state +class mouse_gyro_handler { private: atomic_t active = false; // Whether right mouse button is currently held (gyro active) diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index 894d93aaa5..6a39b3d1f3 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -5,7 +5,7 @@ #include "Emu/Io/pad_types.h" #include "Emu/Io/pad_config.h" #include "Emu/Io/pad_config_types.h" -#include "Input/mouse_gyro_state.h" +#include "Input/mouse_gyro_handler.h" #include "Utilities/mutex.h" #include @@ -42,7 +42,7 @@ public: static auto constexpr thread_name = "Pad Thread"sv; - mouse_gyro_state m_mouse_gyro; + mouse_gyro_handler m_mouse_gyro; protected: void Init(); From fef0bccfa0281e33db1e13524df29ff83105f816 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 16:45:42 +0300 Subject: [PATCH 20/24] Changed class name Changed class name --- rpcs3/rpcs3.vcxproj | 4 ++-- rpcs3/rpcs3.vcxproj.filters | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rpcs3/rpcs3.vcxproj b/rpcs3/rpcs3.vcxproj index cca31dd52b..9749f60fcd 100644 --- a/rpcs3/rpcs3.vcxproj +++ b/rpcs3/rpcs3.vcxproj @@ -194,7 +194,7 @@ - + @@ -1080,7 +1080,7 @@ $(QTDIR)\bin\moc.exe;%(FullPath) $(QTDIR)\bin\moc.exe;%(FullPath) - + diff --git a/rpcs3/rpcs3.vcxproj.filters b/rpcs3/rpcs3.vcxproj.filters index 797b216a3f..a011ddf62e 100644 --- a/rpcs3/rpcs3.vcxproj.filters +++ b/rpcs3/rpcs3.vcxproj.filters @@ -1272,7 +1272,7 @@ Generated Files\Release - + Io @@ -1514,7 +1514,7 @@ Io\camera - + Io From f5cf5151d9e4655dfda66ed3b1bc7d02d1385721 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 17:11:24 +0300 Subject: [PATCH 21/24] Wrapped to a hotkey Wrapped to a hotkey --- rpcs3/Input/mouse_gyro_handler.cpp | 15 ++++++++++++++- rpcs3/Input/mouse_gyro_handler.h | 3 +++ rpcs3/rpcs3qt/gs_frame.cpp | 9 +++++++++ rpcs3/rpcs3qt/shortcut_settings.cpp | 2 ++ rpcs3/rpcs3qt/shortcut_settings.h | 1 + 5 files changed, 29 insertions(+), 1 deletion(-) diff --git a/rpcs3/Input/mouse_gyro_handler.cpp b/rpcs3/Input/mouse_gyro_handler.cpp index d59878f1ec..6f1c7cd637 100644 --- a/rpcs3/Input/mouse_gyro_handler.cpp +++ b/rpcs3/Input/mouse_gyro_handler.cpp @@ -16,6 +16,13 @@ void mouse_gyro_handler::clear() gyro_z = DEFAULT_MOTION_Z; } +bool mouse_gyro_handler::toggle_enabled() +{ + enabled = !enabled; + clear(); + return enabled; +} + void mouse_gyro_handler::set_gyro_active() { active = true; @@ -46,7 +53,10 @@ void mouse_gyro_handler::set_gyro_y(s32 steps) void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win) { - // Hardcoded mouse-based motion input. + if (!enabled) + return; + + // Mouse-based motion input. // Captures mouse events while the game window is focused. // Updates motion sensor values via mouse position and mouse wheel while RMB is held. // Intentionally independent of chosen pad configuration. @@ -109,6 +119,9 @@ void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win) void mouse_gyro_handler::apply_gyro(const std::shared_ptr& pad) { + if (!enabled) + return; + if (!pad || !pad->is_connected()) return; diff --git a/rpcs3/Input/mouse_gyro_handler.h b/rpcs3/Input/mouse_gyro_handler.h index 8874ed1c97..97a745d919 100644 --- a/rpcs3/Input/mouse_gyro_handler.h +++ b/rpcs3/Input/mouse_gyro_handler.h @@ -11,6 +11,8 @@ class QWindow; class mouse_gyro_handler { private: + atomic_t enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey + atomic_t active = false; // Whether right mouse button is currently held (gyro active) atomic_t reset = false; // One-shot reset request on right mouse button release atomic_t gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center @@ -24,6 +26,7 @@ private: public: void clear(); + bool toggle_enabled(); void handle_event(QEvent* ev, const QWindow& win); void apply_gyro(const std::shared_ptr& pad); diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 9d0ce9fbf8..5779ca9596 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -403,6 +403,15 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey audio::change_volume(-5); break; } + case gui::shortcuts::shortcut::gw_toggle_mouse_gyro: + { + if (auto* pad_thr = pad::get_pad_thread(true)) + { + const bool mouse_gyro_enabled = pad_thr->m_mouse_gyro.toggle_enabled(); + gui_log.notice("Mouse-based gyro emulation %s", mouse_gyro_enabled ? "enabled" : "disabled"); + } + break; + } default: { break; diff --git a/rpcs3/rpcs3qt/shortcut_settings.cpp b/rpcs3/rpcs3qt/shortcut_settings.cpp index 039f493dae..64feb94777 100644 --- a/rpcs3/rpcs3qt/shortcut_settings.cpp +++ b/rpcs3/rpcs3qt/shortcut_settings.cpp @@ -37,6 +37,7 @@ void fmt_class_string::format(std::string& out, u64 arg) case shortcut::gw_mute_unmute: return "gw_mute_unmute"; case shortcut::gw_volume_up: return "gw_volume_up"; case shortcut::gw_volume_down: return "gw_volume_down"; + case shortcut::gw_toggle_mouse_gyro: return "gw_toggle_mouse_gyro"; case shortcut::count: return "count"; } @@ -88,6 +89,7 @@ shortcut_settings::shortcut_settings() { shortcut::gw_mute_unmute, shortcut_info{ "gw_mute_unmute", tr("Mute/Unmute Audio"), "Ctrl+Shift+M", shortcut_handler_id::game_window, false } }, { shortcut::gw_volume_up, shortcut_info{ "gw_volume_up", tr("Volume Up"), "Ctrl+Shift++", shortcut_handler_id::game_window, true } }, { shortcut::gw_volume_down, shortcut_info{ "gw_volume_down", tr("Volume Down"), "Ctrl+Shift+-", shortcut_handler_id::game_window, true } }, + { shortcut::gw_toggle_mouse_gyro, shortcut_info{ "gw_toggle_mouse_gyro", tr("Toggle Mouse-based Gyro"), "Ctrl+G", shortcut_handler_id::game_window, false } }, }) { } diff --git a/rpcs3/rpcs3qt/shortcut_settings.h b/rpcs3/rpcs3qt/shortcut_settings.h index db6458accb..be14ee1e30 100644 --- a/rpcs3/rpcs3qt/shortcut_settings.h +++ b/rpcs3/rpcs3qt/shortcut_settings.h @@ -46,6 +46,7 @@ namespace gui gw_mute_unmute, gw_volume_up, gw_volume_down, + gw_toggle_mouse_gyro, count }; From 36e1fe6230a2185e3898cc661d3d6933287b6540 Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 17:24:32 +0300 Subject: [PATCH 22/24] Fixed CMakeLists Fixed CMakeLists --- rpcs3/rpcs3qt/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/rpcs3/rpcs3qt/CMakeLists.txt b/rpcs3/rpcs3qt/CMakeLists.txt index a8fc4c5886..b59d6f7a11 100644 --- a/rpcs3/rpcs3qt/CMakeLists.txt +++ b/rpcs3/rpcs3qt/CMakeLists.txt @@ -157,6 +157,7 @@ add_library(rpcs3_ui STATIC ../Input/hid_pad_handler.cpp ../Input/keyboard_pad_handler.cpp ../Input/mm_joystick_handler.cpp + ../Input/mouse_gyro_handler.cpp ../Input/pad_thread.cpp ../Input/product_info.cpp ../Input/ps_move_calibration.cpp From 6a18147aaf1aab6b9b9b277761953fef866c96bc Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 17:26:46 +0300 Subject: [PATCH 23/24] Fixed public/private Fixed public/private --- rpcs3/Input/pad_thread.h | 4 +++- rpcs3/rpcs3qt/gs_frame.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rpcs3/Input/pad_thread.h b/rpcs3/Input/pad_thread.h index 6a39b3d1f3..20f53e9034 100644 --- a/rpcs3/Input/pad_thread.h +++ b/rpcs3/Input/pad_thread.h @@ -42,7 +42,7 @@ public: static auto constexpr thread_name = "Pad Thread"sv; - mouse_gyro_handler m_mouse_gyro; + mouse_gyro_handler& get_mouse_gyro() { return m_mouse_gyro; } protected: void Init(); @@ -70,6 +70,8 @@ private: bool m_resume_emulation_flag = false; bool m_ps_button_pressed = false; atomic_t m_home_menu_open = false; + + mouse_gyro_handler m_mouse_gyro; }; namespace pad diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index 5779ca9596..c6521afcc8 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -407,7 +407,7 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey { if (auto* pad_thr = pad::get_pad_thread(true)) { - const bool mouse_gyro_enabled = pad_thr->m_mouse_gyro.toggle_enabled(); + const bool mouse_gyro_enabled = pad_thr->get_mouse_gyro().toggle_enabled(); gui_log.notice("Mouse-based gyro emulation %s", mouse_gyro_enabled ? "enabled" : "disabled"); } break; @@ -1232,7 +1232,7 @@ bool gs_frame::event(QEvent* ev) { if (auto* pad_thr = pad::get_pad_thread(true)) { - pad_thr->m_mouse_gyro.handle_event(ev, *this); + pad_thr->get_mouse_gyro().handle_event(ev, *this); } } From a02a605ea264b61a1bef5afe7ff7de2469ec5c1d Mon Sep 17 00:00:00 2001 From: Windsurf7 Date: Fri, 30 Jan 2026 17:31:23 +0300 Subject: [PATCH 24/24] Cleanup Cleanup --- rpcs3/Input/pad_thread.cpp | 2 +- rpcs3/rpcs3qt/gs_frame.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rpcs3/Input/pad_thread.cpp b/rpcs3/Input/pad_thread.cpp index 53d7f3f178..40887ae5fb 100644 --- a/rpcs3/Input/pad_thread.cpp +++ b/rpcs3/Input/pad_thread.cpp @@ -610,7 +610,7 @@ void pad_thread::operator()() { update_pad_states(); - // Inject mouse-based motion sensor values into pad sensors for gyro emulation. + // Apply mouse-based gyro emulation. // Intentionally bound to Player 1 only. m_mouse_gyro.apply_gyro(m_pads[0]); } diff --git a/rpcs3/rpcs3qt/gs_frame.cpp b/rpcs3/rpcs3qt/gs_frame.cpp index c6521afcc8..6557168cce 100644 --- a/rpcs3/rpcs3qt/gs_frame.cpp +++ b/rpcs3/rpcs3qt/gs_frame.cpp @@ -1227,7 +1227,7 @@ bool gs_frame::event(QEvent* ev) handle_cursor(visibility(), false, false, true); } - // Mouse-based motion input. + // Handle events for mouse-based gyro emulation. if (Emu.IsRunning()) { if (auto* pad_thr = pad::get_pad_thread(true))