Add SteamController implementation

This adds a Steam Shortcuts, Desktop Mode, and X360 Emulation
- Supports all Steam Shortcuts (including on-screen keyboard, and brightness)
- Supports Desktop mode (with a scroll on left pad and left stick), and trackpoint (on right stick)
- Supports X360 mode: hold Options for 1s to switch between Desktop and X360
- Holding Steam button enables Desktop like controls and stops passing all inputs to X360
This commit is contained in:
Kamil Trzciński 2022-11-24 22:37:24 +01:00
parent 203338b669
commit ecbd0407c0
41 changed files with 2486 additions and 34 deletions

View file

@ -0,0 +1,91 @@
using static CommonHelpers.Log;
namespace SteamController.Profiles
{
public class Context : IDisposable
{
public const double JoystickToMouseSensitivity = 1200;
public const double PadToMouseSensitivity = 200;
public const double ThumbToWhellSensitivity = 2;
public Devices.SteamController Steam { get; private set; }
public Devices.Xbox360Controller X360 { get; private set; }
public Devices.KeyboardController Keyboard { get; private set; }
public Devices.MouseController Mouse { get; private set; }
public List<Profile> Profiles { get; } = new List<Profile>();
public bool RequestDesktopMode { get; set; } = true;
public bool DesktopMode
{
get
{
return RequestDesktopMode || !X360.Valid || !Mouse.Valid;
}
}
public Context()
{
Steam = new Devices.SteamController();
X360 = new Devices.Xbox360Controller();
Keyboard = new Devices.KeyboardController();
Mouse = new Devices.MouseController();
}
public void Dispose()
{
using (Steam) { }
using (X360) { }
using (Keyboard) { }
using (Mouse) { }
}
public bool Update()
{
Steam.BeforeUpdate();
X360.BeforeUpdate();
Keyboard.BeforeUpdate();
Mouse.BeforeUpdate();
try
{
bool skip = false;
foreach (Profile profile in Profiles)
{
if (!profile.RunAlways && skip)
{
profile.Skipped(this);
continue;
}
try
{
var status = profile.Run(this);
if (status == Profile.Status.Stop)
skip = true;
}
catch (Exception e)
{
TraceLine("Profile: Exception: {0}", e.Message);
}
}
return true;
}
catch (Exception e)
{
TraceLine("Controller: Exception: {0}", e);
return false;
}
finally
{
Steam.Update();
X360.Update();
Keyboard.Update();
Mouse.Update();
}
}
}
}

View file

@ -0,0 +1,64 @@
using static CommonHelpers.Log;
namespace SteamController.Profiles
{
public sealed class DebugProfile : Profile
{
public DebugProfile()
{
RunAlways = true;
}
List<string> lastItems = new List<string>();
public override Status Run(Context c)
{
var items = new List<string>();
if (c.DesktopMode)
items.Add("[DESKTOP]");
else
items.Add("[CONTROLLER]");
if (c.Steam.LizardButtons)
items.Add("[LB]");
if (c.Steam.LizardMouse)
items.Add("[LM]");
if (c.X360.Connected)
items.Add("[X360]");
else if (c.X360.Valid)
items.Add("[no-X360]");
foreach (var button in c.Steam.AllButtons)
{
if (button is null || !button.LastValue)
continue;
String text = button.Name;
if (button.Consumed is not null)
text += String.Format("[{0}]", button.Consumed);
if (button.Value)
text += "[P]";
items.Add(text);
}
foreach (var key in c.Keyboard.DownKeys)
{
items.Add(String.Format("Key{0}", key));
}
foreach (var mouse in c.Mouse.DownButtons)
{
items.Add(String.Format("Mouse{0}", mouse));
}
if (!items.SequenceEqual(lastItems))
{
TraceLine("DEBUG: {0}", String.Join(" ", items));
lastItems = items;
}
return Status.Continue;
}
}
}

View file

