From f3eaa950416971ccfd10f8e492c4b8b61b53e92d Mon Sep 17 00:00:00 2001
From: Susko3 <16479013+Susko3@users.noreply.github.com>
Date: Fri, 14 Jan 2022 23:16:07 +0100
Subject: [PATCH 1/4] Move input handler settings creation to `OsuGameBase`

---
 osu.Desktop/OsuGameDesktop.cs                 | 25 +++++++++++
 osu.Game/OsuGameBase.cs                       | 21 ++++++++++
 .../Settings/Sections/InputSection.cs         | 41 ++-----------------
 3 files changed, 49 insertions(+), 38 deletions(-)

diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs
index b234207848..a0c2a80bb8 100644
--- a/osu.Desktop/OsuGameDesktop.cs
+++ b/osu.Desktop/OsuGameDesktop.cs
@@ -20,8 +20,15 @@ using osu.Framework.Screens;
 using osu.Game.Screens.Menu;
 using osu.Game.Updater;
 using osu.Desktop.Windows;
+using osu.Framework.Input.Handlers;
+using osu.Framework.Input.Handlers.Joystick;
+using osu.Framework.Input.Handlers.Mouse;
+using osu.Framework.Input.Handlers.Tablet;
 using osu.Framework.Threading;
 using osu.Game.IO;
+using osu.Game.Overlays.Settings;
+using osu.Game.Overlays.Settings.Sections;
+using osu.Game.Overlays.Settings.Sections.Input;
 
 namespace osu.Desktop
 {
@@ -156,6 +163,24 @@ namespace osu.Desktop
             desktopWindow.DragDrop += f => fileDrop(new[] { f });
         }
 
+        public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
+        {
+            switch (handler)
+            {
+                case ITabletHandler th:
+                    return new TabletSettings(th);
+
+                case MouseHandler mh:
+                    return new MouseSettings(mh);
+
+                case JoystickHandler _:
+                    return new InputSection.HandlerSection(handler);
+
+                default:
+                    return base.CreateSettingsSubsectionFor(handler);
+            }
+        }
+
         private readonly List<string> importableFiles = new List<string>();
         private ScheduledDelegate importSchedule;
 
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 9256514a0a..0059fc2204 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -17,6 +17,8 @@ using osu.Framework.Graphics.Containers;
 using osu.Framework.Graphics.Performance;
 using osu.Framework.Graphics.Textures;
 using osu.Framework.Input;
+using osu.Framework.Input.Handlers;
+using osu.Framework.Input.Handlers.Midi;
 using osu.Framework.IO.Stores;
 using osu.Framework.Logging;
 using osu.Framework.Platform;
@@ -35,6 +37,8 @@ using osu.Game.Online.Chat;
 using osu.Game.Online.Multiplayer;
 using osu.Game.Online.Spectator;
 using osu.Game.Overlays;
+using osu.Game.Overlays.Settings;
+using osu.Game.Overlays.Settings.Sections;
 using osu.Game.Resources;
 using osu.Game.Rulesets;
 using osu.Game.Rulesets.Mods;
@@ -448,6 +452,23 @@ namespace osu.Game
 
         protected override Storage CreateStorage(GameHost host, Storage defaultStorage) => new OsuStorage(host, defaultStorage);
 
