From 6f1d9648016c9442f6dcdac257f28c1a7a19aca0 Mon Sep 17 00:00:00 2001
From: pineappleEA <67879877+pineappleEA@users.noreply.github.com>
Date: Mon, 15 Feb 2021 23:48:21 +0200
Subject: [PATCH] Hide Cursor On Idle (#1993)

* Implement "Hide Cursor On Idle" option

Adds a general option to autohide the cursor after 8s have elapsed.

* Fix cursor not hiding on Windows and dispose it

* Don't dispose cursor, fix var names

* Abide by the GNOME documentation

* Fix nits

* Disabled by default, make it so it doesn't utilize any timer

* Remove *NIX time and extra lines

* Don't calculate if option is disabled

* Move if case

* Fix alignment
---
 .../Configuration/ConfigurationFileFormat.cs  |  9 +++--
 .../Configuration/ConfigurationState.cs       | 18 ++++++++++
 Ryujinx/Config.json                           |  3 +-
 Ryujinx/Ui/GLRenderer.cs                      | 34 +++++++++++++++++++
 Ryujinx/Ui/Windows/SettingsWindow.cs          |  9 ++++-
 Ryujinx/Ui/Windows/SettingsWindow.glade       | 18 +++++++++-
 Ryujinx/_schema.json                          | 11 ++++++
 7 files changed, 97 insertions(+), 5 deletions(-)

diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index 79993d879f..901c823e5a 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
         /// <summary>
         /// The current version of the file format
         /// </summary>
-        public const int CurrentVersion = 20;
+        public const int CurrentVersion = 22;
 
         public int Version { get; set; }
 
@@ -133,6 +133,11 @@ namespace Ryujinx.Configuration
         /// </summary>
         public bool ShowConfirmExit { get; set; }
 
+        /// <summary>
+        /// Hide Cursor on Idle
+        /// </summary>
+        public bool HideCursorOnIdle { get; set; }
+
         /// <summary>
         /// Enables or disables Vertical Sync
         /// </summary>
@@ -253,4 +258,4 @@ namespace Ryujinx.Configuration
             JsonHelper.Serialize(fileStream, this, true);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index 61cc8f899e..d51ee9efc6 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -365,6 +365,11 @@ namespace Ryujinx.Configuration
         /// </summary>
         public ReactiveObject<bool> ShowConfirmExit { get; private set; }
 
+        /// <summary>
+        /// Hide Cursor on Idle
+        /// </summary>
+        public ReactiveObject<bool> HideCursorOnIdle { get; private set; }
+
         private ConfigurationState()
         {
             Ui                       = new UiSection();
@@ -375,6 +380,7 @@ namespace Ryujinx.Configuration
             EnableDiscordIntegration = new ReactiveObject<bool>();
             CheckUpdatesOnStart      = new ReactiveObject<bool>();
             ShowConfirmExit          = new ReactiveObject<bool>();
+            HideCursorOnIdle         = new ReactiveObject<bool>();
         }
 
         public ConfigurationFileFormat ToFileFormat()
@@ -420,6 +426,7 @@ namespace Ryujinx.Configuration
                 EnableDiscordIntegration  = EnableDiscordIntegration,
                 CheckUpdatesOnStart       = CheckUpdatesOnStart,
                 ShowConfirmExit           = ShowConfirmExit,
+                HideCursorOnIdle          = HideCursorOnIdle,
                 EnableVsync               = Graphics.EnableVsync,
                 EnableShaderCache         = Graphics.EnableShaderCache,
                 EnablePtc                 = System.EnablePtc,
@@ -483,6 +490,7 @@ namespace Ryujinx.Configuration
             EnableDiscordIntegration.Value         = true;
             CheckUpdatesOnStart.Value              = true;
             ShowConfirmExit.Value                  = true;
+            HideCursorOnIdle.Value                 = false;
             Graphics.EnableVsync.Value             = true;
             Graphics.EnableShaderCache.Value       = true;
             System.EnablePtc.Value                 = true;
@@ -787,6 +795,15 @@ namespace Ryujinx.Configuration
                 configurationFileUpdated = true;
             }
 
+            if (configurationFileFormat.Version < 22)
+            {
+                Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 22.");
+
+                configurationFileFormat.HideCursorOnIdle = false;
+
+                configurationFileUpdated = true;
+            }
+
             List<InputConfig> inputConfig = new List<InputConfig>();
             inputConfig.AddRange(configurationFileFormat.ControllerConfig);
             inputConfig.AddRange(configurationFileFormat.KeyboardConfig);
@@ -814,6 +831,7 @@ namespace Ryujinx.Configuration
             EnableDiscordIntegration.Value         = configurationFileFormat.EnableDiscordIntegration;
             CheckUpdatesOnStart.Value              = configurationFileFormat.CheckUpdatesOnStart;
             ShowConfirmExit.Value                  = configurationFileFormat.ShowConfirmExit;
