mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-04-03 11:28:07 -06:00
Merge a02a605ea2 into db3d9cd217
This commit is contained in:
commit
ff608d88b1
146
rpcs3/Input/mouse_gyro_handler.cpp
Normal file
146
rpcs3/Input/mouse_gyro_handler.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "mouse_gyro_handler.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <QWindow>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
void mouse_gyro_handler::clear()
|
||||
{
|
||||
active = false;
|
||||
reset = false;
|
||||
gyro_x = DEFAULT_MOTION_X;
|
||||
gyro_y = DEFAULT_MOTION_Y;
|
||||
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;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_reset()
|
||||
{
|
||||
active = false;
|
||||
reset = true;
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_xz(s32 off_x, s32 off_y)
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
gyro_x = static_cast<u16>(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1));
|
||||
gyro_z = static_cast<u16>(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1));
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::set_gyro_y(s32 steps)
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
gyro_y = static_cast<u16>(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1));
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win)
|
||||
{
|
||||
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.
|
||||
switch (ev->type())
|
||||
{
|
||||
case QEvent::MouseButtonPress:
|
||||
{
|
||||
auto* e = static_cast<QMouseEvent*>(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<QMouseEvent*>(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<QMouseEvent*>(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<QWheelEvent*>(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;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouse_gyro_handler::apply_gyro(const std::shared_ptr<Pad>& pad)
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
33
rpcs3/Input/mouse_gyro_handler.h
Normal file
33
rpcs3/Input/mouse_gyro_handler.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Emu/Io/pad_types.h"
|
||||
|
||||
class QEvent;
|
||||
class QWindow;
|
||||
|
||||
// Mouse-based motion sensor emulation state.
|
||||
class mouse_gyro_handler
|
||||
{
|
||||
private:
|
||||
atomic_t<bool> enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey
|
||||
|
||||
atomic_t<bool> active = false; // Whether right mouse button is currently held (gyro active)
|
||||
atomic_t<bool> reset = false; // One-shot reset request on right mouse button release
|
||||
atomic_t<s32> gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center
|
||||
atomic_t<s32> gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta
|
||||
atomic_t<s32> gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center
|
||||
|
||||
void set_gyro_active();
|
||||
void set_gyro_reset();
|
||||
void set_gyro_xz(s32 off_x, s32 off_y);
|
||||
void set_gyro_y(s32 steps);
|
||||
|
||||
public:
|
||||
void clear();
|
||||
bool toggle_enabled();
|
||||
|
||||
void handle_event(QEvent* ev, const QWindow& win);
|
||||
void apply_gyro(const std::shared_ptr<Pad>& pad);
|
||||
};
|
||||
@ -81,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_setting, CELL_PAD_MAX_PORT_NUM> pad_settings;
|
||||
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads
|
||||
@ -606,6 +609,10 @@ void pad_thread::operator()()
|
||||
if (Emu.IsRunning())
|
||||
{
|
||||
update_pad_states();
|
||||
|
||||
// Apply mouse-based gyro emulation.
|
||||
// Intentionally bound to Player 1 only.
|
||||
m_mouse_gyro.apply_gyro(m_pads[0]);
|
||||
}
|
||||
|
||||
m_info.now_connect = connected_devices + num_ldd_pad;
|
||||
|
||||
@ -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_handler.h"
|
||||
#include "Utilities/mutex.h"
|
||||
|
||||
#include <map>
|
||||
@ -41,6 +42,8 @@ public:
|
||||
|
||||
static auto constexpr thread_name = "Pad Thread"sv;
|
||||
|
||||
mouse_gyro_handler& get_mouse_gyro() { return m_mouse_gyro; }
|
||||
|
||||
protected:
|
||||
void Init();
|
||||
void InitLddPad(u32 handle, const u32* port_status);
|
||||
@ -67,6 +70,8 @@ private:
|
||||
bool m_resume_emulation_flag = false;
|
||||
bool m_ps_button_pressed = false;
|
||||
atomic_t<bool> m_home_menu_open = false;
|
||||
|
||||
mouse_gyro_handler m_mouse_gyro;
|
||||
};
|
||||
|
||||
namespace pad
|
||||
|
||||
@ -194,6 +194,7 @@
|
||||
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
|
||||
<ClCompile Include="Input\gui_pad_thread.cpp" />
|
||||
<ClCompile Include="Input\hid_pad_handler.cpp" />
|
||||
<ClCompile Include="Input\mouse_gyro_handler.cpp" />
|
||||
<ClCompile Include="Input\ps_move_calibration.cpp" />
|
||||
<ClCompile Include="Input\ps_move_config.cpp" />
|
||||
<ClCompile Include="Input\ps_move_tracker.cpp" />
|
||||
@ -1079,6 +1080,7 @@
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="Input\mouse_gyro_handler.h" />
|
||||
<ClInclude Include="Input\ps_move_calibration.h" />
|
||||
<ClInclude Include="Input\ps_move_config.h" />
|
||||
<ClInclude Include="Input\ps_move_tracker.h" />
|
||||
|
||||
@ -1272,6 +1272,9 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_context_menu.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Input\mouse_gyro_handler.cpp">
|
||||
<Filter>Io</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -1511,6 +1514,9 @@
|
||||
<ClInclude Include="Input\sdl_camera_video_sink.h">
|
||||
<Filter>Io\camera</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Input\mouse_gyro_handler.h">
|
||||
<Filter>Io</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="resource.h">
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 <QApplication>
|
||||
#include <QDateTime>
|
||||
@ -402,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->get_mouse_gyro().toggle_enabled();
|
||||
gui_log.notice("Mouse-based gyro emulation %s", mouse_gyro_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
@ -1216,6 +1226,16 @@ 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);
|
||||
}
|
||||
|
||||
// Handle events for mouse-based gyro emulation.
|
||||
if (Emu.IsRunning())
|
||||
{
|
||||
if (auto* pad_thr = pad::get_pad_thread(true))
|
||||
{
|
||||
pad_thr->get_mouse_gyro().handle_event(ev, *this);
|
||||
}
|
||||
}
|
||||
|
||||
return QWindow::event(ev);
|
||||
}
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ void fmt_class_string<shortcut>::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 } },
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ namespace gui
|
||||
gw_mute_unmute,
|
||||
gw_volume_up,
|
||||
gw_volume_down,
|
||||
gw_toggle_mouse_gyro,
|
||||
|
||||
count
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user