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