Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions src/DemoApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,6 @@ void ControllerOnValueChanged(object? sender, ControlEventArgs args)
controller = null;
};

manager.ErrorOccurred += (_, e) =>
{
Console.WriteLine(e.Exception.ToString());
};

var cancellationToken = new CancellationTokenSource();
_ = Task.Factory.StartNew(() =>
{
Expand Down
6 changes: 6 additions & 0 deletions src/Joypad/ControllerEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace OldBit.Joypad;

internal class ControllerEventArgs(JoypadController controller) : EventArgs
{
public JoypadController Controller { get; } = controller;
}
2 changes: 1 addition & 1 deletion src/Joypad/Controls/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public abstract class Control(ControlType controlType)
{
public ControlType ControlType { get; } = controlType;

public int Id { get; protected set; }
public int Id { get; protected init; }

public string Name { get; protected init; } = string.Empty;

Expand Down
6 changes: 0 additions & 6 deletions src/Joypad/ErrorEventArgs.cs

This file was deleted.

1 change: 0 additions & 1 deletion src/Joypad/Joypad.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

<ItemGroup>
<Folder Include="Platforms\Linux\" />
<Folder Include="Platforms\Windows\" />
</ItemGroup>

</Project>
16 changes: 15 additions & 1 deletion src/Joypad/JoypadController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,17 @@ internal JoypadController()
{
}

internal void Update(Control control)
internal void Update()
{
UpdateState();

foreach (var control in Controls)
{
ProcessControl(control);
}
}

private void ProcessControl(Control control)
{
if (!IsConnected)
{
Expand All @@ -61,12 +71,16 @@ public bool TryGetControl(int controlId, [NotNullWhen(true)] out Control? contro

internal void Initialize()
{
UpdateState();

foreach (var control in _controls)
{
control.Value = GetValue(control);
}
}

protected virtual void UpdateState() { }

protected abstract int? GetValue(Control control);

internal void AddControl(Control control)
Expand Down
70 changes: 29 additions & 41 deletions src/Joypad/JoypadManager.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;
using OldBit.Joypad.Platforms;
using OldBit.Joypad.Platforms.MacOS;
using OldBit.Joypad.Platforms.Windows;

namespace OldBit.Joypad;

Expand All @@ -16,26 +16,50 @@ public sealed class JoypadManager : IDisposable

public event EventHandler<JoypadControllerEventArgs>? ControllerConnected;
public event EventHandler<JoypadControllerEventArgs>? ControllerDisconnected;
public event EventHandler<ErrorEventArgs>? ErrorOccurred;

public JoypadManager()
{
if (OperatingSystem.IsMacOS())
{
_deviceManager = CreateMacOSDeviceManager();
_deviceManager = new HidDeviceManager();
}
else if (OperatingSystem.IsWindows())
{
// TODO: Implement Windows device manager
_deviceManager = new XInputDeviceManager();
}
else if (OperatingSystem.IsLinux())
{
// TODO: Implement Linux device manager
throw new NotImplementedException();
}
else
{
throw new PlatformNotSupportedException($"The {Environment.OSVersion.VersionString} platform is not supported.");
}

_deviceManager.ControllerAdded += (_, e) =>
{
_controllers.Add(e.Controller);
e.Controller.IsConnected = true;
e.Controller.Initialize();

ControllerConnected?.Invoke(this, new JoypadControllerEventArgs(e.Controller));
};

_deviceManager.ControllerRemoved += (_, e) =>
{
var existingController = Controllers.FirstOrDefault(c => c.Id == e.Controller.Id);

if (existingController == null)
{
return;
}

_controllers.Remove(existingController);

e.Controller.IsConnected = false;
ControllerDisconnected?.Invoke(this, new JoypadControllerEventArgs(e.Controller));
};
}

public void Start()
Expand Down Expand Up @@ -69,10 +93,7 @@ public void Update(Guid controllerId)
return;
}

foreach (var control in controller.Controls)
{
controller.Update(control);
}
controller.Update();
}

public bool TryGetController(Guid controllerId, [NotNullWhen(true)] out JoypadController? controller)
Expand All @@ -82,39 +103,6 @@ public bool TryGetController(Guid controllerId, [NotNullWhen(true)] out JoypadCo
return controller != null;
}

[SupportedOSPlatform("macos")]
private HidDeviceManager CreateMacOSDeviceManager()
{
var deviceManager = new HidDeviceManager();

deviceManager.ControllerAdded += (_, e) =>
{
_controllers.Add(e.Controller);

e.Controller.IsConnected = true;
ControllerConnected?.Invoke(this, new JoypadControllerEventArgs(e.Controller));
};

deviceManager.ControllerRemoved += (_, e) =>
{
var existingController = Controllers.FirstOrDefault(c => c.Id == e.Controller.Id);

if (existingController == null)
{
return;
}

_controllers.Remove(existingController);

e.Controller.IsConnected = false;
ControllerDisconnected?.Invoke(this, new JoypadControllerEventArgs(e.Controller));
};

deviceManager.ErrorOccurred += (sender, e) => ErrorOccurred?.Invoke(sender, e);

return deviceManager;
}

public void Dispose()
{
_deviceManager?.Dispose();
Expand Down
4 changes: 4 additions & 0 deletions src/Joypad/Platforms/IDeviceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ namespace OldBit.Joypad.Platforms;

internal interface IDeviceManager : IDisposable
{
event EventHandler<ControllerEventArgs>? ControllerAdded;

event EventHandler<ControllerEventArgs>? ControllerRemoved;

void StartListener();

void StopListener();
Expand Down
12 changes: 3 additions & 9 deletions src/Joypad/Platforms/MacOS/HidDeviceManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@

namespace OldBit.Joypad.Platforms.MacOS;

internal class ControllerEventArgs(HidController controller) : EventArgs
{
public HidController Controller { get; } = controller;
}

[SupportedOSPlatform("macos")]
internal class HidDeviceManager : IDeviceManager
{
Expand All @@ -19,9 +14,8 @@
private GCHandle _gch;
private readonly Thread _runLoopThread;

internal event EventHandler<ControllerEventArgs>? ControllerAdded;
internal event EventHandler<ControllerEventArgs>? ControllerRemoved;
internal event EventHandler<ErrorEventArgs>? ErrorOccurred;
public event EventHandler<ControllerEventArgs>? ControllerAdded;
public event EventHandler<ControllerEventArgs>? ControllerRemoved;

internal HidDeviceManager()
{
Expand Down Expand Up @@ -63,9 +57,9 @@
{
RunLoopRun();
}
catch (Exception ex)

Check warning on line 60 in src/Joypad/Platforms/MacOS/HidDeviceManager.cs

View workflow job for this annotation

GitHub Actions / build

The variable 'ex' is declared but never used

Check warning on line 60 in src/Joypad/Platforms/MacOS/HidDeviceManager.cs

View workflow job for this annotation

GitHub Actions / build

The variable 'ex' is declared but never used
{
ErrorOccurred?.Invoke(this, new ErrorEventArgs(ex));
// TODO: Log exception
}
}

Expand Down
46 changes: 46 additions & 0 deletions src/Joypad/Platforms/Windows/Interop/XInput.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Runtime.InteropServices;
using System.Runtime.Versioning;

namespace OldBit.Joypad.Platforms.Windows.Interop;

[SupportedOSPlatform("windows")]
internal static partial class XInput
{
internal const int ErrorSuccess = 0x0000;
internal const int ErrorDeviceNotConnected = 0x048F;

internal const int XInputDevTypeGamepad = 0x0001;

internal const int XInputDevSubTypeUnknown = 0x00;
internal const int XInputDevSubTypeGamepad = 0x01;
internal const int XInputDevSubTypeWheel = 0x02;
internal const int XInputDevSubTypeArcadeStick = 0x03;
internal const int XInputDevSubTypeFlightStick = 0x04;
internal const int XInputDevSubTypeDancePad = 0x05;
internal const int XInputDevSubTypeGuitar = 0x06;
internal const int XInputDevSubTypeGuitarAlternate = 0x07;
internal const int XInputDevSubTypeDrumKit = 0x08;
internal const int XInputDevSubTypeGuitarBass = 0x0B;
internal const int XInputDevSubTypeArcadePad = 0x13;

internal const int XInputGamepadDPadUp = 0x0001;
internal const int XInputGamepadDPadDown = 0x0002;
internal const int XInputGamepadDPadLeft = 0x0004;
internal const int XInputGamepadDPadRight = 0x0008;
internal const int XInputGamepadStart = 0x0010;
internal const int XInputGamepadBack = 0x0020;
internal const int XInputGamepadLeftThumb = 0x0040;
internal const int XInputGamepadRightThumb = 0x0080;
internal const int XInputGamepadLeftShoulder = 0x0100;
internal const int XInputGamepadRightShoulder = 0x0200;
internal const int XInputGamepadA = 0x1000;
internal const int XInputGamepadB = 0x2000;
internal const int XInputGamepadX = 0x4000;
internal const int XInputGamepadY = 0x8000;

[LibraryImport("Xinput1_4.dll")]
internal static partial int XInputGetState(int userIndex, out XInputState state);

[LibraryImport("Xinput1_4.dll")]
internal static partial int XInputGetCapabilities(int userIndex, int flags, out XInputCapabilities capabilities);
}
17 changes: 17 additions & 0 deletions src/Joypad/Platforms/Windows/Interop/XInputCapabilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Runtime.InteropServices;

namespace OldBit.Joypad.Platforms.Windows.Interop;

[StructLayout(LayoutKind.Sequential)]
internal struct XInputCapabilities
{
internal byte Type;

internal byte SubType;

internal ushort Flags;

internal XInputGamepad Gamepad;

internal XInputVibration Vibration;
}
21 changes: 21 additions & 0 deletions src/Joypad/Platforms/Windows/Interop/XInputGamepad.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.Runtime.InteropServices;

namespace OldBit.Joypad.Platforms.Windows.Interop;

[StructLayout(LayoutKind.Sequential)]
internal struct XInputGamepad
{
internal ushort Buttons;

internal byte LeftTrigger;

internal byte RightTrigger;

internal short ThumbLX;

internal short ThumbLY;

internal short ThumbRX;

internal short ThumbRY;
}
11 changes: 11 additions & 0 deletions src/Joypad/Platforms/Windows/Interop/XInputState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Runtime.InteropServices;

namespace OldBit.Joypad.Platforms.Windows.Interop;

[StructLayout(LayoutKind.Sequential)]
internal struct XInputState
{
internal uint PacketNumber;

internal XInputGamepad Gamepad;
}
11 changes: 11 additions & 0 deletions src/Joypad/Platforms/Windows/Interop/XInputVibration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Runtime.InteropServices;

namespace OldBit.Joypad.Platforms.Windows.Interop;

[StructLayout(LayoutKind.Sequential)]
internal struct XInputVibration
{
internal ushort LeftMotorSpeed;

internal ushort RightMotorSpeed;
}
27 changes: 27 additions & 0 deletions src/Joypad/Platforms/Windows/XInputControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Runtime.Versioning;
using OldBit.Joypad.Controls;

namespace OldBit.Joypad.Platforms.Windows;

[SupportedOSPlatform("windows")]
internal class XInputControl : Control
{
private DirectionalPadDirection _direction;

private XInputControl(ControlType controlType, int inputId, string name, DirectionalPadDirection direction = DirectionalPadDirection.None) : base(controlType)
{
Name = name;
Id = inputId;

_direction = direction;
}

internal static XInputControl CreateButton(int inputId, string name) =>
new(ControlType.Button, inputId, name);

internal static XInputControl CreateThumbStick(int inputId, string name) =>
new(ControlType.ThumbStick, inputId, name);

internal static XInputControl CreateDirectionalPad(int inputId, string name, DirectionalPadDirection direction) =>
new(ControlType.ThumbStick, inputId, name, direction);
}
Loading
Loading