@ -0,0 +1,112 @@
using PowerControl.Helpers;
using WindowsInput;
namespace SteamController.Profiles
{
public sealed class DesktopProfile : Profile
{
public const bool LizardButtons = false;
public const bool LizardMouse = true;
public const String Consumed = "DesktopProfileOwner";
public DesktopProfile()
{
}
public override Status Run(Context c)
{
if (!c.DesktopMode)
{
return Status.Continue;
}
if (!c.Mouse.Valid)
{
// Failed to acquire secure context
// Enable emergency Lizard
c.Steam.LizardButtons = true;
c.Steam.LizardMouse = true;
return Status.Continue;
}
c.Steam.LizardButtons = LizardButtons;
c.Steam.LizardMouse = LizardMouse;
EmulateLizardButtons(c);
EmulateLizardMouse(c);
if (c.Steam.LPadX)
{
c.Mouse.HorizontalScroll(c.Steam.LPadX.Scaled(Context.ThumbToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta));
}
if (c.Steam.LPadY)
{
c.Mouse.VerticalScroll(c.Steam.LPadY.Scaled(Context.ThumbToWhellSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta));
}
if (c.Steam.BtnVirtualLeftThumbUp.HoldRepeat(Consumed))
{
c.Mouse.VerticalScroll(Context.ThumbToWhellSensitivity);
}
else if (c.Steam.BtnVirtualLeftThumbDown.HoldRepeat(Consumed))
{
c.Mouse.VerticalScroll(-Context.ThumbToWhellSensitivity);
}
else if (c.Steam.BtnVirtualLeftThumbLeft.HoldRepeat(Consumed))
{
c.Mouse.HorizontalScroll(-Context.ThumbToWhellSensitivity);
}
else if (c.Steam.BtnVirtualLeftThumbRight.HoldRepeat(Consumed))
{
c.Mouse.HorizontalScroll(Context.ThumbToWhellSensitivity);
}
if (c.Steam.BtnRStickTouch && (c.Steam.RightThumbX || c.Steam.RightThumbY))
{
c.Mouse.MoveBy(
c.Steam.RightThumbX.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.AbsoluteTime),
-c.Steam.RightThumbY.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.AbsoluteTime)
);
}
return Status.Continue;
}
private void EmulateLizardButtons(Context c)
{
c.Mouse[Devices.MouseController.Button.Right] = c.Steam.BtnL2 || c.Steam.BtnLPadPress;
c.Mouse[Devices.MouseController.Button.Left] = c.Steam.BtnR2 || c.Steam.BtnRPadPress;
#if true
if (c.Steam.BtnA.Pressed())
c.Keyboard.KeyPress(VirtualKeyCode.RETURN);
if (c.Steam.BtnDpadLeft.HoldRepeat(Consumed))
c.Keyboard.KeyPress(VirtualKeyCode.LEFT);
if (c.Steam.BtnDpadRight.HoldRepeat(Consumed))
c.Keyboard.KeyPress(VirtualKeyCode.RIGHT);
if (c.Steam.BtnDpadUp.HoldRepeat(Consumed))
c.Keyboard.KeyPress(VirtualKeyCode.UP);
if (c.Steam.BtnDpadDown.HoldRepeat(Consumed))
c.Keyboard.KeyPress(VirtualKeyCode.DOWN);
#else
c.Keyboard[VirtualKeyCode.RETURN] = c.Steam.BtnA;
c.Keyboard[VirtualKeyCode.LEFT] = c.Steam.BtnDpadLeft;
c.Keyboard[VirtualKeyCode.RIGHT] = c.Steam.BtnDpadRight;
c.Keyboard[VirtualKeyCode.UP] = c.Steam.BtnDpadUp;
c.Keyboard[VirtualKeyCode.DOWN] = c.Steam.BtnDpadDown;
#endif
}
private void EmulateLizardMouse(Context c)
{
if (c.Steam.RPadX || c.Steam.RPadY)
{
c.Mouse.MoveBy(
c.Steam.RPadX.Scaled(Context.PadToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta),
-c.Steam.RPadY.Scaled(Context.PadToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)
);
}
}
}
}

View file

