diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp index 4444f0ac3b..4ac259d03b 100644 --- a/src/input_common/drivers/mouse.cpp +++ b/src/input_common/drivers/mouse.cpp @@ -4,17 +4,16 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include +#include #include #include #include "common/param_package.h" #include "common/settings.h" -#include "common/thread.h" +#include "common/steady_clock.h" #include "input_common/drivers/mouse.h" namespace InputCommon { -constexpr int update_time = 10; constexpr float default_panning_sensitivity = 0.0010f; constexpr float default_stick_sensitivity = 0.0006f; constexpr float default_deadzone_counterweight = 0.01f; @@ -74,33 +73,31 @@ Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) last_motion_change = {}; } -void Mouse::UpdateStickInput() { - if (!IsMousePanningEnabled()) { - return; - } - - const float length = last_mouse_change.Length(); +void Mouse::UpdateStickInput(Common::SteadyClock::time_point timestamp) { + if (IsMousePanningEnabled()) { + const float length = last_mouse_change.Length(); - // Prevent input from exceeding the max range (1.0f) too much, - // but allow some room to make it easier to sustain - if (length > maximum_stick_range) { - last_mouse_change /= length; - last_mouse_change *= maximum_stick_range; - } + // Prevent input from exceeding the max range (1.0f) too much, + // but allow some room to make it easier to sustain + if (length > maximum_stick_range) { + last_mouse_change /= length; + last_mouse_change *= maximum_stick_range; + } - SetAxis(identifier, mouse_axis_x, last_mouse_change[0]); - SetAxis(identifier, mouse_axis_y, -last_mouse_change[1]); + SetAxis(identifier, mouse_axis_x, last_mouse_change[0]); + SetAxis(identifier, mouse_axis_y, -last_mouse_change[1]); - // Decay input over time - const float clamped_length = (std::min)(1.0f, length); - const float decay_strength = Settings::values.mouse_panning_decay_strength.GetValue(); - const float decay = 1 - clamped_length * clamped_length * decay_strength * 0.01f; - const float min_decay = Settings::values.mouse_panning_min_decay.GetValue(); - const float clamped_decay = (std::min)(1 - min_decay / 100.0f, decay); - last_mouse_change *= clamped_decay; + // Decay input over time + const float clamped_length = (std::min)(1.0f, length); + const float decay_strength = Settings::values.mouse_panning_decay_strength.GetValue(); + const float decay = 1 - clamped_length * clamped_length * decay_strength * 0.01f; + const float min_decay = Settings::values.mouse_panning_min_decay.GetValue(); + const float clamped_decay = (std::min)(1 - min_decay / 100.0f, decay); + last_mouse_change *= clamped_decay; + } } -void Mouse::UpdateMotionInput() { +void Mouse::UpdateMotionInput(Common::SteadyClock::time_point timestamp) { const float sensitivity = IsMousePanningEnabled() ? default_motion_panning_sensitivity : default_motion_sensitivity; @@ -114,23 +111,21 @@ void Mouse::UpdateMotionInput() { last_motion_change[1] = last_motion_change[1] * multiplier; } - const BasicMotion motion_data{ - .gyro_x = last_motion_change[0] * sensitivity, - .gyro_y = last_motion_change[1] * sensitivity, - .gyro_z = last_motion_change[2] * sensitivity, - .accel_x = 0, - .accel_y = 0, - .accel_z = 0, - .delta_timestamp = update_time * 1000, - }; - if (IsMousePanningEnabled()) { last_motion_change[0] = 0; last_motion_change[1] = 0; } last_motion_change[2] = 0; - SetMotion(motion_identifier, 0, motion_data); + SetMotion(motion_identifier, 0, BasicMotion{ + .gyro_x = last_motion_change[0] * sensitivity, + .gyro_y = last_motion_change[1] * sensitivity, + .gyro_z = last_motion_change[2] * sensitivity, + .accel_x = 0, + .accel_y = 0, + .accel_z = 0, + .delta_timestamp = u64(std::chrono::duration_cast(timestamp - last_notify_timestamp).count()), + }); } void Mouse::Move(int x, int y, int center_x, int center_y) { @@ -149,29 +144,27 @@ void Mouse::Move(int x, int y, int center_x, int center_y) { last_mouse_change /= length; last_mouse_change *= deadzone_cw; } - return; - } - - if (button_pressed) { - const auto mouse_move = Common::Vec(x, y) - mouse_origin; - const float x_sensitivity = - Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; - const float y_sensitivity = - Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; - SetAxis(identifier, mouse_axis_x, float(mouse_move[0]) * x_sensitivity); - SetAxis(identifier, mouse_axis_y, float(-mouse_move[1]) * y_sensitivity); - - last_motion_change = { - float(-mouse_move[1]) * x_sensitivity, - float(-mouse_move[0]) * y_sensitivity, - last_motion_change[2], - }; + } else { + if (button_pressed) { + const auto mouse_move = Common::Vec(x, y) - mouse_origin; + const float x_sensitivity = Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; + const float y_sensitivity = Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; + SetAxis(identifier, mouse_axis_x, float(mouse_move[0]) * x_sensitivity); + SetAxis(identifier, mouse_axis_y, float(-mouse_move[1]) * y_sensitivity); + last_motion_change = { + float(-mouse_move[1]) * x_sensitivity, + float(-mouse_move[0]) * y_sensitivity, + last_motion_change[2], + }; + } } } void Mouse::NotifyChanged() { - UpdateStickInput(); - UpdateMotionInput(); + auto const timestamp = Common::SteadyClock::Now(); + UpdateStickInput(timestamp); + UpdateMotionInput(timestamp); + last_notify_timestamp = timestamp; } void Mouse::MouseMove(f32 touch_x, f32 touch_y) { @@ -223,8 +216,8 @@ void Mouse::MouseWheelChange(int x, int y) { wheel_position[0] += x; wheel_position[1] += y; last_motion_change[2] += static_cast(y); - SetAxis(identifier, wheel_axis_x, static_cast(wheel_position[0])); - SetAxis(identifier, wheel_axis_y, static_cast(wheel_position[1])); + SetAxis(identifier, wheel_axis_x, f32(wheel_position[0])); + SetAxis(identifier, wheel_axis_y, f32(wheel_position[1])); } void Mouse::ReleaseAllButtons() { diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h index 25f75109b9..1f7cb7e966 100644 --- a/src/input_common/drivers/mouse.h +++ b/src/input_common/drivers/mouse.h @@ -7,7 +7,9 @@ #pragma once #include +#include +#include "common/steady_clock.h" #include "common/polyfill_thread.h" #include "common/vector_math.h" #include "input_common/input_engine.h" @@ -101,8 +103,8 @@ public: Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override; private: - void UpdateStickInput(); - void UpdateMotionInput(); + void UpdateStickInput(Common::SteadyClock::time_point timestamp); + void UpdateMotionInput(Common::SteadyClock::time_point timestamp); bool IsMousePanningEnabled(); Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const; @@ -112,6 +114,7 @@ private: Common::Vec last_mouse_change; Common::Vec last_motion_change; Common::Vec wheel_position; + Common::SteadyClock::time_point last_notify_timestamp{}; bool button_pressed = false; }; diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index f9c0233901..308db3621b 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -73,6 +73,7 @@ class QPaintEngine; class QSurface; constexpr int default_mouse_constrain_timeout = 10; +constexpr int default_mouse_update_timeout = 5; class RenderWidget : public QWidget { public: @@ -138,6 +139,10 @@ GRenderWindow::GRenderWindow(MainWindow* parent, mouse_constrain_timer.setInterval(default_mouse_constrain_timeout); connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse); + + mouse_update_timer.setInterval(default_mouse_update_timeout); + connect(&mouse_update_timer, &QTimer::timeout, this, &GRenderWindow::UpdateMouse); + mouse_update_timer.start(); } void GRenderWindow::ExecuteProgram(std::size_t program_index) { @@ -487,7 +492,6 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) { input_subsystem->GetMouse()->PressMouseButton(button); input_subsystem->GetMouse()->PressButton(pos.x(), pos.y(), button); input_subsystem->GetMouse()->PressTouchButton(touch_x, touch_y, button); - input_subsystem->GetMouse()->NotifyChanged(); emit MouseActivity(); } @@ -508,7 +512,6 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { input_subsystem->GetMouse()->MouseMove(touch_x, touch_y); input_subsystem->GetMouse()->TouchMove(touch_x, touch_y); input_subsystem->GetMouse()->Move(pos.x(), pos.y(), center_x, center_y); - input_subsystem->GetMouse()->NotifyChanged(); // Center mouse for mouse panning if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { @@ -518,8 +521,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { // Constrain mouse for mouse emulation with mouse panning if (Settings::values.mouse_panning && Settings::values.mouse_enabled) { const auto [clamped_mouse_x, clamped_mouse_y] = ClipToTouchScreen(x, y); - QCursor::setPos(mapToGlobal( - QPoint{static_cast(clamped_mouse_x), static_cast(clamped_mouse_y)})); + QCursor::setPos(mapToGlobal(QPoint{int(clamped_mouse_x), int(clamped_mouse_y)})); } mouse_constrain_timer.stop(); @@ -534,16 +536,10 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { const auto button = QtButtonToMouseButton(event->button()); input_subsystem->GetMouse()->ReleaseButton(button); - input_subsystem->GetMouse()->NotifyChanged(); } void GRenderWindow::ConstrainMouse() { - if (QtCommon::emu_thread == nullptr || !Settings::values.mouse_panning) { - mouse_constrain_timer.stop(); - return; - } - - if (!this->isActiveWindow()) { + if (QtCommon::emu_thread == nullptr || Settings::values.mouse_panning || !this->isActiveWindow()) { mouse_constrain_timer.stop(); return; } @@ -552,22 +548,22 @@ void GRenderWindow::ConstrainMouse() { const auto pos = mapFromGlobal(QCursor::pos()); const int new_pos_x = std::clamp(pos.x(), 0, width()); const int new_pos_y = std::clamp(pos.y(), 0, height()); - QCursor::setPos(mapToGlobal(QPoint{new_pos_x, new_pos_y})); - return; + } else { + const int center_x = width() / 2; + const int center_y = height() / 2; + QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); } +} - const int center_x = width() / 2; - const int center_y = height() / 2; - - QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); +void GRenderWindow::UpdateMouse() { + input_subsystem->GetMouse()->NotifyChanged(); // required to reset mouse once it's no longer moved } void GRenderWindow::wheelEvent(QWheelEvent* event) { const int x = event->angleDelta().x(); const int y = event->angleDelta().y(); input_subsystem->GetMouse()->MouseWheelChange(x, y); - input_subsystem->GetMouse()->NotifyChanged(); } void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { @@ -716,7 +712,6 @@ void GRenderWindow::focusOutEvent(QFocusEvent* event) { input_subsystem->GetKeyboard()->ReleaseAllKeys(); input_subsystem->GetTouchScreen()->ReleaseAllTouch(); input_subsystem->GetMouse()->ReleaseAllButtons(); - input_subsystem->GetMouse()->NotifyChanged(); } void GRenderWindow::resizeEvent(QResizeEvent* event) { diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 1ed61a8191..bb18bbb3bb 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -144,6 +144,7 @@ private: void TouchUpdateEvent(const QTouchEvent* event); void TouchEndEvent(); void ConstrainMouse(); + void UpdateMouse(); void RequestCameraCapture(); void OnCameraCapture(int requestId, const QImage& img); @@ -184,6 +185,7 @@ private: #endif QTimer mouse_constrain_timer; + QTimer mouse_update_timer; protected: void showEvent(QShowEvent* event) override; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl3.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl3.cpp index cff2292bb2..950e001f81 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl3.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3.cpp @@ -37,10 +37,16 @@ EmuWindow_SDL3::EmuWindow_SDL3(InputCommon::InputSubsystem* input_subsystem_, Co SDL_SetWindowTitle(this_->render_window, title.c_str()); return 2000; }, this); + mouse_timer = SDL_AddTimer(100, [](void *userdata, SDL_TimerID, Uint32) -> Uint32 { + auto* this_ = (EmuWindow_SDL3*)userdata; + this_->input_subsystem->GetMouse()->NotifyChanged(); + return 100; + }, this); } EmuWindow_SDL3::~EmuWindow_SDL3() { SDL_RemoveTimer(titlebar_timer); + SDL_RemoveTimer(mouse_timer); system.HIDCore().UnloadInputDevices(); input_subsystem->Shutdown(); SDL_Quit(); diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl3.h b/src/yuzu_cmd/emu_window/emu_window_sdl3.h index bf63b65554..ffabaeb143 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl3.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl3.h @@ -84,6 +84,9 @@ protected: /// Periodic changer of titlebar (independent of event loop) SDL_TimerID titlebar_timer; + // Mouse resetter once it + SDL_TimerID mouse_timer; + /// Is the window still open? bool is_open = true;