From 52b79ac00965aaf10db1a181067c2f2d33125171 Mon Sep 17 00:00:00 2001 From: german Date: Wed, 3 Feb 2021 12:34:25 -0600 Subject: [PATCH] Add mouse panning --- src/core/settings.h | 5 +- src/input_common/mouse/mouse_input.cpp | 32 +++++++- src/input_common/mouse/mouse_input.h | 7 +- src/input_common/mouse/mouse_poller.cpp | 3 +- src/yuzu/bootmanager.cpp | 14 +++- src/yuzu/configuration/config.cpp | 10 ++- src/yuzu/configuration/config.h | 2 +- .../configure_input_advanced.cpp | 5 ++ .../configuration/configure_input_advanced.ui | 82 ++++++++++++++----- src/yuzu/main.cpp | 24 ++++-- src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | 3 +- 11 files changed, 149 insertions(+), 38 deletions(-) diff --git a/src/core/settings.h b/src/core/settings.h index a324530bd..d849dded3 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -181,12 +181,13 @@ struct Values { std::string motion_device; std::string udp_input_servers; - bool emulate_analog_keyboard; - + bool mouse_panning; + float mouse_panning_sensitivity; bool mouse_enabled; std::string mouse_device; MouseButtonsRaw mouse_buttons; + bool emulate_analog_keyboard; bool keyboard_enabled; KeyboardKeysRaw keyboard_keys; KeyboardModsRaw keyboard_mods; diff --git a/src/input_common/mouse/mouse_input.cpp b/src/input_common/mouse/mouse_input.cpp index 10786a541..67a584d53 100644 --- a/src/input_common/mouse/mouse_input.cpp +++ b/src/input_common/mouse/mouse_input.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "core/settings.h" #include "input_common/mouse/mouse_input.h" namespace MouseInput { @@ -36,6 +37,9 @@ void Mouse::UpdateThread() { if (configuring) { UpdateYuzuSettings(); } + if (mouse_panning_timout++ > 8) { + StopPanning(); + } std::this_thread::sleep_for(std::chrono::milliseconds(update_time)); } } @@ -65,8 +69,34 @@ void Mouse::PressButton(int x, int y, int button_) { mouse_info[button_index].data.pressed = true; } -void Mouse::MouseMove(int x, int y) { +void Mouse::StopPanning() { for (MouseInfo& info : mouse_info) { + if (Settings::values.mouse_panning) { + info.data.axis = {}; + info.tilt_speed = 0; + info.last_mouse_change = {}; + } + } +} + +void Mouse::MouseMove(int x, int y, int center_x, int center_y) { + for (MouseInfo& info : mouse_info) { + if (Settings::values.mouse_panning) { + const auto mouse_change = Common::MakeVec(x, y) - Common::MakeVec(center_x, center_y); + mouse_panning_timout = 0; + + if (mouse_change.y == 0 && mouse_change.x == 0) { + continue; + } + + info.last_mouse_change = (info.last_mouse_change * 0.8f) + (mouse_change * 0.2f); + info.data.axis = {static_cast(16 * info.last_mouse_change.x), + static_cast(16 * -info.last_mouse_change.y)}; + info.tilt_direction = info.last_mouse_change; + info.tilt_speed = info.tilt_direction.Normalize() * info.sensitivity; + continue; + } + if (info.data.pressed) { const auto mouse_move = Common::MakeVec(x, y) - info.mouse_origin; const auto mouse_change = Common::MakeVec(x, y) - info.last_mouse_position; diff --git a/src/input_common/mouse/mouse_input.h b/src/input_common/mouse/mouse_input.h index 58803c1bf..46aa676c1 100644 --- a/src/input_common/mouse/mouse_input.h +++ b/src/input_common/mouse/mouse_input.h @@ -57,8 +57,10 @@ public: * Signals that mouse has moved. * @param x the x-coordinate of the cursor * @param y the y-coordinate of the cursor + * @param center_x the x-coordinate of the middle of the screen + * @param center_y the y-coordinate of the middle of the screen */ - void MouseMove(int x, int y); + void MouseMove(int x, int y, int center_x, int center_y); /** * Signals that a motion sensor tilt has ended. @@ -74,11 +76,13 @@ public: private: void UpdateThread(); void UpdateYuzuSettings(); + void StopPanning(); struct MouseInfo { InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f}; Common::Vec2 mouse_origin; Common::Vec2 last_mouse_position; + Common::Vec2 last_mouse_change; bool is_tilting = false; float sensitivity{0.120f}; @@ -94,5 +98,6 @@ private: Common::SPSCQueue mouse_queue; bool configuring{false}; bool update_thread_running{true}; + int mouse_panning_timout{}; }; } // namespace MouseInput diff --git a/src/input_common/mouse/mouse_poller.cpp b/src/input_common/mouse/mouse_poller.cpp index 508eb0c7d..3d0dd7fc8 100644 --- a/src/input_common/mouse/mouse_poller.cpp +++ b/src/input_common/mouse/mouse_poller.cpp @@ -6,6 +6,7 @@ #include #include "common/threadsafe_queue.h" +#include "core/settings.h" #include "input_common/mouse/mouse_input.h" #include "input_common/mouse/mouse_poller.h" @@ -71,7 +72,7 @@ public: std::lock_guard lock{mutex}; const auto axis_value = static_cast(mouse_input->GetMouseState(button).axis.at(axis)); - return axis_value / (100.0f * range); + return axis_value * Settings::values.mouse_panning_sensitivity / (100.0f * range); } std::pair GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const { diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 4528eb196..f278f1439 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -405,12 +405,17 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { if (event->source() == Qt::MouseEventSynthesizedBySystem) { return; } - auto pos = event->pos(); const auto [x, y] = ScaleTouch(pos); - input_subsystem->GetMouse()->MouseMove(x, y); + const int center_x = width() / 2; + const int center_y = height() / 2; + input_subsystem->GetMouse()->MouseMove(x, y, center_x, center_y); this->TouchMoved(x, y, 0); + if (Settings::values.mouse_panning) { + QCursor::setPos(mapToGlobal({center_x, center_y})); + } + emit MouseActivity(); } @@ -727,6 +732,11 @@ void GRenderWindow::showEvent(QShowEvent* event) { bool GRenderWindow::eventFilter(QObject* object, QEvent* event) { if (event->type() == QEvent::HoverMove) { + if (Settings::values.mouse_panning) { + auto* hover_event = static_cast(event); + mouseMoveEvent(hover_event); + return false; + } emit MouseActivity(); } return false; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 8d85a1986..8f7458119 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -220,7 +220,7 @@ const std::array Config::default // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off -const std::array Config::default_hotkeys{{ +const std::array Config::default_hotkeys{{ {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}}, {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, @@ -235,6 +235,7 @@ const std::array Config::default_hotkeys{{ {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}}, + {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}}, {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}}, }}; @@ -507,6 +508,9 @@ void Config::ReadControlValues() { Settings::values.emulate_analog_keyboard = ReadSetting(QStringLiteral("emulate_analog_keyboard"), false).toBool(); + Settings::values.mouse_panning = ReadSetting(QStringLiteral("mouse_panning"), false).toBool(); + Settings::values.mouse_panning_sensitivity = + ReadSetting(QStringLiteral("mouse_panning_sensitivity"), 1).toFloat(); ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), true); ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"), @@ -1184,7 +1188,9 @@ void Config::SaveControlValues() { WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false); WriteSetting(QStringLiteral("emulate_analog_keyboard"), Settings::values.emulate_analog_keyboard, false); - + WriteSetting(QStringLiteral("mouse_panning"), Settings::values.mouse_panning, false); + WriteSetting(QStringLiteral("mouse_panning_sensitivity"), + Settings::values.mouse_panning_sensitivity, 1.0f); qt_config->endGroup(); } diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 8a600e19d..949c4eb13 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -42,7 +42,7 @@ public: default_mouse_buttons; static const std::array default_keyboard_keys; static const std::array default_keyboard_mods; - static const std::array default_hotkeys; + static const std::array default_hotkeys; private: void Initialize(const std::string& config_name); diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp index 4e557bc6f..a1a0eb676 100644 --- a/src/yuzu/configuration/configure_input_advanced.cpp +++ b/src/yuzu/configuration/configure_input_advanced.cpp @@ -122,6 +122,9 @@ void ConfigureInputAdvanced::ApplyConfiguration() { Settings::values.mouse_enabled = ui->mouse_enabled->isChecked(); Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked(); Settings::values.emulate_analog_keyboard = ui->emulate_analog_keyboard->isChecked(); + Settings::values.mouse_panning = ui->mouse_panning->isChecked(); + Settings::values.mouse_panning_sensitivity = + static_cast(ui->mouse_panning_sensitivity->value()); Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked(); } @@ -149,6 +152,8 @@ void ConfigureInputAdvanced::LoadConfiguration() { ui->mouse_enabled->setChecked(Settings::values.mouse_enabled); ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled); ui->emulate_analog_keyboard->setChecked(Settings::values.emulate_analog_keyboard); + ui->mouse_panning->setChecked(Settings::values.mouse_panning); + ui->mouse_panning_sensitivity->setValue(Settings::values.mouse_panning_sensitivity); ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled); UpdateUIEnabled(); diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui index f207e5d3b..173130d8d 100644 --- a/src/yuzu/configuration/configure_input_advanced.ui +++ b/src/yuzu/configuration/configure_input_advanced.ui @@ -2546,27 +2546,65 @@ - - - - - 0 - 23 - - - - Emulate Analog with Keyboard Input - - - - + + + + + 0 + 23 + + + + Emulate Analog with Keyboard Input + + + + + + + + 0 + 23 + + + + Enable mouse panning + + + + + + + Mouse sensitivity + + + Qt::AlignCenter + + + 2 + + + 0.100000000000000 + + + 16.000000000000000 + + + 0.010000000000000 + + + 1.000000000000000 + + + + Advanced - + Qt::Horizontal @@ -2582,21 +2620,21 @@ - + Advanced - + Touchscreen - + @@ -2609,28 +2647,28 @@ - + Motion / Touch - + Configure - + Debug Controller - + Configure diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 886e6e9d2..1991d0da6 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -844,6 +844,16 @@ void GMainWindow::InitializeHotkeys() { connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this), &QShortcut::activated, this, [] { Settings::values.audio_muted = !Settings::values.audio_muted; }); + + connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Mouse Panning"), this), + &QShortcut::activated, this, [&] { + Settings::values.mouse_panning = !Settings::values.mouse_panning; + if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { + mouse_hide_timer.start(); + render_window->installEventFilter(render_window); + render_window->setAttribute(Qt::WA_Hover, true); + } + }); } void GMainWindow::SetDefaultUIGeometry() { @@ -1191,7 +1201,7 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { multicore_status_button->setDisabled(true); renderer_status_button->setDisabled(true); - if (UISettings::values.hide_mouse) { + if (UISettings::values.hide_mouse || Settings::values.mouse_panning) { mouse_hide_timer.start(); render_window->installEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, true); @@ -2352,7 +2362,7 @@ void GMainWindow::OnConfigure() { config->Save(); - if (UISettings::values.hide_mouse && emulation_running) { + if ((UISettings::values.hide_mouse || Settings::values.mouse_panning) && emulation_running) { render_window->installEventFilter(render_window); render_window->setAttribute(Qt::WA_Hover, true); mouse_hide_timer.start(); @@ -2593,7 +2603,8 @@ void GMainWindow::UpdateUISettings() { } void GMainWindow::HideMouseCursor() { - if (emu_thread == nullptr || UISettings::values.hide_mouse == false) { + if (emu_thread == nullptr || + (!UISettings::values.hide_mouse && !Settings::values.mouse_panning)) { mouse_hide_timer.stop(); ShowMouseCursor(); return; @@ -2603,13 +2614,16 @@ void GMainWindow::HideMouseCursor() { void GMainWindow::ShowMouseCursor() { render_window->unsetCursor(); - if (emu_thread != nullptr && UISettings::values.hide_mouse) { + if (emu_thread != nullptr && + (UISettings::values.hide_mouse || Settings::values.mouse_panning)) { mouse_hide_timer.start(); } } void GMainWindow::OnMouseActivity() { - ShowMouseCursor(); + if (!Settings::values.mouse_panning) { + ShowMouseCursor(); + } } void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) { diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 7843d5167..39841aa28 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -30,7 +30,8 @@ EmuWindow_SDL2::~EmuWindow_SDL2() { void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) { TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0), 0); - input_subsystem->GetMouse()->MouseMove(x, y); + + input_subsystem->GetMouse()->MouseMove(x, y, 0, 0); } void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {