Oakrey.Joysticks
3.0.0
dotnet add package Oakrey.Joysticks --version 3.0.0
NuGet\Install-Package Oakrey.Joysticks -Version 3.0.0
<PackageReference Include="Oakrey.Joysticks" Version="3.0.0" />
<PackageVersion Include="Oakrey.Joysticks" Version="3.0.0" />
<PackageReference Include="Oakrey.Joysticks" />
paket add Oakrey.Joysticks --version 3.0.0
#r "nuget: Oakrey.Joysticks, 3.0.0"
#:package Oakrey.Joysticks@3.0.0
#addin nuget:?package=Oakrey.Joysticks&version=3.0.0
#tool nuget:?package=Oakrey.Joysticks&version=3.0.0
Oakrey.Joysticks
Overview
Oakrey.Joysticks is a .NET library for joystick and game controller input handling, built on Vortice.DirectInput and System.Reactive. It exposes three levels of abstraction over raw DirectInput polling, from individual buffered update events up to typed, grouped observable channels for axes, buttons, sliders, and point-of-view controllers.
Main Features
- Three abstraction levels
Joystick- low-level, real-timeIObservable<JoystickUpdate>stream backed by a background polling thread.ColdJoystick- snapshot-basedIObservable<JoystickOffsetDictionary>that always replays the latest full state to new subscribers.GroupedJoystick- high-level typed observables split intoAxes,Buttons,Sliders, andPointOfViewchannels with convenient ratio properties.
- Device enumeration - extension methods on
IDirectInput8to list attached gamepads, joysticks, keyboards, and mice. - Update classification -
JoystickUpdateExtensionshelpers:IsAxis(),IsButton(),IsRotation(),IsSlider(),IsPointOfViewController(). - Axis ratios - all axis and rotation values are normalised to
[0.0, 1.0]viaXRatio,YRatio, etc. (raw range 0-65535). - Safe resource cleanup - all classes implement
IDisposable; background threads and subscriptions are cancelled and joined on dispose. - Extensible status model -
IStatusinterface andObservableStatus<T>base allow custom grouped status types.
Architecture
classDiagram
direction LR
class Joystick {
+IObservable~JoystickUpdate~
+DirectInput : IDirectInput8
+Joystick(guid)
+Dispose()
}
class ColdJoystick {
+IObservable~JoystickOffsetDictionary~
-offsets : JoystickOffsetDictionary
+ColdJoystick(guid)
+Dispose()
}
class GroupedJoystick {
+Axes : IObservable~AxesStatus~
+Buttons : IObservable~ButtonsStatus~
+Sliders : IObservable~SlidersStatus~
+PointOfView : IObservable~PointOfViewStatus~
+GroupedJoystick(guid)
+Dispose()
}
class AxesStatus {
+X, Y, Z : int
+XRatio, YRatio, ZRatio : double
+RotationX/Y/Z + Ratios
}
class ButtonsStatus {
+Buttons : string
}
class SlidersStatus {
+Slider0, Slider1 : int
}
class PointOfViewStatus {
+PointOfView0..3Degrees : double
}
Joystick <-- ColdJoystick : wraps
Joystick <-- GroupedJoystick : wraps
GroupedJoystick --> AxesStatus
GroupedJoystick --> ButtonsStatus
GroupedJoystick --> SlidersStatus
GroupedJoystick --> PointOfViewStatus
Project structure
Joystick/
Joystick.cs # Raw IObservable<JoystickUpdate>
JoystickUpdateExtensions.cs # IsAxis / IsButton / IsRotation / IsSlider / IsPointOfViewController
DirectInputExtensions.cs # Enumeration and device creation helpers
Cold/
ColdJoystick.cs # Snapshot observable (JoystickOffsetDictionary)
JoystickOffsetDictionary.cs # Dictionary<string, int> alias
Grouped/
GroupedJoystick.cs # Typed observable channels
AxesStatus.cs # X/Y/Z + rotation axes with ratio properties
ButtonsStatus.cs # Up to 128 digital buttons
SlidersStatus.cs # Two slider channels
PointOfViewStatus.cs # Up to 4 POV hat switches in degrees
ObservableStatus.cs # ReplaySubject-backed base for status classes
IStatus.cs # Update(JoystickUpdate) contract
Requirements
- .NET 10 (
net10.0-windows) Vortice.DirectInput3.8.3Vortice.Mathematics2.1.1System.Reactive6.1.0Oakrey.Log.Abstractions3.0.0- A physical or virtual DirectInput-compatible game controller
Installation
.NET CLI
dotnet add package Oakrey.Joysticks
Package Manager Console
Install-Package Oakrey.Joysticks
NuGet Package Manager
Search for Oakrey.Joysticks in Tools > NuGet Package Manager > Manage NuGet Packages for Solution and click Install.
Example Usage
Raw update stream (first attached device)
using Oakrey.Joysticks;
using Joystick joystick = new();
using IDisposable subscription = joystick.Subscribe(update =>
Console.WriteLine($"{update.Offset}: {update.Value}"));
Console.ReadKey();
Snapshot of all offsets (ColdJoystick)
using Oakrey.Joysticks.Cold;
using ColdJoystick joystick = new();
using IDisposable subscription = joystick.Subscribe(state =>
{
foreach ((string key, int value) in state)
{
Console.WriteLine($"{key}: {value}");
}
});
Console.ReadKey();
Grouped typed channels (GroupedJoystick)
using Oakrey.Joysticks.Grouped;
using GroupedJoystick joystick = new();
using IDisposable axes = joystick.Axes.Subscribe(s =>
Console.WriteLine($"X: {s.XRatio:0.00} Y: {s.YRatio:0.00} Z: {s.ZRatio:0.00}"));
using IDisposable buttons = joystick.Buttons.Subscribe(s =>
Console.WriteLine($"Buttons: {s.Buttons}"));
using IDisposable pov = joystick.PointOfView.Subscribe(s =>
Console.WriteLine($"POV0: {s.PointOfView0Degrees:0.00} deg"));
using IDisposable sliders = joystick.Sliders.Subscribe(s =>
Console.WriteLine($"S0: {s.Slider0} S1: {s.Slider1}"));
Console.ReadKey();
Select a specific device by GUID
IEnumerable<DeviceInstance> devices =
Joystick.DirectInput.EnumerateAllAttachedGameDevices();
DeviceInstance target = devices.First(d => d.InstanceName.Contains("Hotas"));
using GroupedJoystick joystick = new(target);
Development Notes
Joystickuses a dedicated backgroundThread(not aTask) for DirectInput polling because DirectInput is apartment-threaded.- All three classes use
ReplaySubject<T>(1), so a new subscriber immediately receives the last known state. - Axis raw values span 0-65535; the
*Ratioproperties divide by 65535.0 to give a normalised double. - If
Poll()orAcquire()fails (e.g. device disconnected), the background thread exits and the observable sequence terminates with an error. DirectInputExtensions.FirstDevice()selects the first enumerated gamepad or joystick; pass an explicitGuidorDeviceInstanceto target a specific controller.- DirectInput devices are acquired in non-exclusive background mode and are released automatically when polling ends.
Project Information
| Field | Value |
|---|---|
| Package ID | Oakrey.Joysticks |
| Version | 2.0.1 |
| Author | Oakrey |
| License | MIT |
| Target | net10.0-windows |
| Repository | https://dev.azure.com/oakrey/OpenPackages/_git/Drivers |
| Project URL | https://www.oakrey.cz/opkg_joysticks |
License
This project is licensed under the MIT License. See the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-windows7.0 is compatible. |
-
net10.0-windows7.0
- Oakrey.Log.Abstractions (>= 3.0.0)
- System.Reactive (>= 6.1.0)
- Vortice.DirectInput (>= 3.8.3)
- Vortice.Mathematics (>= 2.1.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0 | 112 | 7/20/2026 |