From e7664b7a4fe1035bc3c9afb51254bfff1f25654a Mon Sep 17 00:00:00 2001
From: FearlessTobi <thm.frey@gmail.com>
Date: Sat, 11 Apr 2020 04:22:50 +0200
Subject: [PATCH] yuzu: Option to hide mouse on inactivity

Co-Authored-By: Vitor K <vitor-k@users.noreply.github.com>
---
 src/yuzu/bootmanager.cpp                     |  4 ++
 src/yuzu/configuration/config.cpp            |  3 ++
 src/yuzu/configuration/configure_general.cpp |  2 +
 src/yuzu/configuration/configure_general.ui  |  7 +++
 src/yuzu/main.cpp                            | 56 ++++++++++++++++++++
 src/yuzu/main.h                              |  5 ++
 src/yuzu/uisettings.h                        |  1 +
 7 files changed, 78 insertions(+)

diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 1cac2f942f..3d759f77b2 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -292,6 +292,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
     setLayout(layout);
     InputCommon::Init();
 
+    this->setMouseTracking(true);
+
     connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
 }
 
@@ -385,6 +387,7 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
     } else if (event->button() == Qt::RightButton) {
         InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
     }
+    QWidget::mousePressEvent(event);
 }
 
 void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
@@ -397,6 +400,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
     const auto [x, y] = ScaleTouch(pos);
     this->TouchMoved(x, y);
     InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
+    QWidget::mouseMoveEvent(event);
 }
 
 void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 3b9ab38dd3..946aa287ab 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -740,6 +740,8 @@ void Config::ReadUIValues() {
     UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
     UISettings::values.pause_when_in_background =
         ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
+    UISettings::values.hide_mouse =
+        ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
 
     ApplyDefaultProfileIfInputInvalid();
 
@@ -1164,6 +1166,7 @@ void Config::SaveUIValues() {
     WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
     WriteSetting(QStringLiteral("pauseWhenInBackground"),
                  UISettings::values.pause_when_in_background, false);
+    WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
 
     qt_config->endGroup();
 }
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 5ef9271145..cb95423e09 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -26,6 +26,7 @@ void ConfigureGeneral::SetConfiguration() {
     ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);
     ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);
     ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);
+    ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse);
 
     ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit);
     ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked());
@@ -36,6 +37,7 @@ void ConfigureGeneral::ApplyConfiguration() {
     UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
     UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
     UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
+    UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked();
 
     Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked();
     Settings::values.frame_limit = ui->frame_limit->value();
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui
index 857119bb3b..fc3b7e65a5 100644
--- a/src/yuzu/configuration/configure_general.ui
+++ b/src/yuzu/configuration/configure_general.ui
@@ -72,6 +72,13 @@
             </property>
            </widget>
           </item>
+          <item>
+           <widget class="QCheckBox" name="toggle_hide_mouse">
+            <property name="text">
+             <string>Hide mouse on inactivity</string>
+            </property>
+           </widget>
+          </item>
          </layout>
         </item>
        </layout>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 05baec7e13..1e76f789c5 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -135,6 +135,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 }
 #endif
 
+constexpr int default_mouse_timeout = 2500;
+
 constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
 
 /**
@@ -236,6 +238,14 @@ GMainWindow::GMainWindow()
     // Show one-time "callout" messages to the user
     ShowTelemetryCallout();
 
+    // make sure menubar has the arrow cursor instead of inheriting from this
+    ui.menubar->setCursor(QCursor());
+    statusBar()->setCursor(QCursor());
+
+    mouse_hide_timer.setInterval(default_mouse_timeout);
+    connect(&mouse_hide_timer, &QTimer::timeout, this, &GMainWindow::HideMouseCursor);
+    connect(ui.menubar, &QMenuBar::hovered, this, &GMainWindow::ShowMouseCursor);
+
     QStringList args = QApplication::arguments();
     if (args.length() >= 2) {
         BootGame(args[1]);
@@ -1012,6 +1022,13 @@ void GMainWindow::BootGame(const QString& filename) {
     async_status_button->setDisabled(true);
     renderer_status_button->setDisabled(true);
 
+    if (UISettings::values.hide_mouse) {
+        mouse_hide_timer.start();
+        setMouseTracking(true);
+        ui.centralwidget->setMouseTracking(true);
+        ui.menubar->setMouseTracking(true);
+    }
+
     const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
 
     std::string title_name;
@@ -1080,6 +1097,10 @@ void GMainWindow::ShutdownGame() {
         game_list->show();
     game_list->setFilterFocus();
 
+    setMouseTracking(false);
+    ui.centralwidget->setMouseTracking(false);
+    ui.menubar->setMouseTracking(false);
+
     UpdateWindowTitle();
 
     // Disable status bar updates
@@ -1835,6 +1856,17 @@ void GMainWindow::OnConfigure() {
 
     config->Save();
 
+    if (UISettings::values.hide_mouse && emulation_running) {
+        setMouseTracking(true);
+        ui.centralwidget->setMouseTracking(true);
+        ui.menubar->setMouseTracking(true);
+        mouse_hide_timer.start();
+    } else {
+        setMouseTracking(false);
+        ui.centralwidget->setMouseTracking(false);
+        ui.menubar->setMouseTracking(false);
+    }
+
     dock_status_button->setChecked(Settings::values.use_docked_mode);
     async_status_button->setChecked(Settings::values.use_asynchronous_gpu_emulation);
 #ifdef HAS_VULKAN
@@ -1968,6 +2000,30 @@ void GMainWindow::UpdateStatusBar() {
     emu_frametime_label->setVisible(true);
 }
 
+void GMainWindow::HideMouseCursor() {
+    if (emu_thread == nullptr || UISettings::values.hide_mouse == false) {
+        mouse_hide_timer.stop();
+        ShowMouseCursor();
+        return;
+    }
+    setCursor(QCursor(Qt::BlankCursor));
+}
+
+void GMainWindow::ShowMouseCursor() {
+    unsetCursor();
+    if (emu_thread != nullptr && UISettings::values.hide_mouse) {
+        mouse_hide_timer.start();
+    }
+}
+
+void GMainWindow::mouseMoveEvent(QMouseEvent* event) {
+    ShowMouseCursor();
+}
+
+void GMainWindow::mousePressEvent(QMouseEvent* event) {
+    ShowMouseCursor();
+}
+
 void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
     QMessageBox::StandardButton answer;
     QString status_message;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 0b750689de..60b17c54a7 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -216,6 +216,8 @@ private:
     std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
     void UpdateWindowTitle(const QString& title_name = {});
     void UpdateStatusBar();
+    void HideMouseCursor();
+    void ShowMouseCursor();
 
     Ui::MainWindow ui;
 
@@ -244,6 +246,7 @@ private:
     QString game_path;
 
     bool auto_paused = false;
+    QTimer mouse_hide_timer;
 
     // FS
     std::shared_ptr<FileSys::VfsFilesystem> vfs;
@@ -265,4 +268,6 @@ protected:
     void dropEvent(QDropEvent* event) override;
     void dragEnterEvent(QDragEnterEvent* event) override;
     void dragMoveEvent(QDragMoveEvent* event) override;
+    void mouseMoveEvent(QMouseEvent* event) override;
+    void mousePressEvent(QMouseEvent* event) override;
 };
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index a675ecf4d7..830932d459 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -59,6 +59,7 @@ struct Values {
     bool confirm_before_closing;
     bool first_start;
     bool pause_when_in_background;
+    bool hide_mouse;
 
     bool select_user_on_boot;