@ -0,0 +1,19 @@
namespace SteamController.Profiles
{
public abstract class Profile
{
public enum Status
{
Continue,
Stop
}
public bool RunAlways { get; set; }
public abstract Status Run(Context context);
public virtual void Skipped(Context context)
{
}
}
}

View file

@ -0,0 +1,166 @@
using System.Diagnostics;
using ExternalHelpers;
using PowerControl.Helpers;
using WindowsInput;
namespace SteamController.Profiles
{
public sealed class SteamShortcutsProfile : Profile
{
public const bool LizardButtons = true;
public const bool LizardMouse = false;
public const String Consumed = "SteamShortcutsProfile";
public readonly TimeSpan HoldForShorcuts = TimeSpan.FromMilliseconds(200);
public readonly TimeSpan HoldForKill = TimeSpan.FromSeconds(3);
public readonly TimeSpan HoldForClose = TimeSpan.FromSeconds(1);
public readonly TimeSpan HoldToSwitchDesktop = TimeSpan.FromSeconds(1);
public SteamShortcutsProfile()
{
RunAlways = true;
}
public override Status Run(Context c)
{
// Steam + 3 dots simulate CTRL+ALT+DELETE
if (c.Steam.BtnSteam.Hold(HoldForShorcuts, Consumed) && c.Steam.BtnQuickAccess.HoldOnce(HoldForShorcuts, Consumed))
{
c.Keyboard.KeyPress(new VirtualKeyCode[] { VirtualKeyCode.LCONTROL, VirtualKeyCode.LMENU }, VirtualKeyCode.DELETE);
}
if (c.Steam.BtnSteam.Hold(HoldForShorcuts, Consumed))
{
c.Steam.LizardButtons = LizardButtons;
c.Steam.LizardMouse = LizardMouse;
SteamShortcuts(c);
AdditionalShortcuts(c);
return Status.Stop;
}
if (c.Steam.BtnOptions.HoldOnce(HoldToSwitchDesktop, Consumed))
{
c.RequestDesktopMode = !c.RequestDesktopMode;
}
if (c.Steam.BtnQuickAccess.Hold(HoldForShorcuts, Consumed))
{
// nothing there, just consume
return Status.Stop;
}
return Status.Continue;
}
private void SteamShortcuts(Context c)
{
c.Steam.LizardButtons = false;
c.Steam.LizardMouse = true;
if (c.Steam.BtnA.Pressed())
{
c.Keyboard.KeyPress(VirtualKeyCode.RETURN);
}
if (c.Steam.BtnB.HoldOnce(HoldForKill, Consumed))
{
// kill application
}
else if (c.Steam.BtnB.HoldOnce(HoldForClose, Consumed))
{
// close application
c.Keyboard.KeyPress(VirtualKeyCode.LMENU, VirtualKeyCode.F4);
}
if (c.Steam.BtnX.Pressed())
{
OnScreenKeyboard.Toggle();
}
if (c.Steam.BtnL1.Pressed())
{
if (Process.GetProcessesByName("Magnify").Any())
{
// close magnifier
c.Keyboard.KeyPress(VirtualKeyCode.LWIN, VirtualKeyCode.ESCAPE);
}
else
{
// enable magnifier
c.Keyboard.KeyPress(VirtualKeyCode.LWIN, VirtualKeyCode.OEM_PLUS);
}
}
if (c.Steam.BtnR1.Pressed())
{
// take screenshot
c.Keyboard.KeyPress(VirtualKeyCode.LWIN, VirtualKeyCode.SNAPSHOT);
}
c.Mouse[Devices.MouseController.Button.Right] = c.Steam.BtnL2 || c.Steam.BtnLPadPress;
c.Mouse[Devices.MouseController.Button.Left] = c.Steam.BtnR2 || c.Steam.BtnRPadPress;
if (c.Steam.BtnRStickTouch && (c.Steam.RightThumbX || c.Steam.RightThumbY))
{
c.Mouse.MoveBy(
c.Steam.RightThumbX.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.AbsoluteTime),
-c.Steam.RightThumbY.Scaled(Context.JoystickToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.AbsoluteTime)
);
}
if (!c.Steam.LizardMouse)
{
EmulateLizardMouse(c);
}
if (c.Steam.BtnVirtualLeftThumbUp.HoldRepeat(Consumed))
{
WindowsSettingsBrightnessController.Increase(5);
}
if (c.Steam.BtnVirtualLeftThumbDown.HoldRepeat(Consumed))
{
WindowsSettingsBrightnessController.Increase(-5);
}
if (c.Steam.BtnDpadRight.Pressed())
{
c.Keyboard.KeyPress(VirtualKeyCode.RETURN);
}
if (c.Steam.BtnDpadDown.Pressed())
{
c.Keyboard.KeyPress(VirtualKeyCode.TAB);
}
if (c.Steam.BtnDpadLeft.Pressed())
{
c.Keyboard.KeyPress(VirtualKeyCode.ESCAPE);
}
}
private void AdditionalShortcuts(Context c)
{
if (c.Steam.BtnMenu.Pressed())
{
c.Keyboard.KeyPress(VirtualKeyCode.LWIN, VirtualKeyCode.TAB);
}
if (c.Steam.BtnOptions.Pressed())
{
c.Keyboard.KeyPress(VirtualKeyCode.F11);
}
}
private void EmulateLizardMouse(Context c)
{
if (c.Steam.RPadX || c.Steam.RPadY)
{
c.Mouse.MoveBy(
c.Steam.RPadX.Scaled(Context.PadToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta),
-c.Steam.RPadY.Scaled(Context.PadToMouseSensitivity, Devices.SteamController.SteamAxis.ScaledMode.Delta)
);
}
}
}
}