+        /// <summary>
+        /// Creates an input settings subsection for an <see cref="InputHandler"/>.
+        /// </summary>
+        /// <remarks>Should be overriden per-platform to provide settings for platform-specific handlers.</remarks>
+        public virtual SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
+        {
+            switch (handler)
+            {
+                case MidiHandler _:
+                    return new InputSection.HandlerSection(handler);
+
+                // return null for handlers that shouldn't have settings.
+                default:
+                    return null;
+            }
+        }
+
         private void onRulesetChanged(ValueChangedEvent<RulesetInfo> r)
         {
             if (r.NewValue?.Available != true)
diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs
index d282ba5318..77076ae920 100644
--- a/osu.Game/Overlays/Settings/Sections/InputSection.cs
+++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs
@@ -5,10 +5,6 @@ using osu.Framework.Allocation;
 using osu.Framework.Graphics;
 using osu.Framework.Graphics.Sprites;
 using osu.Framework.Input.Handlers;
-using osu.Framework.Input.Handlers.Joystick;
-using osu.Framework.Input.Handlers.Midi;
-using osu.Framework.Input.Handlers.Mouse;
-using osu.Framework.Input.Handlers.Tablet;
 using osu.Framework.Localisation;
 using osu.Framework.Platform;
 using osu.Game.Localisation;
@@ -22,9 +18,6 @@ namespace osu.Game.Overlays.Settings.Sections
 
         public override LocalisableString Header => InputSettingsStrings.InputSectionHeader;
 
-        [Resolved]
-        private GameHost host { get; set; }
-
         public override Drawable CreateIcon() => new SpriteIcon
         {
             Icon = FontAwesome.Solid.Keyboard
@@ -36,7 +29,7 @@ namespace osu.Game.Overlays.Settings.Sections
         }
 
         [BackgroundDependencyLoader]
-        private void load()
+        private void load(GameHost host, OsuGameBase game)
         {
             Children = new Drawable[]
             {
@@ -45,42 +38,14 @@ namespace osu.Game.Overlays.Settings.Sections
 
             foreach (var handler in host.AvailableInputHandlers)
             {
-                var handlerSection = createSectionFor(handler);
+                var handlerSection = game.CreateSettingsSubsectionFor(handler);
 
                 if (handlerSection != null)
                     Add(handlerSection);
             }
         }
 
-        private SettingsSubsection createSectionFor(InputHandler handler)
-        {
-            SettingsSubsection section;
-
-            switch (handler)
-            {
-                // ReSharper disable once SuspiciousTypeConversion.Global (net standard fuckery)
-                case ITabletHandler th:
-                    section = new TabletSettings(th);
-                    break;
-
-                case MouseHandler mh:
-                    section = new MouseSettings(mh);
-                    break;
-
-                // whitelist the handlers which should be displayed to avoid any weird cases of users touching settings they shouldn't.
-                case JoystickHandler _:
-                case MidiHandler _:
-                    section = new HandlerSection(handler);
-                    break;
-
-                default:
-                    return null;
-            }
-
-            return section;
-        }
-
-        private class HandlerSection : SettingsSubsection
+        public class HandlerSection : SettingsSubsection
         {
             private readonly InputHandler handler;
 

From 037e56f13eca292e9b8b4a43a0eb4da262ac0fa8 Mon Sep 17 00:00:00 2001
From: Susko3 <16479013+Susko3@users.noreply.github.com>
Date: Sat, 15 Jan 2022 14:38:38 +0100
Subject: [PATCH 2/4] Add Android mouse settings

---
 osu.Android/AndroidMouseSettings.cs           | 97 +++++++++++++++++++
 osu.Android/OsuGameAndroid.cs                 | 22 +++++
 osu.Android/osu.Android.csproj                |  1 +
 .../Settings/Sections/Input/MouseSettings.cs  |  4 +-
 4 files changed, 122 insertions(+), 2 deletions(-)
 create mode 100644 osu.Android/AndroidMouseSettings.cs

diff --git a/osu.Android/AndroidMouseSettings.cs b/osu.Android/AndroidMouseSettings.cs
new file mode 100644
index 0000000000..7dff929cd4
--- /dev/null
+++ b/osu.Android/AndroidMouseSettings.cs
@@ -0,0 +1,97 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Android.OS;
+using osu.Framework.Allocation;
+using osu.Framework.Android.Input;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Localisation;
+using osu.Game.Configuration;
+using osu.Game.Localisation;
+using osu.Game.Overlays.Settings;
+using osu.Game.Overlays.Settings.Sections.Input;
+
+namespace osu.Android
+{
+    public class AndroidMouseSettings : SettingsSubsection
+    {
+        private readonly AndroidMouseHandler mouseHandler;
+
+        protected override LocalisableString Header => MouseSettingsStrings.Mouse;
+
+        private Bindable<double> handlerSensitivity;
+
+        private Bindable<double> localSensitivity;
+
+        private Bindable<bool> relativeMode;
+
+        public AndroidMouseSettings(AndroidMouseHandler mouseHandler)
+        {
+            this.mouseHandler = mouseHandler;
+        }
+
+        [BackgroundDependencyLoader]
+        private void load(OsuConfigManager osuConfig)
+        {
+            // use local bindable to avoid changing enabled state of game host's bindable.
+            handlerSensitivity = mouseHandler.Sensitivity.GetBoundCopy();
+            localSensitivity = handlerSensitivity.GetUnboundCopy();
+
+            relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy();
+
+            // High precision/pointer capture is only available on Android 8.0 and up
+            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
+            {
+                AddRange(new Drawable[]
+                {
+                    new SettingsCheckbox
+                    {
+                        LabelText = MouseSettingsStrings.HighPrecisionMouse,
+                        TooltipText = MouseSettingsStrings.HighPrecisionMouseTooltip,
+                        Current = relativeMode,
+                        Keywords = new[] { @"raw", @"input", @"relative", @"cursor", @"captured", @"pointer" },
+                    },
+                    new MouseSettings.SensitivitySetting
+                    {
+                        LabelText = MouseSettingsStrings.CursorSensitivity,
+                        Current = localSensitivity,
+                    },
+                });
+            }
+
+            AddRange(new Drawable[]
+            {
+                new SettingsCheckbox
+                {
+                    LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
+                    TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
+                    Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel),
+                },
+                new SettingsCheckbox
+                {
+                    LabelText = MouseSettingsStrings.DisableMouseButtons,
+                    Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
+                },
+            });
+        }
+
+        protected override void LoadComplete()
+        {
+            base.LoadComplete();
+
+            relativeMode.BindValueChanged(relative => localSensitivity.Disabled = !relative.NewValue, true);
+
+            handlerSensitivity.BindValueChanged(val =>
+            {
+                bool disabled = localSensitivity.Disabled;
+
+                localSensitivity.Disabled = false;
+                localSensitivity.Value = val.NewValue;
+                localSensitivity.Disabled = disabled;
+            }, true);
+
+            localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue);
+        }
+    }
+}
diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs
index 050bf2b787..ba2527d71e 100644
--- a/osu.Android/OsuGameAndroid.cs
+++ b/osu.Android/OsuGameAndroid.cs
@@ -5,7 +5,11 @@ using System;
 using Android.App;
 using Android.OS;
 using osu.Framework.Allocation;
+using osu.Framework.Android.Input;
+using osu.Framework.Input.Handlers;
+using osu.Framework.Platform;
 using osu.Game;
+using osu.Game.Overlays.Settings;
 using osu.Game.Updater;
 using osu.Game.Utils;
 using Xamarin.Essentials;
@@ -73,10 +77,28 @@ namespace osu.Android
             LoadComponentAsync(new GameplayScreenRotationLocker(), Add);
         }
 
