VisualPinball.NativeInput
0.0.6
dotnet add package VisualPinball.NativeInput --version 0.0.6
NuGet\Install-Package VisualPinball.NativeInput -Version 0.0.6
<PackageReference Include="VisualPinball.NativeInput" Version="0.0.6" />
<PackageVersion Include="VisualPinball.NativeInput" Version="0.0.6" />
<PackageReference Include="VisualPinball.NativeInput" />
paket add VisualPinball.NativeInput --version 0.0.6
#r "nuget: VisualPinball.NativeInput, 0.0.6"
#:package VisualPinball.NativeInput@0.0.6
#addin nuget:?package=VisualPinball.NativeInput&version=0.0.6
#tool nuget:?package=VisualPinball.NativeInput&version=0.0.6
VPE Native Input Polling
High-performance native input polling library for Visual Pinball Engine.
Purpose
Provides OS-level input polling to achieve sub-millisecond input latency by bypassing Unity's Input System buffer. The input polling runs on a dedicated thread at 500-2000 Hz and forwards events to the simulation thread via a callback.
Architecture
┌─────────────────────────────────────┐
│ OS Input Events (Keyboard/HID) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Native Input Polling Thread │
│ (500-2000 Hz) │
├─────────────────────────────────────┤
│ • GetAsyncKeyState (Windows) │
│ • X11 key polling (Linux) │
│ • CoreGraphics/AppKit (macOS) │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Lock-Free SPSC Ring Buffer │
└─────────────────────────────────────┘
↓
┌─────────────────────────────────────┐
│ Simulation Thread (1000 Hz) │
└─────────────────────────────────────┘
Building
Windows (Visual Studio 2022)
cd VisualPinball.NativeInput
mkdir build
cd build
cmake .. -G "Visual Studio 17 2022" -A x64
cmake --build . --config Release
Output: artifacts/win-x64/VisualPinball.NativeInput.dll
Windows (MinGW)
cd VisualPinball.NativeInput
mkdir build
cd build
cmake .. -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
cmake --build .
Linux
cd VisualPinball.NativeInput
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
Requires libx11-dev and libudev-dev. The app user needs read access to the controller's input devices.
macOS
cd VisualPinball.NativeInput
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make
Requires CMake 3.24 or newer and the macOS SDK.
CI / NuGet
GitHub Actions builds win-x64, win-x86, linux-x64, osx-x64, and osx-arm64 binaries and packs them into a single NuGet package.
Publishing happens automatically for tags matching v*.
Output directory override
By default, builds are written to artifacts/<rid>/.
You can override the output directory with:
-DNATIVEINPUT_OUTPUT_DIR=/your/path(preferred)-DVPE_NATIVEINPUT_OUTPUT_DIR=/your/path(legacy alias)
Usage from C#
using VisualPinball.Unity.Simulation;
// Initialize
NativeInputApi.VpeInputInit();
if (NativeInputApi.VpeInputGetProtocolVersion() != 2) {
throw new InvalidOperationException("Unsupported native input protocol.");
}
// Set bindings
var bindings = new[] {
new NativeInputApi.InputBinding {
Action = (int)NativeInputApi.InputAction.LeftFlipper,
BindingType = (int)NativeInputApi.BindingType.Keyboard,
KeyCode = (int)NativeInputApi.KeyCode.LShift
}
};
NativeInputApi.VpeInputSetBindings(bindings, bindings.Length);
// Start polling
NativeInputApi.VpeInputStartPolling(OnInputEvent, IntPtr.Zero, 500);
// Callback
[MonoPInvokeCallback(typeof(NativeInputApi.InputEventCallback))]
static void OnInputEvent(ref NativeInputApi.InputEvent evt, IntPtr userData) {
Console.WriteLine($"Input: {evt.Action} = {evt.Value} @ {evt.TimestampUsec}μs");
}
// Stop polling
NativeInputApi.VpeInputStopPolling();
// Shutdown
NativeInputApi.VpeInputShutdown();
Open Pinball Device
NativeInput recognizes Open Pinball Device 1.x HID collections by their report signature on Windows, Linux, and macOS. The native input thread reads the reports and exposes named X/Y acceleration and velocity axes and the reported plunger fields. Preassigned OPD buttons that match existing VPE actions work alongside keyboard bindings, including physical tilt and digital nudges. An action stays pressed while any connected source holds it.
Pinscape Pico can expose both OPD and a joystick. VPE's cabinet setup prefers OPD and lets a player explicitly choose either collection. On Linux and macOS, OPD mapping identities include the physical USB port so identical boards can be mapped independently. Pico acceleration and velocity ranges depend on its firmware configuration.
On Linux, run VPE with an X11 display. The local user needs read access to the Pico's hidraw node for input and read/write access to its USB device node for the signature request. On a systemd-based desktop, these udev rules grant the active local user access to a Pico using the default IDs. Save them as /etc/udev/rules.d/70-pinscape-pico.rules, reload the rules, and reconnect the board. Use the configured VID and PID if the firmware overrides them.
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="eaeb", TAG+="uaccess"
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1209", ATTR{idProduct}=="eaeb", TAG+="uaccess"
Build the report replay tests with -DNATIVEINPUT_BUILD_TESTS=ON, then run ctest --test-dir build -C Release --output-on-failure from this repository.
Platform Status
| Platform | Status | API Used |
|---|---|---|
| Windows | Keyboard, joystick and gamepad axes, Open Pinball Device | GetAsyncKeyState, Windows HID, SDL |
| Linux | Keyboard, joystick and gamepad axes, Open Pinball Device | X11, SDL, hidraw, usbfs |
| macOS | Keyboard, joystick and gamepad axes, Open Pinball Device | CoreGraphics, SDL, IOKit |
SDL is linked into the plugin and only its joystick and gamepad subsystem is started. The native polling thread samples all controller axes. Recognized gamepads expose named stick and trigger axes, and generic joysticks expose raw axes for manual nudge mapping. Windows keeps the existing HID axes and their saved IDs. If a Windows gamepad also exposes HID joystick axes, its standardized axes appear under the separate (Gamepad) entry.
Some Nintendo controllers change their report format when SDL opens them. On Windows, use the standardized entry if a saved raw HID mapping for one of those controllers stops receiving samples.
Controller mappings use serial numbers and physical paths where available. When the platform exposes identical devices without a physical identity, the mappings use slots for that model. Recheck those slots after changing the connection order of identical controllers.
The Player's Nudge settings can bind joystick, gamepad, and generic OPD controls to left, right, and forward nudge, Tilt, and Slam Tilt. POV directions are exposed as buttons. Analog axes can activate an action above or below a threshold. Each action accepts alternatives, including combos whose controls must be held together. The native polling thread evaluates bindings and records presses for capture. Bindings follow stable device IDs across polling sessions and supplement keyboard and predefined OPD buttons. Tilt uses the table's physical Tilt Bob routing.
Performance
- Latency: <0.1ms from physical input to event callback
- CPU Usage: 5-10% of one core (mostly sleeping)
- Overhead: ~10-20ns per P/Invoke call
- Precision: Monotonic microsecond timestamps across platforms
Future Enhancements
- Controller buttons: Configurable bindings for other gameplay actions
- Force feedback: Haptics for nudge, tilt, button feedback
- Configuration: JSON-based key mapping files
- Profiles: Per-table input profiles
License
GPLv3+ (same as VPE)
SDL is distributed under the zlib license.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on VisualPinball.NativeInput:
| Repository | Stars |
|---|---|
|
freezy/VisualPinball.Engine
:video_game: Visual Pinball Engine for Unity
|