diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..cfb7374
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,14 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "C#: XinputWindowsManager Debug",
+ "type": "dotnet",
+ "request": "launch",
+ "projectPath": "${workspaceFolder}/XinputWindowsManager/XinputWindowsManager.csproj"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Images/screenshot1.png b/Images/screenshot1.png
index a8605ec..030c9ba 100644
Binary files a/Images/screenshot1.png and b/Images/screenshot1.png differ
diff --git a/README.md b/README.md
index 9a27f4a..8eced03 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
## About
-This system tray Windows 10 application allows the user to toggle a mouse mode by pressing a special button combination on an Xinput gamepad such as the Xbox One Controller. In this mode, the gamepad is able to freely move the cursor around the screen, perform left and right click actions as well as some useful keyboard presses.
+This system tray Windows application allows the user to toggle a desktop manager mode by pressing a special button combination on an Xinput gamepad such as the Xbox One Controller. In this mode, the gamepad is able to freely move the cursor around the screen, perform left and right click actions as well as some useful keyboard presses.
The software was developed so that users with a desktop setup with only gamepad input or at least inconvenient access to keyboard and mouse can perform basic tasks that require these input methods using a gamepad. An example would be my case, where I use a desktop PC with Steam Big Picture in my living room as a DIY game console.
@@ -25,16 +25,35 @@ Optional - If you want the app to start with windows do the following:
## Usage
-You can toggle mouse mode by right-clicking the system tray icon and selecting the toggle option or by pressing the following button combination on your xinput controller (player one):
+You can toggle manager mode by right-clicking the system tray icon and selecting the toggle option or by pressing the toggle button combination on your xinput controller (player one). Combination can be set in App.Config and it defaults to:
- `BACK` + `A` + `X`
While in mouse mode, the following actions can be performed:
- `Left analog stick`: controls mouse cursor movement;
- `A`: performs left click;
- `X`: performs right click;
+- `Y`: opens Windows Task Manager (CTRL+SHIFT+ESC);
+- `B`: presses ESC key;
+- `LS`: opens OnScreen Keyboard;
+- `RS`: sends mute command;
+- `LT`: presses Windows key;
+- `RT`: minimizes all apps (Win+D);
+- `LB+RB`: performs performs ALT+TAB;
+- `DPAD`: sends arrow keys from keyboard;
+- `Right analog stick UP/DOWN`: controls system volume;
+- `Right analog stick LEFT/RIGHT`: controls prev/next song;
+
+Other settings can be changes on App.Config but default values should sufice on most cases.
## Release Notes
+### 2.0.0
+
+- Refactors codebase
+- Upgrade to .NET 8
+- Utilizes XInputium instead of SharpDX for Xinput control
+- Utilizes InputSimulatorCore for Keyboard/Mouse control
+
### 1.0.0
- Initial version
diff --git a/XinputWindowsManager/App.Config b/XinputWindowsManager/App.Config
new file mode 100644
index 0000000..39f2524
--- /dev/null
+++ b/XinputWindowsManager/App.Config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/XinputWindowsManager/DesktopManagerService.cs b/XinputWindowsManager/DesktopManagerService.cs
deleted file mode 100644
index d1243b0..0000000
--- a/XinputWindowsManager/DesktopManagerService.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Drawing;
-using System.Threading.Tasks;
-using SharpDX.XInput;
-
-namespace XinputWindowsManager
-{
- public class DesktopManagerService
- {
- private const double MAX_CURSOR_MOVEMENT_SPEED = 25.0;
-
- private const double THUMBSTICK_MOVEMENT_THRESHOLD = 0.3;
-
- private static TimeSpan FRAME_TIME = TimeSpan.FromSeconds(1 / 120.0);
-
- private readonly XinputController gamepad;
-
- private readonly Stopwatch frameCounter;
-
- public DesktopManagerService()
- {
- this.gamepad = new XinputController(UserIndex.One);
- this.frameCounter = new Stopwatch();
- this.ServiceRunning = false;
- this.MouseModeOn = false;
- }
-
- public event EventHandler OnMouseModeToggled;
-
- public bool ServiceRunning { get; private set; }
-
- public bool MouseModeOn { get; private set; }
-
- public void Stop()
- {
- this.MouseModeOn = false;
- this.ServiceRunning = false;
- }
-
- public void Start()
- {
- this.ServiceRunning = true;
- this.frameCounter.Start();
-
- while (this.ServiceRunning)
- {
- if (this.gamepad.ButtonCombinationPressed(
- GamepadButtonFlags.Back, GamepadButtonFlags.A, GamepadButtonFlags.X))
- this.ToggleMouseMode();
-
- if (MouseModeOn)
- this.ProcessMouseMode();
-
- this.PoolXinputController();
- }
- }
-
- public void ToggleMouseMode()
- {
- this.MouseModeOn = !this.MouseModeOn;
- Debug.WriteLine($"Switching mouse mode to '{this.MouseModeOn}'");
- Console.Beep();
- this.OnMouseModeToggled?.Invoke(this, this.MouseModeOn);
- }
-
- private void PoolXinputController()
- {
- this.gamepad.Pool();
- TimeSpan elapsedTime = this.frameCounter.Elapsed;
- this.frameCounter.Reset();
-
- if (FRAME_TIME > elapsedTime)
- Task.Delay(FRAME_TIME - elapsedTime).Wait();
- }
-
- private void ProcessMouseMode()
- {
- // Move mouse cursor with left thumb stick
- double leftStickX = gamepad.LeftStickXState();
- double leftStickY = gamepad.LeftStickYState();
-
- if (leftStickX > THUMBSTICK_MOVEMENT_THRESHOLD || leftStickX < -THUMBSTICK_MOVEMENT_THRESHOLD
- || leftStickY > THUMBSTICK_MOVEMENT_THRESHOLD || leftStickY < -THUMBSTICK_MOVEMENT_THRESHOLD)
- {
- Point mousePos = WindowsMouseCursor.GetPosition();
- int newX = (int)(mousePos.X + (leftStickX * MAX_CURSOR_MOVEMENT_SPEED));
- int newY = (int)(mousePos.Y - (leftStickY * MAX_CURSOR_MOVEMENT_SPEED));
- Debug.WriteLine($"New cursor pos: x={mousePos.X}, y={mousePos.Y}");
- WindowsMouseCursor.SetPosition(newX, newY);
- }
-
- // Left click with mouse cursor if A button is pressed
- if (gamepad.ButtonPressed(GamepadButtonFlags.A))
- {
- Debug.WriteLine("Sending left click input...");
- WindowsMouseCursor.SendLeftClick();
- }
- if (gamepad.ButtonPressed(GamepadButtonFlags.X))
- {
- Debug.WriteLine("Sending right click input...");
- WindowsMouseCursor.SendRightClick();
- }
- }
- }
-}
diff --git a/XinputWindowsManager/ManagerConfiguration.cs b/XinputWindowsManager/ManagerConfiguration.cs
new file mode 100644
index 0000000..9637adb
--- /dev/null
+++ b/XinputWindowsManager/ManagerConfiguration.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Configuration;
+using System.Diagnostics;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using XInputium.XInput;
+
+namespace XinputWindowsManager
+{
+ public class ManagerConfiguration
+ {
+ public const string BUTTON_A = "A";
+
+ public const string BUTTON_B = "B";
+
+ public const string BUTTON_X = "X";
+
+ public const string BUTTON_Y = "Y";
+
+ public const string BUTTON_LB = "LB";
+
+ public const string BUTTON_RB = "RB";
+
+ public const string BUTTON_LT = "LT";
+
+ public const string BUTTON_RT = "RT";
+
+ public const string BUTTON_LS = "LS";
+
+ public const string BUTTON_RS = "RS";
+
+ public const string BUTTON_BACK = "BACK";
+
+ public const string BUTTON_START = "START";
+
+ public const string BUTTON_DPAD_UP = "DPAD_UP";
+
+ public const string BUTTON_DPAD_DOWN = "DPAD_DOWN";
+
+ public const string BUTTON_DPAD_LEFT = "DPAD_LEFT";
+
+ public const string BUTTON_DPAD_RIGHT = "DPAD_RIGHT";
+
+ public bool StartEnabled { get; set; }
+
+ public int XinputPollingDelayMsecs { get; set; }
+
+ public double MaxCursorMovementSpeed { get; set; }
+
+ public double LeftStickThreshold { get; set; }
+
+ public double RightStickThreshold { get; set; }
+
+ public double TriggerThreshold { get; set; }
+
+ public string[] ToggleManagerCombination { get; set; }
+
+ public ManagerConfiguration()
+ {
+ try
+ {
+ this.StartEnabled = this.ReadBoolSetting("START_ENABLED");
+ this.XinputPollingDelayMsecs = this.ReadIntSetting("XINPUT_POLLING_DELAY_MSECS", minvalue: 4, maxvalue: 32);
+ this.MaxCursorMovementSpeed = this.ReadDoubleSetting("MAX_CURSOR_MOVEMENT_SPEED", minvalue: 5.0, maxvalue: 50.0);
+ this.LeftStickThreshold = this.ReadDoubleSetting("LEFT_STICK_THRESHOLD", minvalue: 0.0, maxvalue: 1.0);
+ this.RightStickThreshold = this.ReadDoubleSetting("RIGHT_STICK_THRESHOLD", minvalue: 0.0, maxvalue: 1.0);
+ this.TriggerThreshold = this.ReadDoubleSetting("TRIGGER_THRESHOLD", minvalue: 0.0, maxvalue: 1.0);
+ this.ToggleManagerCombination = this.ParseToggleCombination();
+ }
+ catch (Exception e)
+ {
+ throw new ArgumentException($"Invalid App.Config. {e.Message}");
+ }
+ }
+
+ private string ReadSetting(string setting)
+ {
+ string value = ConfigurationManager.AppSettings[setting];
+
+ if (string.IsNullOrWhiteSpace(value))
+ {
+ throw new ArgumentException($"Setting {setting} not available or empty");
+ }
+
+ return value;
+ }
+
+ private bool ReadBoolSetting(string setting)
+ {
+ return bool.Parse(this.ReadSetting(setting));
+ }
+
+ private int ReadIntSetting(string setting, int minvalue, int maxvalue)
+ {
+ int v = int.Parse(this.ReadSetting(setting));
+
+ if (v < minvalue || v > maxvalue)
+ {
+ throw new ArgumentException($"Invalid value for setting {setting}. Value must be between {minvalue} and {maxvalue}.");
+ }
+
+ return v;
+ }
+
+ private double ReadDoubleSetting(string setting, double minvalue, double maxvalue)
+ {
+ double v = double.Parse(this.ReadSetting(setting));
+
+ if (v < minvalue || v > maxvalue)
+ {
+ throw new ArgumentException($"Invalid value for setting {setting}. Value must be between {minvalue} and {maxvalue}.");
+ }
+
+ return v;
+ }
+
+ private string[] ParseToggleCombination()
+ {
+ string rawValue = this.ReadSetting("TOGGLE_MANAGER_COMBINATION");
+
+ string[] splitValues = rawValue.Split(',');
+
+ foreach (string button in splitValues)
+ {
+ Debug.WriteLine($"Combination button: {button}");
+
+ if ( button != BUTTON_A
+ && button != BUTTON_B
+ && button != BUTTON_X
+ && button != BUTTON_Y
+ && button != BUTTON_LB
+ && button != BUTTON_RB
+ && button != BUTTON_LS
+ && button != BUTTON_RS
+ && button != BUTTON_LT
+ && button != BUTTON_RT
+ && button != BUTTON_BACK
+ && button != BUTTON_START
+ && button != BUTTON_DPAD_UP
+ && button != BUTTON_DPAD_DOWN
+ && button != BUTTON_DPAD_LEFT
+ && button != BUTTON_DPAD_RIGHT)
+ {
+ throw new ArgumentException($"Invalid button value in TOGGLE_MANAGER_COMBINATION: {button}");
+ }
+ }
+
+ return splitValues;
+ }
+ }
+}
diff --git a/XinputWindowsManager/Program.cs b/XinputWindowsManager/Program.cs
index b87cb98..64f707f 100644
--- a/XinputWindowsManager/Program.cs
+++ b/XinputWindowsManager/Program.cs
@@ -13,9 +13,18 @@ static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
- SystemTrayApplicationContext appContext = new SystemTrayApplicationContext();
- Application.ApplicationExit += appContext.HandleExit;
- Application.Run(appContext);
+
+ try
+ {
+ ManagerConfiguration managerConfiguration = new ManagerConfiguration();
+ SystemTrayApplicationContext appContext = new SystemTrayApplicationContext(managerConfiguration);
+ Application.ApplicationExit += appContext.HandleExit;
+ Application.Run(appContext);
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(text: $"{e.Message}", caption: "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
}
}
}
diff --git a/XinputWindowsManager/SystemTrayApplicationContext.cs b/XinputWindowsManager/SystemTrayApplicationContext.cs
index 275f29b..b0a3420 100644
--- a/XinputWindowsManager/SystemTrayApplicationContext.cs
+++ b/XinputWindowsManager/SystemTrayApplicationContext.cs
@@ -1,6 +1,12 @@
using System;
+using System.Diagnostics;
+using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
+using WindowsInput;
+using WindowsInput.Native;
+using XInputium;
+using XInputium.XInput;
namespace XinputWindowsManager
{
@@ -8,12 +14,10 @@ public class SystemTrayApplicationContext : ApplicationContext
{
private const string APPLICATION_NAME_FORMAT = "Xinput Windows Manager v{0}";
- private const string TOGGLE_LABEL_TEXT_FORMAT = "Toggle Mouse Mode {0}";
+ private const string TOGGLE_LABEL_TEXT_FORMAT = "Toggle Manager {0}";
private const string EXIT_LABEL_TEXT = "Exit";
- private readonly DesktopManagerService desktopManagerService;
-
private NotifyIcon trayIcon;
private ContextMenuStrip contextMenuStrip;
@@ -22,38 +26,29 @@ public class SystemTrayApplicationContext : ApplicationContext
private ToolStripMenuItem exitLabel;
- public SystemTrayApplicationContext()
- {
- this.SetupContextMenu();
- this.UpdateToggleLabel(false);
+ private bool desktopManagerActive = false;
- this.desktopManagerService = new DesktopManagerService();
- this.desktopManagerService.OnMouseModeToggled += this.HandleMouseModeToggle;
+ private ManagerConfiguration managerConfiguration;
- Task.Factory.StartNew(() => this.desktopManagerService.Start());
- }
+ private readonly InputSimulator inputSimulator;
- public void HandleExit(object sender, EventArgs e)
- {
- this.desktopManagerService.Stop();
- trayIcon.Visible = false; // Hide tray icon, otherwise it will remain shown until user mouses over it
- Application.Exit();
- }
+ private readonly XGamepad xinputDevice;
- private void HandleMouseModeToggle(object o, bool mouseModeOn)
- {
- if (this.contextMenuStrip.InvokeRequired)
- this.contextMenuStrip.Invoke((Action)(() => this.UpdateToggleLabel(mouseModeOn)));
- else
- this.UpdateToggleLabel(mouseModeOn);
- }
+ private DateTime LastToggleTime = DateTime.Now;
- private void SetupContextMenu()
+ private TimeSpan ToggleCooldown = TimeSpan.FromSeconds(1);
+
+ public SystemTrayApplicationContext(ManagerConfiguration managerConfiguration)
{
+ this.managerConfiguration = managerConfiguration;
+ this.inputSimulator = new InputSimulator();
+ this.xinputDevice = new XGamepad();
+
+ // SetupContextMenu
this.toggleLabel = new ToolStripMenuItem();
this.toggleLabel.Anchor = AnchorStyles.Right;
- this.toggleLabel.Click += (o, e) => this.desktopManagerService.ToggleMouseMode();
-
+ this.toggleLabel.Click += (o, e) => this.ToggleDesktopManager();
+
this.exitLabel = new ToolStripMenuItem(EXIT_LABEL_TEXT);
this.exitLabel.Anchor = AnchorStyles.Right;
this.exitLabel.Click += this.HandleExit;
@@ -70,11 +65,265 @@ private void SetupContextMenu()
Text = string.Format(APPLICATION_NAME_FORMAT, Application.ProductVersion),
ContextMenuStrip = this.contextMenuStrip,
};
+
+ this.UpdateToggleLabel(false);
+
+ // Setup Xinput events
+ this.xinputDevice.LeftJoystick.Updated += this.HandleLeftJoystickUpdated;
+ this.xinputDevice.RightJoystick.Updated += this.HandleRightJoystickUpdated;
+ this.xinputDevice.ButtonPressed += this.HandleXinputButtonPressed;
+ this.xinputDevice.LeftTriggerMove += this.HandleLeftTriggerMove;
+ this.xinputDevice.RightTriggerMove += this.HandleRightTriggerMove;
+
+ Task.Run(() => {
+ while (true)
+ {
+ this.xinputDevice.Update();
+ Task.Delay(TimeSpan.FromMilliseconds(8)).Wait();
+ }
+ });
+ }
+
+ public void HandleExit(object sender, EventArgs e)
+ {
+ trayIcon.Visible = false; // Hide tray icon, otherwise it will remain shown until user mouses over it
+ Application.Exit();
+ }
+
+ public void ToggleDesktopManager()
+ {
+ this.LastToggleTime = DateTime.Now;
+ this.desktopManagerActive = !this.desktopManagerActive;
+
+ if (this.contextMenuStrip.InvokeRequired)
+ {
+ this.contextMenuStrip.Invoke((Action)(() => this.UpdateToggleLabel(this.desktopManagerActive)));
+ }
+ else
+ {
+ this.UpdateToggleLabel(this.desktopManagerActive);
+ }
+
+ Debug.WriteLine($"Switching desktop manager active mode to '{this.desktopManagerActive}'");
+ Console.Beep();
+ }
+
+ private bool IsToggleCombinationPressed()
+ {
+ if (DateTime.Now - this.LastToggleTime < this.ToggleCooldown)
+ {
+ return false;
+ }
+
+ foreach (string button in this.managerConfiguration.ToggleManagerCombination)
+ {
+ if (button == ManagerConfiguration.BUTTON_A && !this.xinputDevice.Buttons.A.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_B && !this.xinputDevice.Buttons.B.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_X && !this.xinputDevice.Buttons.X.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_Y && !this.xinputDevice.Buttons.Y.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_LB && !this.xinputDevice.Buttons.LB.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_RB && !this.xinputDevice.Buttons.RB.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_LS && !this.xinputDevice.Buttons.LS.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_RS && !this.xinputDevice.Buttons.RS.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_START && !this.xinputDevice.Buttons.Start.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_BACK && !this.xinputDevice.Buttons.Back.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_DPAD_UP && !this.xinputDevice.Buttons.DPadUp.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_DPAD_DOWN && !this.xinputDevice.Buttons.DPadDown.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_DPAD_LEFT && !this.xinputDevice.Buttons.DPadLeft.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_DPAD_RIGHT && !this.xinputDevice.Buttons.DPadRight.IsPressed)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_LT && this.xinputDevice.LeftTrigger.Value < this.managerConfiguration.TriggerThreshold)
+ return false;
+ else if (button == ManagerConfiguration.BUTTON_RT && this.xinputDevice.RightTrigger.Value < this.managerConfiguration.TriggerThreshold)
+ return false;
+ }
+
+ Debug.WriteLine("Toggle Combination pressed.");
+
+ return true;
+ }
+
+ private void HandleLeftJoystickUpdated(object sender, EventArgs e)
+ {
+ if (!this.desktopManagerActive || !this.xinputDevice.LeftJoystick.IsPushed) { return; }
+
+ if (this.xinputDevice.LeftJoystick.X > this.managerConfiguration.LeftStickThreshold || this.xinputDevice.LeftJoystick.X < -this.managerConfiguration.LeftStickThreshold
+ || this.xinputDevice.LeftJoystick.Y > this.managerConfiguration.LeftStickThreshold || this.xinputDevice.LeftJoystick.Y < -this.managerConfiguration.LeftStickThreshold)
+ {
+ Debug.WriteLine($"Left Stick Move => Moving Mouse...");
+ this.inputSimulator.Mouse.MoveMouseBy(
+ (int)(this.xinputDevice.LeftJoystick.X * this.managerConfiguration.MaxCursorMovementSpeed),
+ (int)(-this.xinputDevice.LeftJoystick.Y * this.managerConfiguration.MaxCursorMovementSpeed)
+ );
+ }
+ }
+
+ private void HandleRightJoystickUpdated(object sender, EventArgs e)
+ {
+ if (!this.desktopManagerActive || !this.xinputDevice.RightJoystick.IsPushed) { return; }
+
+ if (this.xinputDevice.RightJoystick.Y > this.managerConfiguration.RightStickThreshold)
+ {
+ Debug.WriteLine("Right Stick UP => Sending volume up...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VOLUME_UP);
+ }
+ else if (this.xinputDevice.RightJoystick.Y < -this.managerConfiguration.RightStickThreshold)
+ {
+ Debug.WriteLine("Right Stick DOWN => Sending volume down...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VOLUME_DOWN);
+ }
+ else if (this.xinputDevice.RightJoystick.X < -this.managerConfiguration.RightStickThreshold)
+ {
+ Debug.WriteLine("Right Stick LEFT => Sending prev song command...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.MEDIA_PREV_TRACK);
+ }
+ else if (this.xinputDevice.RightJoystick.X > this.managerConfiguration.RightStickThreshold)
+ {
+ Debug.WriteLine("Right Stick RIGHT => Sending next song command...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.MEDIA_NEXT_TRACK);
+ }
+ }
+
+ private void HandleLeftTriggerMove(object? sender, EventArgs e)
+ {
+ if (!this.desktopManagerActive) { return; }
+
+ if (this.IsToggleCombinationPressed())
+ {
+ this.ToggleDesktopManager();
+ return;
+ }
+
+ if (this.xinputDevice.LeftTrigger.Value >= 1.0)
+ {
+ Debug.WriteLine("LT => Sending Windows Key...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.LWIN);
+ }
+ }
+
+ private void HandleRightTriggerMove(object? sender, EventArgs e)
+ {
+ if (!this.desktopManagerActive) { return; }
+
+ if (this.IsToggleCombinationPressed())
+ {
+ this.ToggleDesktopManager();
+ return;
+ }
+
+ if (this.xinputDevice.RightTrigger.Value >= this.managerConfiguration.TriggerThreshold)
+ {
+ Debug.WriteLine("RT => Minimizing all apps...");
+ this.inputSimulator.Keyboard.ModifiedKeyStroke(new VirtualKeyCode[] { VirtualKeyCode.LWIN }, VirtualKeyCode.VK_D);
+ }
+ }
+
+ private void HandleXinputButtonPressed(object sender, DigitalButtonEventArgs e)
+ {
+ Debug.WriteLine($"Button '{e.Button.Button}' pressed.");
+
+ if (this.IsToggleCombinationPressed())
+ {
+ this.ToggleDesktopManager();
+ return;
+ }
+
+ if (!this.desktopManagerActive) { return; }
+
+ if (e.Button.Button == XButtons.A)
+ {
+ Debug.WriteLine("A => Sending left click input...");
+ this.inputSimulator.Mouse.LeftButtonClick();
+ }
+ else if (e.Button.Button == XButtons.X)
+ {
+ Debug.WriteLine("X => Sending right click input...");
+ this.inputSimulator.Mouse.RightButtonClick();
+ }
+ else if (e.Button.Button == XButtons.B)
+ {
+ Debug.WriteLine("B => Sending ESC...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.ESCAPE);
+ }
+ else if (e.Button.Button == XButtons.Y)
+ {
+ Debug.WriteLine("Y => Opening Task Manager (CTRL+SHIFT+ESC)...");
+ this.inputSimulator.Keyboard.ModifiedKeyStroke(new VirtualKeyCode[] { VirtualKeyCode.LCONTROL, VirtualKeyCode.LSHIFT }, VirtualKeyCode.ESCAPE);
+ }
+ else if (e.Button.Button == XButtons.Start)
+ {
+ Debug.WriteLine("START => Sending ENTER...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.ACCEPT);
+ }
+ else if (e.Button.Button == XButtons.Back)
+ {
+ Debug.WriteLine("SELECT => Closing Window (ALT+F4)...");
+ this.inputSimulator.Keyboard.ModifiedKeyStroke(VirtualKeyCode.LMENU, VirtualKeyCode.F4);
+ }
+ else if (e.Button.Button == XButtons.DPadUp)
+ {
+ Debug.WriteLine("DPAD UP => Sending UP Arrow...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.UP);
+ }
+ else if (e.Button.Button == XButtons.DPadDown)
+ {
+ Debug.WriteLine("DPAD DOWN => Sending DOWN Arrow...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.DOWN);
+ }
+ else if (e.Button.Button == XButtons.DPadLeft)
+ {
+ Debug.WriteLine("DPAD LEFT => Sending LEFT Arrow...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.LEFT);
+ }
+ else if (e.Button.Button == XButtons.DPadRight)
+ {
+ Debug.WriteLine("DPAD RIGHT => Sending RIGHT Arrow...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.RIGHT);
+ }
+ else if (e.Button.Button == XButtons.LS)
+ {
+ Debug.WriteLine("LS => Toggling OnScreen Keyboard...");
+ this.inputSimulator.Keyboard.ModifiedKeyStroke(new VirtualKeyCode[] { VirtualKeyCode.LCONTROL, VirtualKeyCode.LWIN }, VirtualKeyCode.VK_O);
+ }
+ else if (e.Button.Button == XButtons.RS)
+ {
+ Debug.WriteLine("RS => Sending Mute command...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.VOLUME_MUTE);
+ }
+
+ else if (e.Button.Button == XButtons.RB)
+ {
+ if (this.xinputDevice.Buttons.LB.IsPressed == true)
+ {
+ Debug.WriteLine("LB => Holding Alt...");
+ this.inputSimulator.Keyboard.KeyDown(VirtualKeyCode.MENU);
+ }
+ else
+ {
+ this.inputSimulator.Keyboard.KeyUp(VirtualKeyCode.MENU);
+ }
+
+ Debug.WriteLine("RB => Sending Tab...");
+ this.inputSimulator.Keyboard.KeyPress(VirtualKeyCode.TAB);
+ }
}
- private void UpdateToggleLabel(bool mouseModeOn)
+ private void UpdateToggleLabel(bool newState)
{
- this.toggleLabel.Text = string.Format(TOGGLE_LABEL_TEXT_FORMAT, mouseModeOn ? "Off" : "On");
+ this.toggleLabel.Text = string.Format(TOGGLE_LABEL_TEXT_FORMAT, newState ? "Off" : "On");
}
}
}
diff --git a/XinputWindowsManager/WindowsMouseCursor.cs b/XinputWindowsManager/WindowsMouseCursor.cs
deleted file mode 100644
index 2c8c0e7..0000000
--- a/XinputWindowsManager/WindowsMouseCursor.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using System;
-using System.Drawing;
-using System.Runtime.InteropServices;
-
-namespace XinputWindowsManager
-{
- public class WindowsMouseCursor
- {
- [DllImport("user32.dll")]
- private static extern bool GetCursorPos(out Point lpPoint);
-
- [DllImport("user32")]
- private static extern int SetCursorPos(int x, int y);
-
- [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
- public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo);
- private const uint MOUSEEVENTF_LEFTDOWN = 0x02;
- private const uint MOUSEEVENTF_LEFTUP = 0x04;
- private const uint MOUSEEVENTF_RIGHTDOWN = 0x08;
- private const uint MOUSEEVENTF_RIGHTUP = 0x10;
-
- public static Point GetPosition()
- {
- Point position = default;
- GetCursorPos(out position);
-
- return position;
- }
-
- public static void SetPosition(int x, int y)
- {
- SetCursorPos(x, y);
- }
-
- public static void SendLeftClick()
- {
- Point p = GetPosition();
- mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)p.X, (uint)p.Y, 0, (UIntPtr)0);
- }
-
- public static void SendRightClick()
- {
- Point p = GetPosition();
- mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, (uint)p.X, (uint)p.Y, 0, (UIntPtr)0);
- }
- }
-}
diff --git a/XinputWindowsManager/XinputController.cs b/XinputWindowsManager/XinputController.cs
deleted file mode 100644
index f05e78d..0000000
--- a/XinputWindowsManager/XinputController.cs
+++ /dev/null
@@ -1,182 +0,0 @@
-using SharpDX.XInput;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace XinputWindowsManager
-{
- public class XinputController : IDisposable
- {
- private const double MAX_TRIGGER_VALUE = 255.0;
-
- private const double MAX_AXIS_VALUE = 32767.0;
-
- private Controller gamepad;
-
- private State gamepadState;
-
- private IDictionary buttonStates;
-
- private CancellationTokenSource poolingCts;
- public XinputController(UserIndex gamepadIndex = UserIndex.One)
- {
- this.gamepad = new Controller(gamepadIndex);
- this.buttonStates = new Dictionary();
-
- this.buttonStates.Add(GamepadButtonFlags.Start, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.Back, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.A, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.B, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.X, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.Y, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.LeftShoulder, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.RightShoulder, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.LeftThumb, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.RightThumb, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.DPadUp, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.DPadDown, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.DPadLeft, (false, false));
- this.buttonStates.Add(GamepadButtonFlags.DPadRight, (false, false));
- }
-
- public event EventHandler OnButtonPressed;
-
- public event EventHandler OnButtonReleased;
-
- public bool IsPooling
- {
- get
- {
- return this.poolingCts != null && !this.poolingCts.IsCancellationRequested;
- }
- }
-
- public void Pool()
- {
- this.gamepadState = gamepad.GetState();
-
- this.UpdateButtonState(GamepadButtonFlags.Back);
- this.UpdateButtonState(GamepadButtonFlags.Start);
- this.UpdateButtonState(GamepadButtonFlags.A);
- this.UpdateButtonState(GamepadButtonFlags.B);
- this.UpdateButtonState(GamepadButtonFlags.X);
- this.UpdateButtonState(GamepadButtonFlags.Y);
- this.UpdateButtonState(GamepadButtonFlags.LeftShoulder);
- this.UpdateButtonState(GamepadButtonFlags.RightShoulder);
- this.UpdateButtonState(GamepadButtonFlags.LeftThumb);
- this.UpdateButtonState(GamepadButtonFlags.RightThumb);
- this.UpdateButtonState(GamepadButtonFlags.DPadUp);
- this.UpdateButtonState(GamepadButtonFlags.DPadDown);
- this.UpdateButtonState(GamepadButtonFlags.DPadLeft);
- this.UpdateButtonState(GamepadButtonFlags.DPadRight);
- }
-
- public void StartPooling()
- {
- if (!this.IsPooling)
- {
- this.poolingCts = new CancellationTokenSource();
-
- Task.Factory.StartNew(() => {
- while (!this.poolingCts.IsCancellationRequested)
- this.Pool();
- });
- }
- }
-
- public void StopPooling()
- {
- if (this.IsPooling)
- {
- this.poolingCts.Cancel();
- this.poolingCts.Dispose();
- this.poolingCts = null;
- }
- }
-
- public void Dispose()
- {
- this.StopPooling();
- }
-
- public bool ButtonState(GamepadButtonFlags button)
- {
- return this.buttonStates[button].Item2;
- }
-
- public bool ButtonPressed(GamepadButtonFlags button)
- {
- return !this.buttonStates[button].Item1 && this.buttonStates[button].Item2;
- }
-
- public bool ButtonHolded(GamepadButtonFlags button)
- {
- return this.buttonStates[button].Item1 && this.buttonStates[button].Item2;
- }
-
- public bool ButtonReleased(GamepadButtonFlags button)
- {
- return this.buttonStates[button].Item1 && !this.buttonStates[button].Item2;
- }
-
- public bool ButtonCombinationPressed(params GamepadButtonFlags[] buttons)
- {
- bool atLeastOneJustPressed = false;
-
- foreach (GamepadButtonFlags button in buttons)
- {
- if (!this.ButtonState(button))
- return false;
-
- atLeastOneJustPressed = atLeastOneJustPressed || this.ButtonPressed(button);
- }
-
- return atLeastOneJustPressed;
- }
-
- public double LeftTriggerState()
- {
- return this.gamepadState.Gamepad.LeftTrigger / MAX_TRIGGER_VALUE;
- }
-
- public double RightTriggerState()
- {
- return this.gamepadState.Gamepad.RightTrigger / MAX_TRIGGER_VALUE;
- }
-
- public double LeftStickXState()
- {
- return this.gamepadState.Gamepad.LeftThumbX / MAX_AXIS_VALUE;
- }
-
- public double LeftStickYState()
- {
- return this.gamepadState.Gamepad.LeftThumbY / MAX_AXIS_VALUE;
- }
-
- public double RightStickXState()
- {
- return this.gamepadState.Gamepad.RightThumbX / MAX_AXIS_VALUE;
- }
-
- public double RightStickYState()
- {
- return this.gamepadState.Gamepad.RightThumbY / MAX_AXIS_VALUE;
- }
-
- private void UpdateButtonState(GamepadButtonFlags button)
- {
- this.buttonStates[button] = (this.buttonStates[button].Item2, this.gamepadState.Gamepad.Buttons.HasFlag(button));
-
- if (!this.buttonStates[button].Item1 && this.buttonStates[button].Item2)
- {
- this.OnButtonPressed?.Invoke(this, button);
- }
- else if (this.buttonStates[button].Item1 && !this.buttonStates[button].Item2)
- {
- this.OnButtonReleased?.Invoke(this, button);
- }
- }
- }
-}
diff --git a/XinputWindowsManager/XinputWindowsManager.csproj b/XinputWindowsManager/XinputWindowsManager.csproj
index d950e3d..293697e 100644
--- a/XinputWindowsManager/XinputWindowsManager.csproj
+++ b/XinputWindowsManager/XinputWindowsManager.csproj
@@ -1,34 +1,33 @@
-
-
-
- WinExe
- net6.0-windows
- true
- icon.ico
- 1.0.0.0
- 1.0.0.0
- 1.0.0
-
-
-
-
-
-
-
-
-
-
- True
- True
- Resources.resx
-
-
-
-
-
- ResXFileCodeGenerator
- Resources.Designer.cs
-
-
-
-
+
+
+ WinExe
+ net8.0-windows7.0
+ enable
+ enable
+ true
+ true
+ icon.ico
+ 2.0.0.0
+ 1.0.0.0
+ 2.0.0
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
\ No newline at end of file