diff --git a/SteamController/App.config b/SteamController/App.config new file mode 100644 index 0000000..fdc215d --- /dev/null +++ b/SteamController/App.config @@ -0,0 +1,15 @@ + + + + +
+ + + + + + True + + + + \ No newline at end of file diff --git a/SteamController/Controller.cs b/SteamController/Controller.cs index 3de4f1e..6871814 100644 --- a/SteamController/Controller.cs +++ b/SteamController/Controller.cs @@ -2,6 +2,7 @@ using ExternalHelpers; using SteamController.Profiles; using System.ComponentModel; +using System.Diagnostics; using System.Runtime.InteropServices; namespace SteamController @@ -15,9 +16,25 @@ namespace SteamController NotifyIcon notifyIcon; StartupManager startupManager = new StartupManager(Title); - Context context; + Context context = new Context() + { + Profiles = { + new Profiles.SteamShortcutsProfile(), + new Profiles.DesktopProfile(), + new Profiles.ProcessProfile(), + new Profiles.SteamDetectProfile(), + new Profiles.X360Profile(), + new Profiles.DebugProfile() + } + }; + Thread? contextThread; bool running = true; + Stopwatch stopwatch = new Stopwatch(); + int updatesReceived = 0; + int lastUpdatesReceived = 0; + TimeSpan lastUpdatesReset; + readonly TimeSpan updateResetInterval = TimeSpan.FromSeconds(1); [DllImport("sas.dll")] static extern void SendSAS(bool asUser); @@ -25,46 +42,45 @@ namespace SteamController public Controller() { Instance.RunOnce(TitleWithVersion, "Global\\SteamController"); - SendSAS(true); - context = new Context() - { - Profiles = { - new Profiles.SteamShortcutsProfile(), - new Profiles.DesktopProfile(), - new Profiles.X360Profile(), - new Profiles.DebugProfile() - } - }; - var contextMenu = new ContextMenuStrip(components); + var enabledItem = new ToolStripMenuItem("&Enabled"); + enabledItem.Checked = context.RequestEnable; + enabledItem.Click += delegate { enabledItem.Checked = context.RequestEnable = !context.RequestEnable; }; + contextMenu.Items.Add(enabledItem); + + var desktopModeItem = new ToolStripMenuItem("&Desktop Mode"); + desktopModeItem.Checked = context.RequestDesktopMode; + desktopModeItem.Click += delegate { desktopModeItem.Checked = context.RequestDesktopMode = !context.RequestDesktopMode; }; + contextMenu.Items.Add(desktopModeItem); + + var steamDetectionItem = new ToolStripMenuItem("Auto-disable on &Steam"); + steamDetectionItem.Checked = Settings.Default.EnableSteamDetection; + steamDetectionItem.Click += delegate + { + steamDetectionItem.Checked = Settings.Default.EnableSteamDetection = !Settings.Default.EnableSteamDetection; + Settings.Default.Save(); + }; + contextMenu.Items.Add(steamDetectionItem); + contextMenu.Items.Add(new ToolStripSeparator()); + if (startupManager.IsAvailable) { var startupItem = new ToolStripMenuItem("Run On Startup"); startupItem.Checked = startupManager.Startup; - startupItem.Click += delegate - { - startupManager.Startup = !startupManager.Startup; - startupItem.Checked = startupManager.Startup; - }; + startupItem.Click += delegate { startupItem.Checked = startupManager.Startup = !startupManager.Startup; }; contextMenu.Items.Add(startupItem); } var helpItem = contextMenu.Items.Add("&Help"); - helpItem.Click += delegate - { - System.Diagnostics.Process.Start("explorer.exe", "http://github.com/ayufan-research/steam-deck-tools"); - }; + helpItem.Click += delegate { Process.Start("explorer.exe", "http://github.com/ayufan-research/steam-deck-tools"); }; contextMenu.Items.Add(new ToolStripSeparator()); var exitItem = contextMenu.Items.Add("&Exit"); - exitItem.Click += delegate - { - Application.Exit(); - }; + exitItem.Click += delegate { Application.Exit(); }; notifyIcon = new NotifyIcon(components); notifyIcon.Icon = Resources.microsoft_xbox_controller_off; @@ -77,6 +93,8 @@ namespace SteamController contextStateUpdate.Enabled = true; contextStateUpdate.Tick += ContextStateUpdate_Tick; + stopwatch.Start(); + contextThread = new Thread(ContextState_Update); contextThread.Start(); } @@ -85,17 +103,37 @@ namespace SteamController { while (running) { - context.Update(); + if (lastUpdatesReset + updateResetInterval < stopwatch.Elapsed) + { + lastUpdatesReset = stopwatch.Elapsed; + lastUpdatesReceived = updatesReceived; + updatesReceived = 0; + } + + updatesReceived++; + + lock (context) + { + context.Update(); + } + + if (!context.Enabled) + { + Thread.Sleep(100); + } } } private void ContextStateUpdate_Tick(object? sender, EventArgs e) { - context.X360.CreateClient(); + lock (context) + { + context.Tick(); + } if (!context.Mouse.Valid) { - notifyIcon.Text = TitleWithVersion + ". Cannot send input"; + notifyIcon.Text = TitleWithVersion + ". Cannot send input."; notifyIcon.Icon = Resources.microsoft_xbox_controller_off_red; } else if (!context.X360.Valid) @@ -103,20 +141,28 @@ namespace SteamController notifyIcon.Text = TitleWithVersion + ". Missing ViGEm?"; notifyIcon.Icon = Resources.microsoft_xbox_controller_red; } - else if (context.DesktopMode) + else if (context.Enabled) { - notifyIcon.Icon = Resources.microsoft_xbox_controller_off; - notifyIcon.Text = TitleWithVersion + ". Desktop mode"; + notifyIcon.Icon = context.DesktopMode ? Resources.monitor : Resources.microsoft_xbox_controller; + notifyIcon.Text = TitleWithVersion; + } + else if (context.DisableDueToSteam) + { + notifyIcon.Icon = context.DesktopMode ? Resources.monitor_off : Resources.microsoft_xbox_controller_off; + notifyIcon.Text = TitleWithVersion + ". Steam Detected"; } else { - notifyIcon.Icon = Resources.microsoft_xbox_controller; - notifyIcon.Text = TitleWithVersion; + notifyIcon.Icon = context.DesktopMode ? Resources.monitor_off : Resources.microsoft_xbox_controller_off; + notifyIcon.Text = TitleWithVersion + ". Disabled"; } + + notifyIcon.Text += String.Format(". Updates: {0}/s", lastUpdatesReceived); } public void Dispose() { + notifyIcon.Visible = false; running = false; if (contextThread != null) diff --git a/SteamController/Devices/SteamControllerActions.cs b/SteamController/Devices/SteamControllerActions.cs index 66148fc..3bbc68c 100644 --- a/SteamController/Devices/SteamControllerActions.cs +++ b/SteamController/Devices/SteamControllerActions.cs @@ -254,7 +254,10 @@ namespace SteamController.Devices } public static implicit operator bool(SteamAxis button) => button.Active; - public static implicit operator short(SteamAxis button) => button.Value; + public static implicit operator short(SteamAxis button) + { + return Math.Abs(button.Value) > button.Deadzone ? button.Value : (short)0; + } public bool Active { diff --git a/SteamController/Devices/Xbox360Controller.cs b/SteamController/Devices/Xbox360Controller.cs index ac27667..f8b9ed0 100644 --- a/SteamController/Devices/Xbox360Controller.cs +++ b/SteamController/Devices/Xbox360Controller.cs @@ -22,7 +22,7 @@ namespace SteamController.Devices using (client) { } } - internal bool CreateClient() + internal bool Tick() { if (this.device is not null) return true; diff --git a/SteamController/Helpers/SteamManager.cs b/SteamController/Helpers/SteamManager.cs new file mode 100644 index 0000000..8face96 --- /dev/null +++ b/SteamController/Helpers/SteamManager.cs @@ -0,0 +1,61 @@ +using System.Diagnostics; +using Microsoft.Win32; + +namespace SteamController.Helpers +{ + internal static class SteamManager + { + public const String SteamKey = @"Software\Valve\Steam"; + public const String RunningAppIDValue = @"RunningAppID"; + public const String BigPictureInForegroundValue = @"BigPictureInForeground"; + + public const String ActiveProcessKey = @"Software\Valve\Steam\ActiveProcess"; + public const String PIDValue = @"pid"; + + public static bool? IsRunning + { + get + { + var value = GetValue(ActiveProcessKey, PIDValue); + if (value is null) + return null; + if (Process.GetProcessById(value.Value) is null) + return false; + return true; + } + } + + public static bool? IsBigPictureMode + { + get + { + var value = GetValue(SteamKey, BigPictureInForegroundValue); + return value.HasValue ? value != 0 : null; + } + } + + public static bool? IsRunningGame + { + get + { + var value = GetValue(SteamKey, RunningAppIDValue); + return value.HasValue ? value != 0 : null; + } + } + + private static T? GetValue(string key, string value) where T : struct + { + try + { + using (var registryKey = Registry.CurrentUser.OpenSubKey(key)) + { + return registryKey?.GetValue(value) as T?; + } + } + catch (Exception) + { + return null; + } + } + } +} \ No newline at end of file diff --git a/SteamController/Profiles/Context.cs b/SteamController/Profiles/Context.cs index 7617502..5f16768 100644 --- a/SteamController/Profiles/Context.cs +++ b/SteamController/Profiles/Context.cs @@ -6,7 +6,10 @@ namespace SteamController.Profiles { public const double JoystickToMouseSensitivity = 1200; public const double PadToMouseSensitivity = 200; - public const double ThumbToWhellSensitivity = 2; + public const double PadToWhellSensitivity = 4; + public const double ThumbToWhellSensitivity = 4; + public static readonly TimeSpan ThumbToWhellFirstRepeat = TimeSpan.FromMilliseconds(30 * ThumbToWhellSensitivity); + public static readonly TimeSpan ThumbToWhellRepeat = TimeSpan.FromMilliseconds(30 * ThumbToWhellSensitivity); public Devices.SteamController Steam { get; private set; } public Devices.Xbox360Controller X360 { get; private set; } @@ -15,7 +18,14 @@ namespace SteamController.Profiles public List Profiles { get; } = new List(); + public bool RequestEnable { get; set; } = true; public bool RequestDesktopMode { get; set; } = true; + public bool DisableDueToSteam { get; set; } = false; + + public bool Enabled + { + get { return RequestEnable && !DisableDueToSteam; } + } public bool DesktopMode { @@ -41,8 +51,27 @@ namespace SteamController.Profiles using (Mouse) { } } + public void Tick() + { + X360.Tick(); + + foreach (Profile profile in Profiles) + { + try { profile.Tick(this); } + catch (Exception e) { TraceLine("Profile: {0}. Exception: {1}", e); } + } + } + public bool Update() { + if (!Enabled) + { + X360.Connected = false; + Steam.LizardButtons = true; + Steam.LizardMouse = true; + return true; + } + Steam.BeforeUpdate(); X360.BeforeUpdate(); Keyboard.BeforeUpdate(); diff --git a/SteamController/Profiles/DesktopProfile.cs b/SteamController/Profiles/DesktopProfile.cs index 6acc2b2..3f742ff 100644 --- a/SteamController/Profiles/DesktopProfile.cs +++ b/SteamController/Profiles/DesktopProfile.cs @@ -38,26 +38,26 @@ namespace SteamController.Profiles if (c.Steam.LPadX) { - c.Mouse.HorizontalScroll(c.Steam.LPadX.Scaled(Context.ThumbToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); + c.Mouse.HorizontalScroll(c.Steam.LPadX.Scaled(Context.PadToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); } if (c.Steam.LPadY) { - c.Mouse.VerticalScroll(c.Steam.LPadY.Scaled(Context.ThumbToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); + c.Mouse.VerticalScroll(c.Steam.LPadY.Scaled(Context.PadToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); } - if (c.Steam.BtnVirtualLeftThumbUp.HoldRepeat(Consumed)) + if (c.Steam.BtnVirtualLeftThumbUp.HoldRepeat(Context.ThumbToWhellFirstRepeat, Context.ThumbToWhellRepeat, Consumed)) { c.Mouse.VerticalScroll(Context.ThumbToWhellSensitivity); } - else if (c.Steam.BtnVirtualLeftThumbDown.HoldRepeat(Consumed)) + else if (c.Steam.BtnVirtualLeftThumbDown.HoldRepeat(Context.ThumbToWhellFirstRepeat, Context.ThumbToWhellRepeat, Consumed)) { c.Mouse.VerticalScroll(-Context.ThumbToWhellSensitivity); } - else if (c.Steam.BtnVirtualLeftThumbLeft.HoldRepeat(Consumed)) + else if (c.Steam.BtnVirtualLeftThumbLeft.HoldRepeat(Context.ThumbToWhellFirstRepeat, Context.ThumbToWhellRepeat, Consumed)) { c.Mouse.HorizontalScroll(-Context.ThumbToWhellSensitivity); } - else if (c.Steam.BtnVirtualLeftThumbRight.HoldRepeat(Consumed)) + else if (c.Steam.BtnVirtualLeftThumbRight.HoldRepeat(Context.ThumbToWhellFirstRepeat, Context.ThumbToWhellRepeat, Consumed)) { c.Mouse.HorizontalScroll(Context.ThumbToWhellSensitivity); } diff --git a/SteamController/Profiles/ProcessProfile.cs b/SteamController/Profiles/ProcessProfile.cs new file mode 100644 index 0000000..f23df4a --- /dev/null +++ b/SteamController/Profiles/ProcessProfile.cs @@ -0,0 +1,52 @@ +using System.Diagnostics; + +namespace SteamController.Profiles +{ + public sealed class ProcessProfile : Profile + { + public static readonly String[] ActivationProcessNames = new String[] + { + "Playnite.FullscreenApp" + }; + + private bool activated; + + private Process? FindActivationProcess() + { + foreach (var processName in ActivationProcessNames) + { + var process = Process.GetProcessesByName(processName).FirstOrDefault(); + if (process is not null) + return process; + } + + return null; + } + + public override void Tick(Context context) + { + // React to state change + if (FindActivationProcess() is not null) + { + if (!activated) + { + activated = true; + context.RequestDesktopMode = false; + } + } + else + { + if (activated) + { + activated = false; + context.RequestDesktopMode = true; + } + } + } + + public override Status Run(Context context) + { + return Status.Continue; + } + } +} diff --git a/SteamController/Profiles/Profile.cs b/SteamController/Profiles/Profile.cs index c38fb82..1ea19fe 100644 --- a/SteamController/Profiles/Profile.cs +++ b/SteamController/Profiles/Profile.cs @@ -12,6 +12,10 @@ namespace SteamController.Profiles public abstract Status Run(Context context); + public virtual void Tick(Context context) + { + } + public virtual void Skipped(Context context) { } diff --git a/SteamController/Profiles/SteamDetectProfile.cs b/SteamController/Profiles/SteamDetectProfile.cs new file mode 100644 index 0000000..c5edd22 --- /dev/null +++ b/SteamController/Profiles/SteamDetectProfile.cs @@ -0,0 +1,35 @@ +using System.Diagnostics; +using SteamController.Helpers; + +namespace SteamController.Profiles +{ + public sealed class SteamDetectProfile : Profile + { + public override void Tick(Context context) + { + if (!Settings.Default.EnableSteamDetection) + { + context.DisableDueToSteam = false; + return; + } + + var usesController = UsesController(); + + // if controller is used, disable due to Steam + context.DisableDueToSteam = usesController ?? true; + } + + private bool? UsesController() + { + if (!SteamManager.IsRunning.GetValueOrDefault(false)) + return null; + + return SteamManager.IsBigPictureMode.GetValueOrDefault(false) || SteamManager.IsRunningGame.GetValueOrDefault(false); + } + + public override Status Run(Context context) + { + return Status.Continue; + } + } +} diff --git a/SteamController/Profiles/SteamShortcutsProfile.cs b/SteamController/Profiles/SteamShortcutsProfile.cs index 7420b44..c6cb4b6 100644 --- a/SteamController/Profiles/SteamShortcutsProfile.cs +++ b/SteamController/Profiles/SteamShortcutsProfile.cs @@ -108,10 +108,16 @@ namespace SteamController.Profiles ); } - if (!c.Steam.LizardMouse) + if (c.Steam.LPadX) { - EmulateLizardMouse(c); + c.Mouse.HorizontalScroll(c.Steam.LPadX.Scaled(Context.PadToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); } + if (c.Steam.LPadY) + { + c.Mouse.VerticalScroll(c.Steam.LPadY.Scaled(Context.PadToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)); + } + + EmulateLizardMouse(c); if (c.Steam.BtnVirtualLeftThumbUp.HoldRepeat(Consumed)) { diff --git a/SteamController/Resources.Designer.cs b/SteamController/Resources.Designer.cs index 75e8e2c..8a5d6e9 100644 --- a/SteamController/Resources.Designer.cs +++ b/SteamController/Resources.Designer.cs @@ -60,6 +60,26 @@ namespace SteamController { } } + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon laptop { + get { + object obj = ResourceManager.GetObject("laptop", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon laptop_off { + get { + object obj = ResourceManager.GetObject("laptop_off", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). /// @@ -119,5 +139,35 @@ namespace SteamController { return ((System.Drawing.Icon)(obj)); } } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon monitor { + get { + object obj = ResourceManager.GetObject("monitor", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon monitor_off { + get { + object obj = ResourceManager.GetObject("monitor_off", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon steam { + get { + object obj = ResourceManager.GetObject("steam", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } } } diff --git a/SteamController/Resources.resx b/SteamController/Resources.resx index 87d7ff6..e66ba11 100644 --- a/SteamController/Resources.resx +++ b/SteamController/Resources.resx @@ -118,6 +118,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Resources\laptop.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\laptop-off.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + Resources\microsoft-xbox-controller.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -136,4 +142,13 @@ Resources\microsoft-xbox-controller-white.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + Resources\monitor.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\monitor-off.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\steam.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/SteamController/Resources/laptop-off.ico b/SteamController/Resources/laptop-off.ico new file mode 100644 index 0000000..e163297 Binary files /dev/null and b/SteamController/Resources/laptop-off.ico differ diff --git a/SteamController/Resources/laptop-off.png b/SteamController/Resources/laptop-off.png new file mode 100644 index 0000000..3b51d96 Binary files /dev/null and b/SteamController/Resources/laptop-off.png differ diff --git a/SteamController/Resources/laptop.ico b/SteamController/Resources/laptop.ico new file mode 100644 index 0000000..cffa06d Binary files /dev/null and b/SteamController/Resources/laptop.ico differ diff --git a/SteamController/Resources/laptop.png b/SteamController/Resources/laptop.png new file mode 100644 index 0000000..eef7fe7 Binary files /dev/null and b/SteamController/Resources/laptop.png differ diff --git a/SteamController/Resources/monitor-off.ico b/SteamController/Resources/monitor-off.ico new file mode 100644 index 0000000..8e4ccdc Binary files /dev/null and b/SteamController/Resources/monitor-off.ico differ diff --git a/SteamController/Resources/monitor-off.png b/SteamController/Resources/monitor-off.png new file mode 100644 index 0000000..68828f7 Binary files /dev/null and b/SteamController/Resources/monitor-off.png differ diff --git a/SteamController/Resources/monitor.ico b/SteamController/Resources/monitor.ico new file mode 100644 index 0000000..a3ee3ce Binary files /dev/null and b/SteamController/Resources/monitor.ico differ diff --git a/SteamController/Resources/monitor.png b/SteamController/Resources/monitor.png new file mode 100644 index 0000000..3e22e5b Binary files /dev/null and b/SteamController/Resources/monitor.png differ diff --git a/SteamController/Resources/steam.ico b/SteamController/Resources/steam.ico new file mode 100644 index 0000000..6feeba4 Binary files /dev/null and b/SteamController/Resources/steam.ico differ diff --git a/SteamController/Resources/steam.png b/SteamController/Resources/steam.png new file mode 100644 index 0000000..6e7c4d9 Binary files /dev/null and b/SteamController/Resources/steam.png differ diff --git a/SteamController/Settings.Designer.cs b/SteamController/Settings.Designer.cs index 896ea78..62f28ea 100644 --- a/SteamController/Settings.Designer.cs +++ b/SteamController/Settings.Designer.cs @@ -22,5 +22,17 @@ namespace SteamController { return defaultInstance; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool EnableSteamDetection { + get { + return ((bool)(this["EnableSteamDetection"])); + } + set { + this["EnableSteamDetection"] = value; + } + } } } diff --git a/SteamController/Settings.settings b/SteamController/Settings.settings index 049245f..cd0ffdd 100644 --- a/SteamController/Settings.settings +++ b/SteamController/Settings.settings @@ -1,6 +1,9 @@  - - - - - + + + + + True + + + \ No newline at end of file