Add Process and Steam detection

- This makes to autmatically swittch to controller
  when Playnite Fullscreen is in use.
- This makes to automatically disable when Steam
  is in Big Picture or running Game
- Indicate current status with well distinguishable icons
- Expose all options via Context Menu
This commit is contained in:
Kamil Trzciński 2022-11-25 07:48:01 +01:00
parent ecbd0407c0
commit 10d6c055da
25 changed files with 380 additions and 49 deletions

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="SteamController.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<SteamController.Settings>
<setting name="EnableSteamDetection" serializeAs="String">
<value>True</value>
</setting>
</SteamController.Settings>
</userSettings>
</configuration>

View file

@ -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)

View file

@ -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
{

View file

@ -22,7 +22,7 @@ namespace SteamController.Devices
using (client) { }
}
internal bool CreateClient()
internal bool Tick()
{
if (this.device is not null)
return true;

View file

@ -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<int>(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<int>(SteamKey, BigPictureInForegroundValue);
return value.HasValue ? value != 0 : null;
}
}
public static bool? IsRunningGame
{
get
{
var value = GetValue<int>(SteamKey, RunningAppIDValue);
return value.HasValue ? value != 0 : null;
}
}
private static T? GetValue<T>(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;
}
}
}
}

View file

@ -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<Profile> Profiles { get; } = new List<Profile>();
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();

View file

@ -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);
}

View file

@ -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;
}
}
}

View file

@ -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)
{
}

View file

@ -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;
}
}
}

View file

@ -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))
{

View file

@ -60,6 +60,26 @@ namespace SteamController {
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon laptop {
get {
object obj = ResourceManager.GetObject("laptop", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon laptop_off {
get {
object obj = ResourceManager.GetObject("laptop_off", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
@ -119,5 +139,35 @@ namespace SteamController {
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon monitor {
get {
object obj = ResourceManager.GetObject("monitor", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon monitor_off {
get {
object obj = ResourceManager.GetObject("monitor_off", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
internal static System.Drawing.Icon steam {
get {
object obj = ResourceManager.GetObject("steam", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
}
}

View file

@ -118,6 +118,12 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="laptop" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\laptop.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="laptop_off" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\laptop-off.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="microsoft_xbox_controller" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\microsoft-xbox-controller.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
@ -136,4 +142,13 @@
<data name="microsoft_xbox_controller_white" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\microsoft-xbox-controller-white.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="monitor" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\monitor.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="monitor_off" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\monitor-off.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="steam" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\steam.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -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;
}
}
}
}

View file

@ -1,6 +1,9 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
</SettingsFile>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="SteamController" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="EnableSteamDetection" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
</Settings>
</SettingsFile>