View file

@ -0,0 +1,66 @@
using Nefarius.ViGEm.Client.Targets.Xbox360;
namespace SteamController.Profiles
{
public sealed class X360Profile : Profile
{
public override void Skipped(Context context)
{
if (!context.DesktopMode)
{
context.X360.Connected = true;
ControlButtons(context);
}
}
public override Status Run(Context context)
{
if (context.DesktopMode)
{
context.X360.Connected = false;
return Status.Continue;
}
context.Steam.LizardButtons = false;
context.Steam.LizardMouse = true;
context.X360.Connected = true;
// DPad
context.X360[Xbox360Button.Up] = context.Steam.BtnDpadUp;
context.X360[Xbox360Button.Down] = context.Steam.BtnDpadDown;
context.X360[Xbox360Button.Left] = context.Steam.BtnDpadLeft;
context.X360[Xbox360Button.Right] = context.Steam.BtnDpadRight;
// Buttons
context.X360[Xbox360Button.A] = context.Steam.BtnA;
context.X360[Xbox360Button.B] = context.Steam.BtnB;
context.X360[Xbox360Button.X] = context.Steam.BtnX;
context.X360[Xbox360Button.Y] = context.Steam.BtnY;
// Sticks
context.X360[Xbox360Axis.LeftThumbX] = context.Steam.LeftThumbX;
context.X360[Xbox360Axis.LeftThumbY] = context.Steam.LeftThumbY;
context.X360[Xbox360Axis.RightThumbX] = context.Steam.RightThumbX;
context.X360[Xbox360Axis.RightThumbY] = context.Steam.RightThumbY;
context.X360[Xbox360Button.LeftThumb] = context.Steam.BtnLeftStickPress;
context.X360[Xbox360Button.RightThumb] = context.Steam.BtnRightStickPress;
// Triggers
context.X360[Xbox360Slider.LeftTrigger] = context.Steam.LeftTrigger;
context.X360[Xbox360Slider.RightTrigger] = context.Steam.RightTrigger;
context.X360[Xbox360Button.LeftShoulder] = context.Steam.BtnL1;
context.X360[Xbox360Button.RightShoulder] = context.Steam.BtnR1;
ControlButtons(context);
return Status.Continue;
}
private void ControlButtons(Context context)
{
// Controls
context.X360[Xbox360Button.Guide] = context.Steam.BtnSteam.Pressed();
context.X360[Xbox360Button.Back] = context.Steam.BtnMenu;
context.X360[Xbox360Button.Start] = context.Steam.BtnOptions;
}
}
}