+        public override void SetHost(GameHost host)
+        {
+            base.SetHost(host);
+            host.Window.CursorState |= CursorState.Hidden;
+        }
+
         protected override UpdateManager CreateUpdateManager() => new SimpleUpdateManager();
 
         protected override BatteryInfo CreateBatteryInfo() => new AndroidBatteryInfo();
 
+        public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
+        {
+            switch (handler)
+            {
+                case AndroidMouseHandler mh:
+                    return new AndroidMouseSettings(mh);
+
+                default:
+                    return base.CreateSettingsSubsectionFor(handler);
+            }
+        }
+
         private class AndroidBatteryInfo : BatteryInfo
         {
             public override double ChargeLevel => Battery.ChargeLevel;
diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj
index fc50ca9fa1..90b02c527b 100644
--- a/osu.Android/osu.Android.csproj
+++ b/osu.Android/osu.Android.csproj
@@ -26,6 +26,7 @@
     <EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
   </PropertyGroup>
   <ItemGroup>
+    <Compile Include="AndroidMouseSettings.cs" />
     <Compile Include="GameplayScreenRotationLocker.cs" />
     <Compile Include="OsuGameActivity.cs" />
     <Compile Include="OsuGameAndroid.cs" />
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 4235dc0a05..6bb97c1137 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
             }, true);
         }
 
-        private class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
+        public class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
         {
             public SensitivitySetting()
             {
@@ -133,7 +133,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
             }
         }
 
-        private class SensitivitySlider : OsuSliderBar<double>
+        public class SensitivitySlider : OsuSliderBar<double>
         {
             public override LocalisableString TooltipText => Current.Disabled ? MouseSettingsStrings.EnableHighPrecisionForSensitivityAdjust : $"{base.TooltipText}x";
         }

From d4af8498af6795213d648844d1c11da99f65f4e6 Mon Sep 17 00:00:00 2001
From: Susko3 <16479013+Susko3@users.noreply.github.com>
Date: Sat, 15 Jan 2022 14:48:41 +0100
Subject: [PATCH 3/4] Add iOS mouse settings