+            HideCursorOnIdle.Value                 = configurationFileFormat.HideCursorOnIdle;
             Graphics.EnableVsync.Value             = configurationFileFormat.EnableVsync;
             Graphics.EnableShaderCache.Value       = configurationFileFormat.EnableShaderCache;
             System.EnablePtc.Value                 = configurationFileFormat.EnablePtc;
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 76c0a139aa..804b020c4d 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,5 +1,5 @@
 {
-  "version": 20,
+  "version": 22,
   "res_scale": 1,
   "res_scale_custom": 1,
   "max_anisotropy": -1,
@@ -22,6 +22,7 @@
   "enable_discord_integration": true,
   "check_updates_on_start": true,
   "show_confirm_exit": true,
+  "hide_cursor_on_idle": false,
   "enable_vsync": true,
   "enable_shader_cache": true,
   "enable_ptc": true,
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index 35c00a332f..52dcedbc9a 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -42,6 +42,8 @@ namespace Ryujinx.Ui
         private double _mouseY;
         private bool   _mousePressed;
 
+        private DateTime _lastCursorMoveTime = DateTime.Now;
+
         private bool _toggleFullscreen;
         private bool _toggleDockedMode;
 
@@ -62,6 +64,8 @@ namespace Ryujinx.Ui
         private GraphicsDebugLevel _glLogLevel;
 
         private readonly ManualResetEvent _exitEvent;
+        
+        private Gdk.Cursor _invisibleCursor = new Gdk.Cursor (Gdk.Display.Default, Gdk.CursorType.BlankCursor);
 
         public GlRenderer(Switch device, GraphicsDebugLevel glLogLevel)
             : base (GetGraphicsMode(),
@@ -304,9 +308,37 @@ namespace Ryujinx.Ui
                 _mouseY = evnt.Y;
             }
 
+            ResetCursorIdle();
+
             return false;
         }
 
+        private void ResetCursorIdle()
+        {
+           if (ConfigurationState.Instance.HideCursorOnIdle)
+           {
+               _lastCursorMoveTime = DateTime.Now;
+           }
+
+           if (Window.Cursor != null)
+           {
+               Window.Cursor = null;
+           }
+        }
+
+        private void HideCursorIdle()
+        {
+           if (ConfigurationState.Instance.HideCursorOnIdle)
+           {
+               TimeSpan elapsedTime = DateTime.Now.Subtract(_lastCursorMoveTime);
+
+               if (elapsedTime.TotalSeconds > 8)
+               {
+                   Gtk.Application.Invoke(delegate { Window.Cursor = _invisibleCursor; });
+               }
+           }
+        }
+
         protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight)
         {
             Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
@@ -485,6 +517,8 @@ namespace Ryujinx.Ui
 
             MotionDevice motionDevice = new MotionDevice(_dsuClient);
 
+            HideCursorIdle();
+
             foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
             {
                 ControllerKeys   currentButton = 0;
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index ba64226c37..e839a366c6 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -43,6 +43,7 @@ namespace Ryujinx.Ui.Windows
         [GUI] CheckButton     _discordToggle;
         [GUI] CheckButton     _checkUpdatesToggle;
         [GUI] CheckButton     _showConfirmExitToggle;
+        [GUI] CheckButton     _hideCursorOnIdleToggle;
         [GUI] CheckButton     _vSyncToggle;
         [GUI] CheckButton     _shaderCacheToggle;
         [GUI] CheckButton     _ptcToggle;
@@ -185,6 +186,11 @@ namespace Ryujinx.Ui.Windows
                 _showConfirmExitToggle.Click();
             }
 
+            if (ConfigurationState.Instance.HideCursorOnIdle)
+            {
+                _hideCursorOnIdleToggle.Click();
+            }
+
             if (ConfigurationState.Instance.Graphics.EnableVsync)
             {
                 _vSyncToggle.Click();
@@ -403,6 +409,7 @@ namespace Ryujinx.Ui.Windows
             ConfigurationState.Instance.EnableDiscordIntegration.Value         = _discordToggle.Active;
             ConfigurationState.Instance.CheckUpdatesOnStart.Value              = _checkUpdatesToggle.Active;
             ConfigurationState.Instance.ShowConfirmExit.Value                  = _showConfirmExitToggle.Active;
+            ConfigurationState.Instance.HideCursorOnIdle.Value                 = _hideCursorOnIdleToggle.Active;
             ConfigurationState.Instance.Graphics.EnableVsync.Value             = _vSyncToggle.Active;
             ConfigurationState.Instance.Graphics.EnableShaderCache.Value       = _shaderCacheToggle.Active;
             ConfigurationState.Instance.System.EnablePtc.Value                 = _ptcToggle.Active;
@@ -582,4 +589,4 @@ namespace Ryujinx.Ui.Windows
             Dispose();
         }
     }
-}
\ No newline at end of file
+}
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade
index 5ead8dd820..e9d241f80b 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.glade
+++ b/Ryujinx/Ui/Windows/SettingsWindow.glade
@@ -150,7 +150,23 @@
                                     <property name="expand">False</property>
                                     <property name="fill">True</property>
                                     <property name="padding">5</property>
-                                    <property name="position">1</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkCheckButton" id="_hideCursorOnIdleToggle">
+                                    <property name="label" translatable="yes">Hide Cursor On Idle</property>
+                                    <property name="visible">True</property>
+                                    <property name="can-focus">True</property>
+                                    <property name="receives-default">False</property>
+                                    <property name="halign">start</property>
+                                    <property name="draw-indicator">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="padding">5</property>
+                                    <property name="position">3</property>
                                   </packing>
                                 </child>
                               </object>
diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json
index eff1f9d4cf..b61e9ed259 100644
--- a/Ryujinx/_schema.json
+++ b/Ryujinx/_schema.json
@@ -1209,6 +1209,17 @@
         true,
         false
       ]
+    },
+      "hide_cursor_on_idle": {
+      "$id": "#/properties/hide_cursor_on_idle",
+      "type": "boolean",
+      "title": "Hide Cursor On Idle",
+      "description": "Hides the cursor after being idle for 5 seconds",
+      "default": false,
+      "examples": [
+        true,
+        false
+      ]
     },
     "enable_vsync": {
       "$id": "#/properties/enable_vsync",