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
16
CommonHelpers/Log.cs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
using System.Diagnostics;
|
||||
|
||||
namespace CommonHelpers
|
||||
{
|
||||
public static class Log
|
||||
{
|
||||
public static void TraceLine(string format, params object?[] arg)
|
||||
{
|
||||
String line = string.Format(format, arg);
|
||||
|
||||
Trace.WriteLine(line);
|
||||
if (Environment.UserInteractive)
|
||||
Console.WriteLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
ExternalHelpers/OnScreenKeyboard.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace ExternalHelpers
|
||||
{
|
||||
public static class OnScreenKeyboard
|
||||
{
|
||||
public const String TabTipPath = @"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe";
|
||||
|
||||
public static bool Toggle()
|
||||
{
|
||||
StartTabTip();
|
||||
|
||||
var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));
|
||||
if (type is null)
|
||||
return false;
|
||||
var instance = (ITipInvocation?)Activator.CreateInstance(type);
|
||||
if (instance is null)
|
||||
return false;
|
||||
instance?.Toggle(GetDesktopWindow());
|
||||
Marshal.ReleaseComObject(instance);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void StartTabTip()
|
||||
{
|
||||
if (FindWindow("IPTIP_Main_Window", "") != IntPtr.Zero)
|
||||
return;
|
||||
|
||||
Process.Start(TabTipPath);
|
||||
|
||||
for (int i = 0; i < 10 && FindWindow("IPTIP_Main_Window", "") == IntPtr.Zero; i++)
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
|
||||
[ComImport, Guid("4ce576fa-83dc-4F88-951c-9d0782b4e376")]
|
||||
class UIHostNoLaunch
|
||||
{
|
||||
}
|
||||
|
||||
[ComImport, Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
interface ITipInvocation
|
||||
{
|
||||
void Toggle(IntPtr hwnd);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = false)]
|
||||
static extern IntPtr GetDesktopWindow();
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "FindWindow")]
|
||||
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,8 @@ namespace PowerControl.External
|
|||
CONFIGURE_BT = 0x18,
|
||||
}
|
||||
|
||||
public enum SDCButton0
|
||||
[Flags]
|
||||
public enum SDCButton0 : ushort
|
||||
{
|
||||
BTN_L5 = 0b1000000000000000,
|
||||
BTN_OPTIONS = 0b0100000000000000,
|
||||
|
|
@ -65,22 +66,25 @@ namespace PowerControl.External
|
|||
BTN_R2 = 0b0000000000000001,
|
||||
}
|
||||
|
||||
public enum SDCButton1
|
||||
[Flags]
|
||||
public enum SDCButton1 : byte
|
||||
{
|
||||
BTN_LSTICK_PRESS = 0b01000000,
|
||||
BTN_RPAD_TOUCH = 0b00010000,
|
||||
BTN_LPAD_TOUCH = 0b00001000,
|
||||
BTN_RPAD_PRESS = 0b00000100,
|
||||
BTN_LPAD_PRESS = 0b00000010,
|
||||
BTN_RPAD_PRESS = 0b00010000,
|
||||
BTN_RPAD_TOUCH = 0b00000100,
|
||||
BTN_R5 = 0b00000001,
|
||||
}
|
||||
|
||||
public enum SDCButton2
|
||||
[Flags]
|
||||
public enum SDCButton2 : byte
|
||||
{
|
||||
BTN_RSTICK_PRESS = 0b00000100,
|
||||
}
|
||||
|
||||
public enum SDCButton4
|
||||
[Flags]
|
||||
public enum SDCButton4 : byte
|
||||
{
|
||||
BTN_LSTICK_TOUCH = 0b01000000,
|
||||
BTN_RSTICK_TOUCH = 0b10000000,
|
||||
|
|
@ -88,29 +92,30 @@ namespace PowerControl.External
|
|||
BTN_L4 = 0b00000010,
|
||||
}
|
||||
|
||||
public enum SDCButton5
|
||||
[Flags]
|
||||
public enum SDCButton5 : byte
|
||||
{
|
||||
BTN_QUICK_ACCESS = 0b00000100,
|
||||
}
|
||||
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct SDCInput
|
||||
{
|
||||
public byte ptype; //0x00
|
||||
public byte _a1; //0x01
|
||||
public byte _a2; //0x02
|
||||
public byte _a1; //0x01
|
||||
public byte _a2; //0x02
|
||||
public byte _a3; //0x03
|
||||
public uint seq; //0x04
|
||||
public ushort buttons0; //0x09
|
||||
public byte buttons1; //0x0A
|
||||
public byte buttons2; //0x0C
|
||||
public byte buttons3; //0x0D
|
||||
public byte buttons4; //0x0E
|
||||
public byte buttons5; //0x0E
|
||||
public uint seq; //0x04
|
||||
public SDCButton0 buttons0; //0x08
|
||||
public SDCButton1 buttons1; //0x0A
|
||||
public SDCButton2 buttons2; //0x0B
|
||||
public byte buttons3; //0x0C
|
||||
public SDCButton4 buttons4; //0x0D
|
||||
public SDCButton5 buttons5; //0x0E
|
||||
public byte buttons6; //0x0F
|
||||
public short lpad_x; //0x10
|
||||
public short lpad_y; //0x12
|
||||
public short rpad_x; //0x13
|
||||
public short rpad_x; //0x14
|
||||
public short rpad_y; //0x16
|
||||
public short accel_x; //0x18
|
||||
public short accel_y; //0x1A
|
||||
|
|
@ -136,7 +141,7 @@ namespace PowerControl.External
|
|||
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
return (SDCInput)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(SDCInput));
|
||||
return Marshal.PtrToStructure<SDCInput>(handle.AddrOfPinnedObject());
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@ namespace PowerControl.Helpers
|
|||
/// </summary>
|
||||
public static class WindowsSettingsBrightnessController
|
||||
{
|
||||
public static void Increase(int brightness)
|
||||
{
|
||||
var current = Get();
|
||||
current += brightness;
|
||||
current = Math.Clamp(current, 0, 100);
|
||||
Set(current);
|
||||
}
|
||||
|
||||
public static int Get(double roundValue = 10.0)
|
||||
{
|
||||
return (int)(Math.Round(Get() / roundValue) * roundValue);
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ namespace PowerControl
|
|||
|
||||
osdDismissTimer = new System.Windows.Forms.Timer(components);
|
||||
osdDismissTimer.Interval = 3000;
|
||||
osdDismissTimer.Tick += delegate(object ? sender, EventArgs e)
|
||||
osdDismissTimer.Tick += delegate (object? sender, EventArgs e)
|
||||
{
|
||||
hideOSD();
|
||||
};
|
||||
|
|
@ -150,7 +150,7 @@ namespace PowerControl
|
|||
{
|
||||
GlobalHotKey.RegisterHotKey("VolumeUp", () =>
|
||||
{
|
||||
if ((neptuneDeviceState.buttons5 & (byte)SDCButton5.BTN_QUICK_ACCESS) != 0)
|
||||
if (neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS))
|
||||
rootMenu.SelectNext("Brightness");
|
||||
else
|
||||
rootMenu.SelectNext("Volume");
|
||||
|
|
@ -160,7 +160,7 @@ namespace PowerControl
|
|||
|
||||
GlobalHotKey.RegisterHotKey("VolumeDown", () =>
|
||||
{
|
||||
if ((neptuneDeviceState.buttons5 & (byte)SDCButton5.BTN_QUICK_ACCESS) != 0)
|
||||
if (neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS))
|
||||
rootMenu.SelectPrev("Brightness");
|
||||
else
|
||||
rootMenu.SelectPrev("Volume");
|
||||
|
|
@ -207,7 +207,7 @@ namespace PowerControl
|
|||
}
|
||||
|
||||
// Consume only some events to avoid under-running SWICD
|
||||
if ((input.buttons5 & (byte)SDCButton5.BTN_QUICK_ACCESS) != 0)
|
||||
if (neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS))
|
||||
Thread.Sleep(1000 / 30);
|
||||
else
|
||||
Thread.Sleep(250);
|
||||
|
|
@ -230,12 +230,12 @@ namespace PowerControl
|
|||
return; // otherwise it did not yet trigger
|
||||
|
||||
// Reset sequence: 3 dots + L4|R4|L5|R5
|
||||
if (input.buttons0 == (ushort)SDCButton0.BTN_L5 &&
|
||||
input.buttons1 == (byte)SDCButton1.BTN_R5 &&
|
||||
if (input.buttons0 == SDCButton0.BTN_L5 &&
|
||||
input.buttons1 == SDCButton1.BTN_R5 &&
|
||||
input.buttons2 == 0 &&
|
||||
input.buttons3 == 0 &&
|
||||
input.buttons4 == (byte)(SDCButton4.BTN_L4 | SDCButton4.BTN_R4) &&
|
||||
input.buttons5 == (byte)SDCButton5.BTN_QUICK_ACCESS)
|
||||
input.buttons4 == (SDCButton4.BTN_L4 | SDCButton4.BTN_R4) &&
|
||||
input.buttons5 == SDCButton5.BTN_QUICK_ACCESS)
|
||||
{
|
||||
rootMenu.Show();
|
||||
rootMenu.Reset();
|
||||
|
|
@ -243,7 +243,7 @@ namespace PowerControl
|
|||
return;
|
||||
}
|
||||
|
||||
if ((input.buttons5 & (byte)SDCButton5.BTN_QUICK_ACCESS) == 0 || !RTSS.IsOSDForeground())
|
||||
if (!neptuneDeviceState.buttons5.HasFlag(SDCButton5.BTN_QUICK_ACCESS) || !RTSS.IsOSDForeground())
|
||||
{
|
||||
// schedule next repeat far in the future
|
||||
dismissNeptuneInput();
|
||||
|
|
@ -258,19 +258,19 @@ namespace PowerControl
|
|||
{
|
||||
return;
|
||||
}
|
||||
else if (input.buttons0 == (ushort)SDCButton0.BTN_DPAD_LEFT)
|
||||
else if (input.buttons0 == SDCButton0.BTN_DPAD_LEFT)
|
||||
{
|
||||
rootMenu.SelectPrev();
|
||||
}
|
||||
else if (input.buttons0 == (ushort)SDCButton0.BTN_DPAD_RIGHT)
|
||||
else if (input.buttons0 == SDCButton0.BTN_DPAD_RIGHT)
|
||||
{
|
||||
rootMenu.SelectNext();
|
||||
}
|
||||
else if (input.buttons0 == (ushort)SDCButton0.BTN_DPAD_UP)
|
||||
else if (input.buttons0 == SDCButton0.BTN_DPAD_UP)
|
||||
{
|
||||
rootMenu.Prev();
|
||||
}
|
||||
else if (input.buttons0 == (ushort)SDCButton0.BTN_DPAD_DOWN)
|
||||
else if (input.buttons0 == SDCButton0.BTN_DPAD_DOWN)
|
||||
{
|
||||
rootMenu.Next();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
## 0.5.x
|
||||
|
||||
- Introduce SteamController that provides 3 main modes of operation Desktop, X360 and Steam
|
||||
|
||||
## 0.4.x
|
||||
|
||||
- Highly risky: Allow to change CPU and GPU frequency (enable `EnableExperimentalFeatures` in `PowerControl.dll.config`)
|
||||
|
|
|
|||
131
SteamController/Controller.cs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
using CommonHelpers;
|
||||
using ExternalHelpers;
|
||||
using SteamController.Profiles;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace SteamController
|
||||
{
|
||||
internal class Controller : IDisposable
|
||||
{
|
||||
public const String Title = "Steam Controller";
|
||||
public readonly String TitleWithVersion = Title + " v" + Application.ProductVersion.ToString();
|
||||
|
||||
Container components = new Container();
|
||||
NotifyIcon notifyIcon;
|
||||
StartupManager startupManager = new StartupManager(Title);
|
||||
|
||||
Context context;
|
||||
Thread? contextThread;
|
||||
bool running = true;
|
||||
|
||||
[DllImport("sas.dll")]
|
||||
static extern void SendSAS(bool asUser);
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
};
|
||||
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");
|
||||
};
|
||||
|
||||
contextMenu.Items.Add(new ToolStripSeparator());
|
||||
|
||||
var exitItem = contextMenu.Items.Add("&Exit");
|
||||
exitItem.Click += delegate
|
||||
{
|
||||
Application.Exit();
|
||||
};
|
||||
|
||||
notifyIcon = new NotifyIcon(components);
|
||||
notifyIcon.Icon = Resources.microsoft_xbox_controller_off;
|
||||
notifyIcon.Text = TitleWithVersion;
|
||||
notifyIcon.Visible = true;
|
||||
notifyIcon.ContextMenuStrip = contextMenu;
|
||||
|
||||
var contextStateUpdate = new System.Windows.Forms.Timer(components);
|
||||
contextStateUpdate.Interval = 250;
|
||||
contextStateUpdate.Enabled = true;
|
||||
contextStateUpdate.Tick += ContextStateUpdate_Tick;
|
||||
|
||||
contextThread = new Thread(ContextState_Update);
|
||||
contextThread.Start();
|
||||
}
|
||||
|
||||
private void ContextState_Update(object? obj)
|
||||
{
|
||||
while (running)
|
||||
{
|
||||
context.Update();
|
||||
}
|
||||
}
|
||||
|
||||
private void ContextStateUpdate_Tick(object? sender, EventArgs e)
|
||||
{
|
||||
context.X360.CreateClient();
|
||||
|
||||
if (!context.Mouse.Valid)
|
||||
{
|
||||
notifyIcon.Text = TitleWithVersion + ". Cannot send input";
|
||||
notifyIcon.Icon = Resources.microsoft_xbox_controller_off_red;
|
||||
}
|
||||
else if (!context.X360.Valid)
|
||||
{
|
||||
notifyIcon.Text = TitleWithVersion + ". Missing ViGEm?";
|
||||
notifyIcon.Icon = Resources.microsoft_xbox_controller_red;
|
||||
}
|
||||
else if (context.DesktopMode)
|
||||
{
|
||||
notifyIcon.Icon = Resources.microsoft_xbox_controller_off;
|
||||
notifyIcon.Text = TitleWithVersion + ". Desktop mode";
|
||||
}
|
||||
else
|
||||
{
|
||||
notifyIcon.Icon = Resources.microsoft_xbox_controller;
|
||||
notifyIcon.Text = TitleWithVersion;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
running = false;
|
||||
|
||||
if (contextThread != null)
|
||||
{
|
||||
contextThread.Interrupt();
|
||||
contextThread.Join();
|
||||
}
|
||||
|
||||
using (context) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
95
SteamController/Devices/KeyboardController.cs
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
using WindowsInput;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public class KeyboardController : IDisposable
|
||||
{
|
||||
InputSimulator simulator = new InputSimulator();
|
||||
|
||||
HashSet<VirtualKeyCode> keyCodes = new HashSet<VirtualKeyCode>();
|
||||
HashSet<VirtualKeyCode> lastKeyCodes = new HashSet<VirtualKeyCode>();
|
||||
|
||||
public KeyboardController()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool this[VirtualKeyCode button]
|
||||
{
|
||||
get { return keyCodes.Contains(button); }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
keyCodes.Add(button);
|
||||
else
|
||||
keyCodes.Remove(button);
|
||||
}
|
||||
}
|
||||
|
||||
public VirtualKeyCode[] DownKeys
|
||||
{
|
||||
get { return keyCodes.ToArray(); }
|
||||
}
|
||||
|
||||
internal void BeforeUpdate()
|
||||
{
|
||||
lastKeyCodes = keyCodes;
|
||||
keyCodes = new HashSet<VirtualKeyCode>();
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Key Up: it is missing now
|
||||
var keyUp = lastKeyCodes.Except(keyCodes).ToArray();
|
||||
if (keyUp.Any())
|
||||
simulator.Keyboard.KeyUp(keyUp);
|
||||
|
||||
// Key Down: new keys being down
|
||||
var keyDown = keyCodes.Except(lastKeyCodes).ToArray();
|
||||
if (keyDown.Any())
|
||||
simulator.Keyboard.KeyUp(keyDown);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void KeyPress(params VirtualKeyCode[] keyCodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
simulator.Keyboard.KeyPress(keyCodes);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void KeyPress(VirtualKeyCode modifierKey, params VirtualKeyCode[] keyCodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
simulator.Keyboard.ModifiedKeyStroke(modifierKey, keyCodes);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void KeyPress(IEnumerable<VirtualKeyCode> modifierKeys, params VirtualKeyCode[] keyCodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
simulator.Keyboard.ModifiedKeyStroke(modifierKeys, keyCodes);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
362
SteamController/Devices/MouseController.cs
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
#define ACCUM_MOUSE
|
||||
#define ACCUM_SCROLL
|
||||
|
||||
using WindowsInput;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public class MouseController : IDisposable
|
||||
{
|
||||
private struct Accum
|
||||
{
|
||||
double? last, now;
|
||||
|
||||
public bool Used
|
||||
{
|
||||
get { return now is not null; }
|
||||
}
|
||||
|
||||
public void Tick()
|
||||
{
|
||||
last = now;
|
||||
now = null;
|
||||
}
|
||||
|
||||
public void Add(double delta)
|
||||
{
|
||||
now = (now ?? 0.0) + delta;
|
||||
}
|
||||
|
||||
public int Consume()
|
||||
{
|
||||
double accum = ((now ?? 0.0) + (last ?? 0.0));
|
||||
now = accum - (int)accum;
|
||||
last = null;
|
||||
return (int)accum;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Unsure what it is
|
||||
public const int XButtonID = 0;
|
||||
public const int YButtonID = 1;
|
||||
public const int UpdateValidInterval = 250;
|
||||
|
||||
InputSimulator simulator = new InputSimulator();
|
||||
|
||||
HashSet<Button> mouseButtons = new HashSet<Button>();
|
||||
HashSet<Button> lastMouseButtons = new HashSet<Button>();
|
||||
|
||||
Accum movedX, movedY, verticalScroll, horizontalScroll;
|
||||
|
||||
bool? valid = null;
|
||||
DateTime lastValid = DateTime.Now;
|
||||
|
||||
public enum Button
|
||||
{
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
public bool this[Button button]
|
||||
{
|
||||
get { return mouseButtons.Contains(button); }
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
mouseButtons.Add(button);
|
||||
else
|
||||
mouseButtons.Remove(button);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Valid
|
||||
{
|
||||
get { return valid ?? true; }
|
||||
}
|
||||
|
||||
public Button[] DownButtons
|
||||
{
|
||||
get { return mouseButtons.ToArray(); }
|
||||
}
|
||||
|
||||
public MouseController()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
private void Safe(Func<bool> action)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (action())
|
||||
{
|
||||
valid = true;
|
||||
lastValid = DateTime.Now;
|
||||
}
|
||||
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
valid = false;
|
||||
lastValid = DateTime.Now;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateValid()
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
if (valid is null || lastValid.AddMilliseconds(UpdateValidInterval) < DateTime.Now)
|
||||
{
|
||||
simulator.Mouse.MoveMouseBy(0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
internal void BeforeUpdate()
|
||||
{
|
||||
lastMouseButtons = mouseButtons;
|
||||
mouseButtons = new HashSet<Button>();
|
||||
movedX.Tick();
|
||||
movedY.Tick();
|
||||
verticalScroll.Tick();
|
||||
horizontalScroll.Tick();
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
// Mouse Up: it is missing now
|
||||
foreach (var button in lastMouseButtons.Except(mouseButtons))
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case Button.Left:
|
||||
simulator.Mouse.LeftButtonUp();
|
||||
return true;
|
||||
|
||||
case Button.Right:
|
||||
simulator.Mouse.RightButtonUp();
|
||||
return true;
|
||||
|
||||
case Button.Middle:
|
||||
simulator.Mouse.MiddleButtonUp();
|
||||
return true;
|
||||
|
||||
case Button.X:
|
||||
simulator.Mouse.XButtonUp(XButtonID);
|
||||
return true;
|
||||
|
||||
case Button.Y:
|
||||
simulator.Mouse.XButtonUp(YButtonID);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Key Down: new keys being down
|
||||
foreach (var button in mouseButtons.Except(lastMouseButtons))
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case Button.Left:
|
||||
simulator.Mouse.LeftButtonDown();
|
||||
return true;
|
||||
|
||||
case Button.Right:
|
||||
simulator.Mouse.RightButtonDown();
|
||||
return true;
|
||||
|
||||
case Button.Middle:
|
||||
simulator.Mouse.MiddleButtonDown();
|
||||
return true;
|
||||
|
||||
case Button.X:
|
||||
simulator.Mouse.XButtonDown(XButtonID);
|
||||
return true;
|
||||
|
||||
case Button.Y:
|
||||
simulator.Mouse.XButtonDown(YButtonID);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Move cursor
|
||||
if (movedX.Used || movedY.Used)
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
simulator.Mouse.MoveMouseBy(movedX.Consume(), movedY.Consume());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll
|
||||
if (verticalScroll.Used)
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
int value = verticalScroll.Consume();
|
||||
if (value != 0)
|
||||
simulator.Mouse.VerticalScroll(value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (horizontalScroll.Used)
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
int value = horizontalScroll.Consume();
|
||||
if (value != 0)
|
||||
simulator.Mouse.HorizontalScroll(value);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
UpdateValid();
|
||||
}
|
||||
|
||||
public void MouseClick(Button button)
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case Button.Left:
|
||||
simulator.Mouse.LeftButtonClick();
|
||||
return true;
|
||||
|
||||
case Button.Right:
|
||||
simulator.Mouse.RightButtonClick();
|
||||
return true;
|
||||
|
||||
case Button.Middle:
|
||||
simulator.Mouse.MiddleButtonClick();
|
||||
return true;
|
||||
|
||||
case Button.X:
|
||||
simulator.Mouse.XButtonClick(XButtonID);
|
||||
return true;
|
||||
|
||||
case Button.Y:
|
||||
simulator.Mouse.XButtonClick(YButtonID);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void MouseDoubleClick(Button button)
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case Button.Left:
|
||||
simulator.Mouse.LeftButtonDoubleClick();
|
||||
return true;
|
||||
|
||||
case Button.Right:
|
||||
simulator.Mouse.RightButtonDoubleClick();
|
||||
return true;
|
||||
|
||||
case Button.Middle:
|
||||
simulator.Mouse.MiddleButtonDoubleClick();
|
||||
return true;
|
||||
|
||||
case Button.X:
|
||||
simulator.Mouse.XButtonDoubleClick(XButtonID);
|
||||
return true;
|
||||
|
||||
case Button.Y:
|
||||
simulator.Mouse.XButtonDoubleClick(YButtonID);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void MoveBy(double pixelDeltaX, double pixelDeltaY)
|
||||
{
|
||||
#if ACCUM_MOUSE
|
||||
movedX.Add(pixelDeltaX);
|
||||
movedY.Add(pixelDeltaY);
|
||||
#else
|
||||
if (pixelDeltaX == 0 && pixelDeltaY == 0)
|
||||
return;
|
||||
|
||||
Safe(() =>
|
||||
{
|
||||
simulator.Mouse.MoveMouseBy((int)pixelDeltaX, (int)pixelDeltaY);
|
||||
return true;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
public void MoveTo(double absoluteX, double absoluteY)
|
||||
{
|
||||
Safe(() =>
|
||||
{
|
||||
simulator.Mouse.MoveMouseTo(absoluteX, absoluteY);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void VerticalScroll(double scrollAmountInClicks)
|
||||
{
|
||||
#if ACCUM_SCROLL
|
||||
verticalScroll.Add(scrollAmountInClicks);
|
||||
#else
|
||||
if (scrollAmountInClicks == 0)
|
||||
return;
|
||||
|
||||
Safe(() =>
|
||||
{
|
||||
simulator.Mouse.VerticalScroll((int)scrollAmountInClicks);
|
||||
return true;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
public void HorizontalScroll(double scrollAmountInClicks)
|
||||
{
|
||||
#if ACCUM_SCROLL
|
||||
horizontalScroll.Add(scrollAmountInClicks);
|
||||
#else
|
||||
if (scrollAmountInClicks == 0)
|
||||
return;
|
||||
|
||||
Safe(() =>
|
||||
{
|
||||
simulator.Mouse.HorizontalScroll((int)scrollAmountInClicks);
|
||||
return true;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
76
SteamController/Devices/SteamButtons.cs
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
using hidapi;
|
||||
using PowerControl.External;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public partial class SteamController
|
||||
{
|
||||
public readonly SteamButton BtnL5 = new SteamButton2(0x08, SDCButton0.BTN_L5);
|
||||
public readonly SteamButton BtnOptions = new SteamButton2(0x08, SDCButton0.BTN_OPTIONS);
|
||||
public readonly SteamButton BtnSteam = new SteamButton2(0x08, SDCButton0.BTN_STEAM);
|
||||
public readonly SteamButton BtnMenu = new SteamButton2(0x08, SDCButton0.BTN_MENU);
|
||||
public readonly SteamButton BtnDpadDown = new SteamButton2(0x08, SDCButton0.BTN_DPAD_DOWN) { LizardButton = true };
|
||||
public readonly SteamButton BtnDpadLeft = new SteamButton2(0x08, SDCButton0.BTN_DPAD_LEFT) { LizardButton = true };
|
||||
public readonly SteamButton BtnDpadRight = new SteamButton2(0x08, SDCButton0.BTN_DPAD_RIGHT) { LizardButton = true };
|
||||
public readonly SteamButton BtnDpadUp = new SteamButton2(0x08, SDCButton0.BTN_DPAD_UP) { LizardButton = true };
|
||||
public readonly SteamButton BtnA = new SteamButton2(0x08, SDCButton0.BTN_A) { LizardButton = true };
|
||||
public readonly SteamButton BtnX = new SteamButton2(0x08, SDCButton0.BTN_X) { LizardButton = true };
|
||||
public readonly SteamButton BtnB = new SteamButton2(0x08, SDCButton0.BTN_B) { LizardButton = true };
|
||||
public readonly SteamButton BtnY = new SteamButton2(0x08, SDCButton0.BTN_Y) { LizardButton = true };
|
||||
public readonly SteamButton BtnL1 = new SteamButton2(0x08, SDCButton0.BTN_L1) { LizardButton = true };
|
||||
public readonly SteamButton BtnL2 = new SteamButton2(0x08, SDCButton0.BTN_L2) { LizardButton = true };
|
||||
public readonly SteamButton BtnR1 = new SteamButton2(0x08, SDCButton0.BTN_R1) { LizardButton = true };
|
||||
public readonly SteamButton BtnR2 = new SteamButton2(0x08, SDCButton0.BTN_R2) { LizardButton = true };
|
||||
public readonly SteamButton BtnLeftStickPress = new SteamButton2(0x0a, SDCButton1.BTN_LSTICK_PRESS);
|
||||
public readonly SteamButton BtnLPadTouch = new SteamButton2(0x0a, SDCButton1.BTN_LPAD_TOUCH);
|
||||
public readonly SteamButton BtnLPadPress = new SteamButton2(0x0a, SDCButton1.BTN_LPAD_PRESS);
|
||||
public readonly SteamButton BtnRPadPress = new SteamButton2(0x0a, SDCButton1.BTN_RPAD_PRESS);
|
||||
public readonly SteamButton BtnRPadTouch = new SteamButton2(0x0a, SDCButton1.BTN_RPAD_TOUCH);
|
||||
public readonly SteamButton BtnR5 = new SteamButton2(0x0a, SDCButton1.BTN_R5);
|
||||
public readonly SteamButton BtnRightStickPress = new SteamButton2(0x0B, SDCButton2.BTN_RSTICK_PRESS);
|
||||
public readonly SteamButton BtnLStickTouch = new SteamButton2(0x0D, SDCButton4.BTN_LSTICK_TOUCH);
|
||||
public readonly SteamButton BtnRStickTouch = new SteamButton2(0x0D, SDCButton4.BTN_RSTICK_TOUCH);
|
||||
public readonly SteamButton BtnR4 = new SteamButton2(0x0D, SDCButton4.BTN_R4);
|
||||
public readonly SteamButton BtnL4 = new SteamButton2(0x0D, SDCButton4.BTN_L4);
|
||||
public readonly SteamButton BtnQuickAccess = new SteamButton2(0x0E, SDCButton5.BTN_QUICK_ACCESS);
|
||||
|
||||
public readonly SteamButton BtnVirtualLeftThumbUp = new SteamButton();
|
||||
public readonly SteamButton BtnVirtualLeftThumbDown = new SteamButton();
|
||||
public readonly SteamButton BtnVirtualLeftThumbLeft = new SteamButton();
|
||||
public readonly SteamButton BtnVirtualLeftThumbRight = new SteamButton();
|
||||
|
||||
public readonly SteamAxis LPadX = new SteamAxis(0x10);
|
||||
public readonly SteamAxis LPadY = new SteamAxis(0x12);
|
||||
public readonly SteamAxis RPadX = new SteamAxis(0x14) { LizardMouse = true };
|
||||
public readonly SteamAxis RPadY = new SteamAxis(0x16) { LizardMouse = true };
|
||||
public readonly SteamAxis AccelX = new SteamAxis(0x18);
|
||||
public readonly SteamAxis AccelY = new SteamAxis(0x1A);
|
||||
public readonly SteamAxis AccelZ = new SteamAxis(0x1C);
|
||||
public readonly SteamAxis GyroPitch = new SteamAxis(0x1E);
|
||||
public readonly SteamAxis GyroYaw = new SteamAxis(0x20);
|
||||
public readonly SteamAxis GyroRoll = new SteamAxis(0x22);
|
||||
public readonly SteamAxis LeftTrigger = new SteamAxis(0x2C);
|
||||
public readonly SteamAxis RightTrigger = new SteamAxis(0x2E);
|
||||
public readonly SteamAxis LeftThumbX = new SteamAxis(0x30) { Deadzone = 5000, MinChange = 10 };
|
||||
public readonly SteamAxis LeftThumbY = new SteamAxis(0x32) { Deadzone = 5000, MinChange = 10 };
|
||||
public readonly SteamAxis RightThumbX = new SteamAxis(0x34) { Deadzone = 5000, MinChange = 10 };
|
||||
public readonly SteamAxis RightThumbY = new SteamAxis(0x36) { Deadzone = 5000, MinChange = 10 };
|
||||
public readonly SteamAxis LPadPressure = new SteamAxis(0x38);
|
||||
public readonly SteamAxis RPadPressure = new SteamAxis(0x38);
|
||||
|
||||
private void InitializeButtons()
|
||||
{
|
||||
LPadX.ActiveButton = BtnLPadTouch;
|
||||
LPadY.ActiveButton = BtnLPadTouch;
|
||||
RPadX.ActiveButton = BtnRPadTouch;
|
||||
RPadY.ActiveButton = BtnRPadTouch;
|
||||
|
||||
// map virtual key presses
|
||||
LeftThumbX.VirtualLeft = BtnVirtualLeftThumbLeft;
|
||||
LeftThumbX.VirtualRight = BtnVirtualLeftThumbRight;
|
||||
LeftThumbY.VirtualLeft = BtnVirtualLeftThumbDown;
|
||||
LeftThumbY.VirtualRight = BtnVirtualLeftThumbUp;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
SteamController/Devices/SteamController.cs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using hidapi;
|
||||
using PowerControl.External;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public partial class SteamController : IDisposable
|
||||
{
|
||||
public const ushort SteamVendorID = 0x28DE;
|
||||
public const ushort SteamProductID = 0x1205;
|
||||
public const int ReadTimeout = 50;
|
||||
|
||||
private hidapi.HidDevice neptuneDevice;
|
||||
|
||||
public SteamController()
|
||||
{
|
||||
InitializeButtons();
|
||||
InitializeActions();
|
||||
|
||||
neptuneDevice = new hidapi.HidDevice(SteamVendorID, SteamProductID, 64);
|
||||
neptuneDevice.OpenDevice();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Updated { get; private set; }
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
foreach (var action in AllActions)
|
||||
action.Reset();
|
||||
}
|
||||
|
||||
private void BeforeUpdate(byte[] buffer)
|
||||
{
|
||||
foreach (var action in AllActions)
|
||||
action.BeforeUpdate(buffer, this);
|
||||
}
|
||||
|
||||
internal void BeforeUpdate()
|
||||
{
|
||||
byte[] data = neptuneDevice.Read(ReadTimeout);
|
||||
if (data == null)
|
||||
{
|
||||
Reset();
|
||||
Updated = false;
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeUpdate(data);
|
||||
Updated = true;
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
foreach (var action in AllActions)
|
||||
action.Update();
|
||||
|
||||
UpdateLizardButtons();
|
||||
UpdateLizardMouse();
|
||||
}
|
||||
}
|
||||
}
|
||||
421
SteamController/Devices/SteamControllerActions.cs
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
using hidapi;
|
||||
using PowerControl.External;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public partial class SteamController
|
||||
{
|
||||
public abstract class SteamAction
|
||||
{
|
||||
public String Name { get; internal set; } = "";
|
||||
|
||||
/// This is action controlled by Lizard mode
|
||||
public bool LizardButton { get; set; }
|
||||
public bool LizardMouse { get; set; }
|
||||
public DateTime LastUpdated { get; protected set; } = DateTime.Now;
|
||||
public double DeltaTime { get; protected set; }
|
||||
|
||||
internal abstract void Reset();
|
||||
internal abstract bool BeforeUpdate(byte[] buffer, SteamController controller);
|
||||
internal abstract void Update();
|
||||
|
||||
protected void UpdateTime()
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
DeltaTime = (now - LastUpdated).TotalSeconds;
|
||||
LastUpdated = now;
|
||||
}
|
||||
|
||||
protected bool UsedByLizard(SteamController controller)
|
||||
{
|
||||
if (LizardButton && controller.LizardButtons)
|
||||
return true;
|
||||
if (LizardMouse && controller.LizardMouse)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class SteamButton : SteamAction
|
||||
{
|
||||
public static readonly TimeSpan DefaultHoldDuration = TimeSpan.FromMilliseconds(10);
|
||||
public static readonly TimeSpan DefaultFirstHold = TimeSpan.FromMilliseconds(75);
|
||||
public static readonly TimeSpan DefaultRepeatHold = TimeSpan.FromMilliseconds(150);
|
||||
|
||||
public bool Value { get; private set; }
|
||||
public bool LastValue { get; private set; }
|
||||
|
||||
/// Last press was already consumed by other
|
||||
public object? Consumed { get; private set; }
|
||||
|
||||
/// Set on raising edge
|
||||
public DateTime? HoldSince { get; private set; }
|
||||
public DateTime? HoldRepeated { get; private set; }
|
||||
|
||||
public SteamButton()
|
||||
{
|
||||
}
|
||||
|
||||
public static implicit operator bool(SteamButton button) => button.Hold(DefaultHoldDuration, null);
|
||||
|
||||
/// Generated when button is pressed for the first time
|
||||
public bool JustPressed()
|
||||
{
|
||||
if (!LastValue && Value)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Generated on failing edge of key press
|
||||
public bool Pressed(TimeSpan? duration = null)
|
||||
{
|
||||
// We expect Last to be true, and now to be false (failing edge)
|
||||
if (!(LastValue && !Value))
|
||||
return false;
|
||||
|
||||
if (Consumed is not null)
|
||||
return false;
|
||||
|
||||
if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Consume(object consume)
|
||||
{
|
||||
if (Consumed is not null && Consumed != consume)
|
||||
return false;
|
||||
|
||||
Consumed = consume;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Hold(object? consume = null)
|
||||
{
|
||||
return Hold(null, consume);
|
||||
}
|
||||
|
||||
/// Generated when button was hold for a given period
|
||||
public bool Hold(TimeSpan? duration, object? consume = null)
|
||||
{
|
||||
if (!Value)
|
||||
return false;
|
||||
|
||||
if (Consumed is not null && Consumed != consume)
|
||||
return false;
|
||||
|
||||
if (duration.HasValue && HoldSince?.Add(duration.Value) >= DateTime.Now)
|
||||
return false;
|
||||
|
||||
if (consume is not null)
|
||||
Consumed = consume;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HoldOnce(object consume)
|
||||
{
|
||||
return HoldOnce(null, consume);
|
||||
}
|
||||
|
||||
/// Generated when button was hold for a given period
|
||||
/// but triggered exactly once
|
||||
public bool HoldOnce(TimeSpan? duration, object consume)
|
||||
{
|
||||
if (!Hold(duration))
|
||||
return false;
|
||||
|
||||
Consumed = consume;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Generated when button was repeated for a given period
|
||||
/// but triggered exactly once
|
||||
public bool HoldRepeat(TimeSpan duration, TimeSpan repeatEvery, object consume)
|
||||
{
|
||||
// always generate at least one keypress
|
||||
if (Pressed(duration))
|
||||
return true;
|
||||
|
||||
if (!Hold(duration, consume))
|
||||
return false;
|
||||
|
||||
// first keypress
|
||||
if (!HoldRepeated.HasValue)
|
||||
{
|
||||
HoldRepeated = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
// repeated keypress
|
||||
if (HoldRepeated.Value.Add(repeatEvery) <= DateTime.Now)
|
||||
{
|
||||
HoldRepeated = DateTime.Now;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool HoldRepeat(object consume)
|
||||
{
|
||||
return HoldRepeat(DefaultFirstHold, DefaultRepeatHold, consume);
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
LastValue = Value;
|
||||
Value = false;
|
||||
HoldSince = null;
|
||||
HoldRepeated = null;
|
||||
Consumed = null;
|
||||
}
|
||||
|
||||
internal void SetValue(bool value)
|
||||
{
|
||||
LastValue = Value;
|
||||
Value = value;
|
||||
UpdateTime();
|
||||
|
||||
if (!LastValue && Value)
|
||||
{
|
||||
HoldSince = DateTime.Now;
|
||||
HoldRepeated = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal override bool BeforeUpdate(byte[] buffer, SteamController controller)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal override void Update()
|
||||
{
|
||||
if (!Value)
|
||||
Consumed = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class SteamButton2 : SteamButton
|
||||
{
|
||||
private int offset;
|
||||
private uint mask;
|
||||
|
||||
public SteamButton2(int offset, object mask)
|
||||
{
|
||||
this.offset = offset;
|
||||
this.mask = (uint)mask.GetHashCode();
|
||||
|
||||
while (this.mask > 0xFF)
|
||||
{
|
||||
this.mask >>= 8;
|
||||
this.offset++;
|
||||
}
|
||||
}
|
||||
|
||||
internal override bool BeforeUpdate(byte[] buffer, SteamController controller)
|
||||
{
|
||||
if (UsedByLizard(controller))
|
||||
return false;
|
||||
|
||||
if (offset < buffer.Length)
|
||||
{
|
||||
SetValue((buffer[offset] & mask) != 0);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetValue((buffer[offset] & mask) != 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class SteamAxis : SteamAction
|
||||
{
|
||||
public const short VirtualLeftThreshold = short.MinValue / 2;
|
||||
public const short VirtualRightThreshold = short.MaxValue / 2;
|
||||
|
||||
private int offset;
|
||||
|
||||
public SteamButton? ActiveButton { get; internal set; }
|
||||
public SteamButton? VirtualLeft { get; internal set; }
|
||||
public SteamButton? VirtualRight { get; internal set; }
|
||||
public short Value { get; private set; }
|
||||
public short LastValue { get; private set; }
|
||||
public short Deadzone { get; set; }
|
||||
public short MinChange { get; set; }
|
||||
|
||||
public SteamAxis(int offset)
|
||||
{
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public static implicit operator bool(SteamAxis button) => button.Active;
|
||||
public static implicit operator short(SteamAxis button) => button.Value;
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get
|
||||
{
|
||||
return ActiveButton?.Value ?? true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ScaledMode
|
||||
{
|
||||
Absolute,
|
||||
AbsoluteTime,
|
||||
Delta,
|
||||
DeltaTime
|
||||
}
|
||||
|
||||
public double Scaled(double min, double max, ScaledMode mode)
|
||||
{
|
||||
int value = 0;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case ScaledMode.Absolute:
|
||||
if (Math.Abs(Value) < Deadzone)
|
||||
return 0.0;
|
||||
value = Value;
|
||||
break;
|
||||
|
||||
case ScaledMode.AbsoluteTime:
|
||||
if (Math.Abs(Value) < Deadzone)
|
||||
return 0.0;
|
||||
value = (int)(Value * DeltaTime);
|
||||
break;
|
||||
|
||||
case ScaledMode.Delta:
|
||||
value = Value - LastValue;
|
||||
if (Math.Abs(Value) < MinChange)
|
||||
return 0.0;
|
||||
break;
|
||||
|
||||
case ScaledMode.DeltaTime:
|
||||
value = Value - LastValue;
|
||||
if (Math.Abs(Value) < MinChange)
|
||||
return 0.0;
|
||||
value = (int)(value * DeltaTime);
|
||||
break;
|
||||
}
|
||||
|
||||
if (value == 0)
|
||||
return 0.0;
|
||||
|
||||
double factor = (double)(value - short.MinValue) / (short.MaxValue - short.MinValue);
|
||||
return factor * (max - min) + min;
|
||||
}
|
||||
|
||||
public double Scaled(double range, ScaledMode mode)
|
||||
{
|
||||
return Scaled(-range, range, mode);
|
||||
}
|
||||
|
||||
public int Scaled(int min, int max, ScaledMode mode)
|
||||
{
|
||||
return (int)Scaled((double)min, (double)max, mode);
|
||||
}
|
||||
|
||||
public int Scaled(int range, ScaledMode mode)
|
||||
{
|
||||
return Scaled(-range, range, mode);
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
LastValue = Value;
|
||||
Value = 0;
|
||||
}
|
||||
|
||||
internal void SetValue(short value)
|
||||
{
|
||||
LastValue = Value;
|
||||
Value = value;
|
||||
UpdateTime();
|
||||
|
||||
// first time pressed, reset value as this is a Pad
|
||||
if (ActiveButton is not null && ActiveButton.JustPressed())
|
||||
LastValue = Value;
|
||||
|
||||
if (VirtualRight is not null)
|
||||
VirtualRight.SetValue(value > VirtualRightThreshold);
|
||||
|
||||
if (VirtualLeft is not null)
|
||||
VirtualLeft.SetValue(value < VirtualLeftThreshold);
|
||||
}
|
||||
|
||||
internal override bool BeforeUpdate(byte[] buffer, SteamController controller)
|
||||
{
|
||||
if (UsedByLizard(controller))
|
||||
return false;
|
||||
|
||||
if (offset + 1 < buffer.Length)
|
||||
{
|
||||
SetValue(BitConverter.ToInt16(buffer, offset));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetValue(0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Update()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public SteamAction?[] AllActions { get; private set; }
|
||||
public SteamButton?[] AllButtons { get; private set; }
|
||||
public SteamAxis?[] AllAxises { get; private set; }
|
||||
|
||||
public IEnumerable<SteamButton> HoldButtons
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var action in AllButtons)
|
||||
{
|
||||
if (action.Value)
|
||||
yield return action;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeActions()
|
||||
{
|
||||
var allActions = GetType().
|
||||
GetFields().
|
||||
Where((field) => field.FieldType.IsSubclassOf(typeof(SteamAction))).
|
||||
Select((field) => Tuple.Create(field, field.GetValue(this) as SteamAction)).
|
||||
ToList();
|
||||
|
||||
allActions.ForEach((tuple) => tuple.Item2.Name = tuple.Item1.Name);
|
||||
|
||||
AllActions = allActions.Select((tuple) => tuple.Item2).ToArray();
|
||||
AllAxises = allActions.Where((tuple) => tuple.Item2 is SteamAxis).Select((tuple) => tuple.Item2 as SteamAxis).ToArray();
|
||||
AllButtons = allActions.Where((tuple) => tuple.Item2 is SteamButton).Select((tuple) => tuple.Item2 as SteamButton).ToArray();
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetReport()
|
||||
{
|
||||
List<string> report = new List<string>();
|
||||
|
||||
var buttons = AllButtons.Where((button) => button.Value).Select((button) => button.Name);
|
||||
if (buttons.Any())
|
||||
yield return String.Format("Buttons: {0}", String.Join(",", buttons));
|
||||
|
||||
foreach (var axis in AllAxises)
|
||||
{
|
||||
if (!axis.Active)
|
||||
continue;
|
||||
yield return String.Format("Axis: {0} = {1} [Delta: {2}]", axis.Name, axis.Value, axis.Value - axis.LastValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
73
SteamController/Devices/SteamControllerLizard.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using hidapi;
|
||||
using PowerControl.External;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public partial class SteamController
|
||||
{
|
||||
public const int LizardModeUpdateInterval = 250;
|
||||
|
||||
public bool LizardMouse { get; set; } = true;
|
||||
public bool LizardButtons { get; set; } = true;
|
||||
|
||||
private bool? savedLizardMouse;
|
||||
private bool? savedLizardButtons;
|
||||
private DateTime lizardMouseUpdated = DateTime.Now;
|
||||
private DateTime lizardButtonUpdated = DateTime.Now;
|
||||
|
||||
private void UpdateLizardMouse()
|
||||
{
|
||||
if (savedLizardMouse == LizardMouse)
|
||||
{
|
||||
// We need to explicitly disable lizard every some time
|
||||
// but don't fight enabling it, as someone else might be taking control (Steam?)
|
||||
if (LizardMouse || lizardMouseUpdated.AddMilliseconds(LizardModeUpdateInterval) > DateTime.Now)
|
||||
return;
|
||||
}
|
||||
|
||||
if (LizardMouse)
|
||||
{
|
||||
//Enable mouse emulation
|
||||
byte[] data = new byte[] { 0x8e, 0x00 };
|
||||
neptuneDevice.RequestFeatureReport(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Disable mouse emulation
|
||||
byte[] data = new byte[] { 0x87, 0x03, 0x08, 0x07 };
|
||||
neptuneDevice.RequestFeatureReport(data);
|
||||
}
|
||||
|
||||
savedLizardMouse = LizardMouse;
|
||||
lizardMouseUpdated = DateTime.Now;
|
||||
}
|
||||
|
||||
private void UpdateLizardButtons()
|
||||
{
|
||||
if (savedLizardButtons == LizardButtons)
|
||||
{
|
||||
// We need to explicitly disable lizard every some time
|
||||
// but don't fight enabling it, as someone else might be taking control (Steam?)
|
||||
if (LizardButtons || lizardButtonUpdated.AddMilliseconds(LizardModeUpdateInterval) > DateTime.Now)
|
||||
return;
|
||||
}
|
||||
|
||||
if (LizardButtons)
|
||||
{
|
||||
//Enable keyboard/mouse button emulation
|
||||
byte[] data = new byte[] { 0x85, 0x00 };
|
||||
neptuneDevice.RequestFeatureReport(data);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Disable keyboard/mouse button emulation
|
||||
byte[] data = new byte[] { 0x81, 0x00 };
|
||||
neptuneDevice.RequestFeatureReport(data);
|
||||
}
|
||||
|
||||
savedLizardButtons = LizardButtons;
|
||||
lizardButtonUpdated = DateTime.Now;
|
||||
}
|
||||
}
|
||||
}
|
||||
160
SteamController/Devices/Xbox360Controller.cs
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
using Nefarius.ViGEm.Client;
|
||||
using Nefarius.ViGEm.Client.Exceptions;
|
||||
using Nefarius.ViGEm.Client.Targets;
|
||||
using Nefarius.ViGEm.Client.Targets.Xbox360;
|
||||
using static CommonHelpers.Log;
|
||||
|
||||
namespace SteamController.Devices
|
||||
{
|
||||
public class Xbox360Controller : IDisposable
|
||||
{
|
||||
private ViGEmClient? client;
|
||||
private IXbox360Controller? device;
|
||||
private bool isConnected;
|
||||
private bool submitReport;
|
||||
|
||||
public Xbox360Controller()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
using (client) { }
|
||||
}
|
||||
|
||||
internal bool CreateClient()
|
||||
{
|
||||
if (this.device is not null)
|
||||
return true;
|
||||
|
||||
try
|
||||
{
|
||||
var client = new ViGEmClient();
|
||||
var device = client.CreateXbox360Controller();
|
||||
device.AutoSubmitReport = false;
|
||||
device.FeedbackReceived += X360Device_FeedbackReceived;
|
||||
this.device = device;
|
||||
this.client = client;
|
||||
return true;
|
||||
}
|
||||
catch (VigemBusNotFoundException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void BeforeUpdate()
|
||||
{
|
||||
device?.ResetReport();
|
||||
|
||||
if (!isConnected)
|
||||
{
|
||||
FeedbackLargeMotor = 0;
|
||||
FeedbackSmallMotor = 0;
|
||||
LedNumber = 0;
|
||||
}
|
||||
|
||||
submitReport = false;
|
||||
}
|
||||
|
||||
private void UpdateConnected()
|
||||
{
|
||||
if (Connected == isConnected)
|
||||
return;
|
||||
|
||||
if (Connected)
|
||||
{
|
||||
device?.Connect();
|
||||
TraceLine("Connected X360 Controller.");
|
||||
}
|
||||
else
|
||||
{
|
||||
device?.Disconnect();
|
||||
TraceLine("Disconnected X360 Controller.");
|
||||
}
|
||||
|
||||
isConnected = Connected;
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
UpdateConnected();
|
||||
|
||||
if (isConnected && submitReport)
|
||||
{
|
||||
device?.SubmitReport();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Valid
|
||||
{
|
||||
get { return device is not null; }
|
||||
}
|
||||
|
||||
public bool Connected { get; set; }
|
||||
public byte FeedbackLargeMotor { get; internal set; }
|
||||
public byte FeedbackSmallMotor { get; internal set; }
|
||||
public byte LedNumber { get; internal set; }
|
||||
|
||||
public bool this[Xbox360Button button]
|
||||
{
|
||||
set
|
||||
{
|
||||
SetButtonState(button, value);
|
||||
}
|
||||
}
|
||||
|
||||
public short this[Xbox360Axis axis]
|
||||
{
|
||||
set
|
||||
{
|
||||
SetAxisValue(axis, value);
|
||||
}
|
||||
}
|
||||
|
||||
public short this[Xbox360Slider slider]
|
||||
{
|
||||
set
|
||||
{
|
||||
SetSliderValue(slider, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAxisValue(Xbox360Axis axis, short value)
|
||||
{
|
||||
device?.SetAxisValue(axis, value);
|
||||
submitReport = true;
|
||||
}
|
||||
|
||||
public void SetButtonState(Xbox360Button button, bool pressed)
|
||||
{
|
||||
device?.SetButtonState(button, pressed);
|
||||
submitReport = true;
|
||||
}
|
||||
|
||||
public void SetSliderValue(Xbox360Slider slider, byte value)
|
||||
{
|
||||
device?.SetSliderValue(slider, value);
|
||||
submitReport = true;
|
||||
}
|
||||
|
||||
public void SetSliderValue(Xbox360Slider slider, short value)
|
||||
{
|
||||
// rescale from -32767..32768 to 0..255
|
||||
int result = value;
|
||||
result -= short.MinValue;
|
||||
result *= byte.MaxValue;
|
||||
result /= ushort.MaxValue;
|
||||
device?.SetSliderValue(slider, (byte)result);
|
||||
submitReport = true;
|
||||
}
|
||||
|
||||
private void X360Device_FeedbackReceived(object sender, Xbox360FeedbackReceivedEventArgs e)
|
||||
{
|
||||
FeedbackLargeMotor = e.LargeMotor;
|
||||
FeedbackSmallMotor = e.SmallMotor;
|
||||
LedNumber = e.LedNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
91
SteamController/Profiles/Context.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
SteamController/Profiles/DebugProfile.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
SteamController/Profiles/DesktopProfile.cs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
19
SteamController/Profiles/Profile.cs
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
166
SteamController/Profiles/SteamShortcutsProfile.cs
Normal 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
SteamController/Profiles/X360Profile.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
SteamController/Program.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
namespace SteamController
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
// To customize application configuration such as set high DPI settings or default font,
|
||||
// see https://aka.ms/applicationconfiguration.
|
||||
ApplicationConfiguration.Initialize();
|
||||
|
||||
using (var controller = new Controller())
|
||||
{
|
||||
Application.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
123
SteamController/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace SteamController {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SteamController.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
|
||||
/// </summary>
|
||||
internal static System.Drawing.Icon microsoft_xbox_controller {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("microsoft_xbox_controller", 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 microsoft_xbox_controller_off {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("microsoft_xbox_controller_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 microsoft_xbox_controller_off_red {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("microsoft_xbox_controller_off_red", 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 microsoft_xbox_controller_off_white {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("microsoft_xbox_controller_off_white", 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 microsoft_xbox_controller_red {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("microsoft_xbox_controller_red", 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 microsoft_xbox_controller_white {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("microsoft_xbox_controller_white", resourceCulture);
|
||||
return ((System.Drawing.Icon)(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
139
SteamController/Resources.resx
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<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="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>
|
||||
<data name="microsoft_xbox_controller_off" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\microsoft-xbox-controller-off.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="microsoft_xbox_controller_off_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\microsoft-xbox-controller-off-red.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="microsoft_xbox_controller_off_white" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\microsoft-xbox-controller-off-white.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="microsoft_xbox_controller_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>Resources\microsoft-xbox-controller-red.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<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>
|
||||
</root>
|
||||
BIN
SteamController/Resources/microsoft-xbox-controller-off-red.ico
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
SteamController/Resources/microsoft-xbox-controller-off-red.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 920 B |
BIN
SteamController/Resources/microsoft-xbox-controller-off.ico
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
SteamController/Resources/microsoft-xbox-controller-off.png
Normal file
|
After Width: | Height: | Size: 839 B |
BIN
SteamController/Resources/microsoft-xbox-controller-red.ico
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
SteamController/Resources/microsoft-xbox-controller-red.png
Normal file
|
After Width: | Height: | Size: 942 B |
BIN
SteamController/Resources/microsoft-xbox-controller-white.ico
Normal file
|
After Width: | Height: | Size: 186 KiB |
BIN
SteamController/Resources/microsoft-xbox-controller-white.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
SteamController/Resources/microsoft-xbox-controller.ico
Normal file
|
After Width: | Height: | Size: 165 KiB |
BIN
SteamController/Resources/microsoft-xbox-controller.png
Normal file
|
After Width: | Height: | Size: 712 B |
26
SteamController/Settings.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace SteamController {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
SteamController/Settings.settings
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?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>
|
||||
68
SteamController/SteamController.csproj
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<ApplicationIcon>Resources\microsoft-xbox-controller.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="app.manifest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\microsoft-xbox-controller.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="H.InputSimulator" Version="1.3.0" />
|
||||
<PackageReference Include="Nefarius.ViGEm.Client" Version="1.21.232" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CommonHelpers\CommonHelpers.csproj" />
|
||||
<ProjectReference Include="..\ExternalHelpers\ExternalHelpers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="hidapi.net">
|
||||
<HintPath>..\ExternalHelpers\hidapi.net.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
61
SteamController/app.manifest
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="0.1.0.0" name="PerformanceOverlay.app"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<!-- UAC Manifest Options
|
||||
If you want to change the Windows User Account Control level replace the
|
||||
requestedExecutionLevel node with one of the following.
|
||||
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
|
||||
Specifying requestedExecutionLevel element will disable file and registry virtualization.
|
||||
Remove this element if your application requires this virtualization for backwards
|
||||
compatibility.
|
||||
-->
|
||||
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
|
||||
<!--<requestedExecutionLevel level="requireAdministrator" uiAccess="true" />-->
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
|
||||
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
|
||||
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
|
||||
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
|
||||
|
||||
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
|
||||
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
|
||||
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
|
||||
<!--
|
||||
<dependency>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Microsoft.Windows.Common-Controls"
|
||||
version="6.0.0.0"
|
||||
processorArchitecture="*"
|
||||
publicKeyToken="6595b64144ccf1df"
|
||||
language="*"
|
||||
/>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
</assembly>
|
||||
|
|
@ -11,7 +11,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommonHelpers", "CommonHelp
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PowerControl", "PowerControl\PowerControl.csproj", "{85A44F35-60C9-493E-B1A7-FB2284E5ACCF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExternalHelpers", "ExternalHelpers\ExternalHelpers.csproj", "{A3FD29A4-844F-42B1-80C5-0301BD053AC0}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExternalHelpers", "ExternalHelpers\ExternalHelpers.csproj", "{A3FD29A4-844F-42B1-80C5-0301BD053AC0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamController", "SteamController\SteamController.csproj", "{A5A9DCD4-4686-49A6-836A-81498226B94B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -83,6 +85,18 @@ Global
|
|||
{A3FD29A4-844F-42B1-80C5-0301BD053AC0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A3FD29A4-844F-42B1-80C5-0301BD053AC0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A3FD29A4-844F-42B1-80C5-0301BD053AC0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A5A9DCD4-4686-49A6-836A-81498226B94B}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
|
|||