Functionality is currently limited to some OsuSettings, but will expand in
the future when high precision mouse is added.
---
 osu.iOS/IOSMouseSettings.cs | 36 ++++++++++++++++++++++++++++++++++++
 osu.iOS/OsuGameIOS.cs       | 15 +++++++++++++++
 osu.iOS/osu.iOS.csproj      |  1 +
 3 files changed, 52 insertions(+)
 create mode 100644 osu.iOS/IOSMouseSettings.cs

diff --git a/osu.iOS/IOSMouseSettings.cs b/osu.iOS/IOSMouseSettings.cs
new file mode 100644
index 0000000000..1979a881f7
--- /dev/null
+++ b/osu.iOS/IOSMouseSettings.cs
@@ -0,0 +1,36 @@
+// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Localisation;
+using osu.Game.Configuration;
+using osu.Game.Localisation;
+using osu.Game.Overlays.Settings;
+
+namespace osu.iOS
+{
+    public class IOSMouseSettings : SettingsSubsection
+    {
+        protected override LocalisableString Header => MouseSettingsStrings.Mouse;
+
+        [BackgroundDependencyLoader]
+        private void load(OsuConfigManager osuConfig)
+        {
+            Children = new Drawable[]
+            {
+                new SettingsCheckbox
+                {
+                    LabelText = MouseSettingsStrings.DisableMouseWheelVolumeAdjust,
+                    TooltipText = MouseSettingsStrings.DisableMouseWheelVolumeAdjustTooltip,
+                    Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableWheel),
+                },
+                new SettingsCheckbox
+                {
+                    LabelText = MouseSettingsStrings.DisableMouseButtons,
+                    Current = osuConfig.GetBindable<bool>(OsuSetting.MouseDisableButtons),
+                },
+            };
+        }
+    }
+}
diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs
index 702aef45f5..c3f7033752 100644
--- a/osu.iOS/OsuGameIOS.cs
+++ b/osu.iOS/OsuGameIOS.cs
@@ -3,7 +3,10 @@
 
 using System;
 using Foundation;
+using osu.Framework.Input.Handlers;
+using osu.Framework.iOS.Input;
 using osu.Game;
+using osu.Game.Overlays.Settings;
 using osu.Game.Updater;
 using osu.Game.Utils;
 using Xamarin.Essentials;
@@ -18,6 +21,18 @@ namespace osu.iOS
 
         protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
 
+        public override SettingsSubsection CreateSettingsSubsectionFor(InputHandler handler)
+        {
+            switch (handler)
+            {
+                case IOSMouseHandler _:
+                    return new IOSMouseSettings();
+
+                default:
+                    return base.CreateSettingsSubsectionFor(handler);
+            }
+        }
+
         private class IOSBatteryInfo : BatteryInfo
         {
             public override double ChargeLevel => Battery.ChargeLevel;
diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj
index 1203c3659b..b9da874f30 100644
--- a/osu.iOS/osu.iOS.csproj
+++ b/osu.iOS/osu.iOS.csproj
@@ -23,6 +23,7 @@
   <ItemGroup>
     <Compile Include="Application.cs" />
     <Compile Include="AppDelegate.cs" />
+    <Compile Include="IOSMouseSettings.cs" />
     <Compile Include="OsuGameIOS.cs" />
   </ItemGroup>
   <ItemGroup>

From 345ae7bbc3ec287f60d5a38fb2202d674aad98b1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= <dach.bartlomiej@gmail.com>
Date: Tue, 21 Jun 2022 00:46:52 +0200
Subject: [PATCH 4/4] Fix build errors after implicit NRT enable

---
 osu.Android/AndroidMouseSettings.cs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/osu.Android/AndroidMouseSettings.cs b/osu.Android/AndroidMouseSettings.cs
index 7dff929cd4..54b787fd17 100644
--- a/osu.Android/AndroidMouseSettings.cs
+++ b/osu.Android/AndroidMouseSettings.cs
@@ -20,11 +20,11 @@ namespace osu.Android
 
         protected override LocalisableString Header => MouseSettingsStrings.Mouse;
 
-        private Bindable<double> handlerSensitivity;
+        private Bindable<double> handlerSensitivity = null!;
 
-        private Bindable<double> localSensitivity;
+        private Bindable<double> localSensitivity = null!;
 
-        private Bindable<bool> relativeMode;
+        private Bindable<bool> relativeMode = null!;
 
         public AndroidMouseSettings(AndroidMouseHandler mouseHandler)
         {