yuzu: Constrain mouse in render window when emulated
This commit is contained in:
parent
91c12db070
commit
f61cf14646
|
@ -167,6 +167,11 @@ protected:
|
||||||
*/
|
*/
|
||||||
std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
|
std::pair<f32, f32> MapToTouchScreen(u32 framebuffer_x, u32 framebuffer_y) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clip the provided coordinates to be inside the touchscreen area.
|
||||||
|
*/
|
||||||
|
std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
|
||||||
|
|
||||||
WindowSystemInfo window_info;
|
WindowSystemInfo window_info;
|
||||||
|
|
||||||
bool strict_context_required = false;
|
bool strict_context_required = false;
|
||||||
|
@ -181,11 +186,6 @@ private:
|
||||||
// By default, ignore this request and do nothing.
|
// By default, ignore this request and do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clip the provided coordinates to be inside the touchscreen area.
|
|
||||||
*/
|
|
||||||
std::pair<u32, u32> ClipToTouchScreen(u32 new_x, u32 new_y) const;
|
|
||||||
|
|
||||||
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
|
Layout::FramebufferLayout framebuffer_layout; ///< Current framebuffer layout
|
||||||
|
|
||||||
u32 client_area_width; ///< Current client width, should be set by window impl.
|
u32 client_area_width; ///< Current client width, should be set by window impl.
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QStringLiteral>
|
#include <QStringLiteral>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
#include <QTimer>
|
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
#include <QtCore/qobjectdefs.h>
|
#include <QtCore/qobjectdefs.h>
|
||||||
|
|
||||||
|
@ -66,6 +65,8 @@ class QObject;
|
||||||
class QPaintEngine;
|
class QPaintEngine;
|
||||||
class QSurface;
|
class QSurface;
|
||||||
|
|
||||||
|
constexpr int default_mouse_constrain_timeout = 10;
|
||||||
|
|
||||||
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
|
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
|
||||||
|
|
||||||
EmuThread::~EmuThread() = default;
|
EmuThread::~EmuThread() = default;
|
||||||
|
@ -304,6 +305,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
|
connect(this, &GRenderWindow::ExitSignal, parent, &GMainWindow::OnExit, Qt::QueuedConnection);
|
||||||
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
|
connect(this, &GRenderWindow::TasPlaybackStateChanged, parent, &GMainWindow::OnTasStateChanged);
|
||||||
|
|
||||||
|
mouse_constrain_timer.setInterval(default_mouse_constrain_timeout);
|
||||||
|
connect(&mouse_constrain_timer, &QTimer::timeout, this, &GRenderWindow::ConstrainMouse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
|
void GRenderWindow::ExecuteProgram(std::size_t program_index) {
|
||||||
|
@ -393,6 +397,22 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
|
||||||
QWidget::closeEvent(event);
|
QWidget::closeEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::leaveEvent(QEvent* event) {
|
||||||
|
if (Settings::values.mouse_panning) {
|
||||||
|
const QRect& rect = QWidget::geometry();
|
||||||
|
QPoint position = QCursor::pos();
|
||||||
|
|
||||||
|
qint32 x = qBound(rect.left(), position.x(), rect.right());
|
||||||
|
qint32 y = qBound(rect.top(), position.y(), rect.bottom());
|
||||||
|
// Only start the timer if the mouse has left the window bound.
|
||||||
|
// The leave event is also triggered when the window looses focus.
|
||||||
|
if (x != position.x() || y != position.y()) {
|
||||||
|
mouse_constrain_timer.start();
|
||||||
|
}
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
|
int GRenderWindow::QtKeyToSwitchKey(Qt::Key qt_key) {
|
||||||
static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
|
static constexpr std::array<std::pair<Qt::Key, Settings::NativeKeyboard::Keys>, 106> key_map = {
|
||||||
std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
|
std::pair<Qt::Key, Settings::NativeKeyboard::Keys>{Qt::Key_A, Settings::NativeKeyboard::A},
|
||||||
|
@ -658,10 +678,19 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||||
input_subsystem->GetMouse()->TouchMove(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()->Move(pos.x(), pos.y(), center_x, center_y);
|
||||||
|
|
||||||
|
// Center mouse for mouse panning
|
||||||
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) {
|
||||||
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
|
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<int>(clamped_mouse_x), static_cast<int>(clamped_mouse_y)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
mouse_constrain_timer.stop();
|
||||||
emit MouseActivity();
|
emit MouseActivity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,6 +704,31 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||||
input_subsystem->GetMouse()->ReleaseButton(button);
|
input_subsystem->GetMouse()->ReleaseButton(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GRenderWindow::ConstrainMouse() {
|
||||||
|
if (emu_thread == nullptr || !Settings::values.mouse_panning) {
|
||||||
|
mouse_constrain_timer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->isActiveWindow()) {
|
||||||
|
mouse_constrain_timer.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Settings::values.mouse_enabled) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int center_x = width() / 2;
|
||||||
|
const int center_y = height() / 2;
|
||||||
|
|
||||||
|
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
|
||||||
|
}
|
||||||
|
|
||||||
void GRenderWindow::wheelEvent(QWheelEvent* event) {
|
void GRenderWindow::wheelEvent(QWheelEvent* event) {
|
||||||
const int x = event->angleDelta().x();
|
const int x = event->angleDelta().x();
|
||||||
const int y = event->angleDelta().y();
|
const int y = event->angleDelta().y();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <qglobal.h>
|
#include <qglobal.h>
|
||||||
#include <qnamespace.h>
|
#include <qnamespace.h>
|
||||||
|
@ -38,7 +39,6 @@ class QMouseEvent;
|
||||||
class QObject;
|
class QObject;
|
||||||
class QResizeEvent;
|
class QResizeEvent;
|
||||||
class QShowEvent;
|
class QShowEvent;
|
||||||
class QTimer;
|
|
||||||
class QTouchEvent;
|
class QTouchEvent;
|
||||||
class QWheelEvent;
|
class QWheelEvent;
|
||||||
|
|
||||||
|
@ -166,6 +166,7 @@ public:
|
||||||
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
|
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
|
||||||
|
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
void leaveEvent(QEvent* event) override;
|
||||||
|
|
||||||
void resizeEvent(QResizeEvent* event) override;
|
void resizeEvent(QResizeEvent* event) override;
|
||||||
|
|
||||||
|
@ -229,6 +230,7 @@ private:
|
||||||
void TouchBeginEvent(const QTouchEvent* event);
|
void TouchBeginEvent(const QTouchEvent* event);
|
||||||
void TouchUpdateEvent(const QTouchEvent* event);
|
void TouchUpdateEvent(const QTouchEvent* event);
|
||||||
void TouchEndEvent();
|
void TouchEndEvent();
|
||||||
|
void ConstrainMouse();
|
||||||
|
|
||||||
void RequestCameraCapture();
|
void RequestCameraCapture();
|
||||||
void OnCameraCapture(int requestId, const QImage& img);
|
void OnCameraCapture(int requestId, const QImage& img);
|
||||||
|
@ -268,6 +270,8 @@ private:
|
||||||
std::unique_ptr<QTimer> camera_timer;
|
std::unique_ptr<QTimer> camera_timer;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QTimer mouse_constrain_timer;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -185,7 +185,6 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
constexpr int default_mouse_hide_timeout = 2500;
|
constexpr int default_mouse_hide_timeout = 2500;
|
||||||
constexpr int default_mouse_center_timeout = 10;
|
|
||||||
constexpr int default_input_update_timeout = 1;
|
constexpr int default_input_update_timeout = 1;
|
||||||
|
|
||||||
constexpr size_t CopyBufferSize = 1_MiB;
|
constexpr size_t CopyBufferSize = 1_MiB;
|
||||||
|
@ -435,9 +434,6 @@ GMainWindow::GMainWindow(std::unique_ptr<Config> config_, bool has_broken_vulkan
|
||||||
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
|
connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
|
||||||
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
|
connect(ui->menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
|
||||||
|
|
||||||
mouse_center_timer.setInterval(default_mouse_center_timeout);
|
|
||||||
connect(&mouse_center_timer, &QTimer::timeout, this, &GMainWindow::CenterMouseCursor);
|
|
||||||
|
|
||||||
update_input_timer.setInterval(default_input_update_timeout);
|
update_input_timer.setInterval(default_input_update_timeout);
|
||||||
connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers);
|
connect(&update_input_timer, &QTimer::timeout, this, &GMainWindow::UpdateInputDrivers);
|
||||||
update_input_timer.start();
|
update_input_timer.start();
|
||||||
|
@ -1366,14 +1362,6 @@ void GMainWindow::InitializeHotkeys() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
|
connect_shortcut(QStringLiteral("Toggle Mouse Panning"), [&] {
|
||||||
if (Settings::values.mouse_enabled) {
|
|
||||||
Settings::values.mouse_panning = false;
|
|
||||||
QMessageBox::warning(
|
|
||||||
this, tr("Emulated mouse is enabled"),
|
|
||||||
tr("Real mouse input and mouse panning are incompatible. Please disable the "
|
|
||||||
"emulated mouse in input advanced settings to allow mouse panning."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Settings::values.mouse_panning = !Settings::values.mouse_panning;
|
Settings::values.mouse_panning = !Settings::values.mouse_panning;
|
||||||
if (Settings::values.mouse_panning) {
|
if (Settings::values.mouse_panning) {
|
||||||
render_window->installEventFilter(render_window);
|
render_window->installEventFilter(render_window);
|
||||||
|
@ -4693,26 +4681,10 @@ void GMainWindow::ShowMouseCursor() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::CenterMouseCursor() {
|
|
||||||
if (emu_thread == nullptr || !Settings::values.mouse_panning) {
|
|
||||||
mouse_center_timer.stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->isActiveWindow()) {
|
|
||||||
mouse_center_timer.stop();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const int center_x = render_window->width() / 2;
|
|
||||||
const int center_y = render_window->height() / 2;
|
|
||||||
|
|
||||||
QCursor::setPos(mapToGlobal(QPoint{center_x, center_y}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void GMainWindow::OnMouseActivity() {
|
void GMainWindow::OnMouseActivity() {
|
||||||
if (!Settings::values.mouse_panning) {
|
if (!Settings::values.mouse_panning) {
|
||||||
ShowMouseCursor();
|
ShowMouseCursor();
|
||||||
}
|
}
|
||||||
mouse_center_timer.stop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
||||||
|
@ -4988,22 +4960,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
|
||||||
AcceptDropEvent(event);
|
AcceptDropEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::leaveEvent(QEvent* event) {
|
|
||||||
if (Settings::values.mouse_panning) {
|
|
||||||
const QRect& rect = geometry();
|
|
||||||
QPoint position = QCursor::pos();
|
|
||||||
|
|
||||||
qint32 x = qBound(rect.left(), position.x(), rect.right());
|
|
||||||
qint32 y = qBound(rect.top(), position.y(), rect.bottom());
|
|
||||||
// Only start the timer if the mouse has left the window bound.
|
|
||||||
// The leave event is also triggered when the window looses focus.
|
|
||||||
if (x != position.x() || y != position.y()) {
|
|
||||||
mouse_center_timer.start();
|
|
||||||
}
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GMainWindow::ConfirmChangeGame() {
|
bool GMainWindow::ConfirmChangeGame() {
|
||||||
if (emu_thread == nullptr)
|
if (emu_thread == nullptr)
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -450,7 +450,6 @@ private:
|
||||||
void UpdateInputDrivers();
|
void UpdateInputDrivers();
|
||||||
void HideMouseCursor();
|
void HideMouseCursor();
|
||||||
void ShowMouseCursor();
|
void ShowMouseCursor();
|
||||||
void CenterMouseCursor();
|
|
||||||
void OpenURL(const QUrl& url);
|
void OpenURL(const QUrl& url);
|
||||||
void LoadTranslation();
|
void LoadTranslation();
|
||||||
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
|
void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
|
||||||
|
@ -532,7 +531,6 @@ private:
|
||||||
bool auto_paused = false;
|
bool auto_paused = false;
|
||||||
bool auto_muted = false;
|
bool auto_muted = false;
|
||||||
QTimer mouse_hide_timer;
|
QTimer mouse_hide_timer;
|
||||||
QTimer mouse_center_timer;
|
|
||||||
QTimer update_input_timer;
|
QTimer update_input_timer;
|
||||||
|
|
||||||
QString startup_icon_theme;
|
QString startup_icon_theme;
|
||||||
|
@ -589,5 +587,4 @@ protected:
|
||||||
void dropEvent(QDropEvent* event) override;
|
void dropEvent(QDropEvent* event) override;
|
||||||
void dragEnterEvent(QDragEnterEvent* event) override;
|
void dragEnterEvent(QDragEnterEvent* event) override;
|
||||||
void dragMoveEvent(QDragMoveEvent* event) override;
|
void dragMoveEvent(QDragMoveEvent* event) override;
|
||||||
void leaveEvent(QEvent* event) override;
|
|
||||||
};
|
};
|
||||||
|
|
Reference in New Issue