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