Skip to content

ThallesP/x360w-userspace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

x360w-userspace

Userspace Rust prototype for reading an Xbox 360 Wireless Receiver on macOS via libusb.

The default mode is lower-level: claim a receiver interface, read interrupt packets, and decode wireless Xbox 360 controller reports. It can also send simple receiver output commands such as LED selection and rumble, and it has an optional virtual HID experiment for macOS.

This is slop-coded for one machine and one immediate use case. Treat it as a hacky prototype, not a polished driver or supported emulator input stack.

Build

Requirements:

  • macOS
  • Rust stable with Cargo
  • An Xbox 360 Wireless Receiver visible as USB device 045e:0291

Build and test:

cargo build
cargo test

Run diagnostics:

cargo run -- --doctor
cargo run -- --list

Build a release binary:

cargo build --release
./target/release/x360w-userspace --doctor

Device

The expected receiver is Microsoft 045e:0291. The same protocol is also used by some compatible receiver IDs such as 045e:0719 and unofficial 045e:02a9, but the default target here is the device already seen on this machine:

Vendor ID:  0x045e
Product ID: 0x0291

Usage

cargo run -- --list
cargo run -- --doctor
cargo run -- --controller 0

Useful flags:

cargo run -- --controller 1
cargo run -- --product 0719 --controller 0
cargo run -- --controller 0 --max-packets 20
cargo run -- --controller 0 --no-led
cargo run -- --controller 0 --rumble-left 200 --rumble-right 80 --rumble-ms 500 --rumble-only
cargo run -- --controller 0 --virtual-hid
cargo run -- --controller 0 --keyboard
cargo run -- --controller 0 --dolphin
cargo run -- --controller 0 --rhythm-heaven

--doctor checks the two separate requirements:

  1. The receiver must be visible to libusb. On macOS this can fail if the process is running inside a restricted sandbox.
  2. A virtual gamepad must be creatable. Without that, packet decoding still works, but games will not see a normal controller device.

Controller slot mapping follows xboxdrv notes:

controller 0 -> interface 0, endpoint 1 in/out
controller 1 -> interface 2, endpoint 3 in/out
controller 2 -> interface 4, endpoint 5 in/out
controller 3 -> interface 6, endpoint 7 in/out

Virtual HID

--virtual-hid attempts to create a generic macOS HID gamepad and forwards decoded controller reports into it. Normal packet decoding still works without this flag.

This path uses IOHIDUserDeviceCreateWithProperties, which Apple documents as requiring the com.apple.developer.hid.virtual.device entitlement. On macOS 15.6.1, ad-hoc signing with that entitlement is rejected by AMFI as a restricted entitlement, so unsigned local builds, ad-hoc signatures, root privileges, and most non-Apple-developer certificates cannot make this path appear as a normal gamepad to games.

To make games react as if this were a normal controller on current macOS, one of these has to exist:

  1. An Apple-provisioned build with com.apple.developer.hid.virtual.device.
  2. A signed DriverKit virtual HID/gamepad helper with the appropriate Apple-granted DriverKit HID virtual-device entitlement.
  3. An external hardware adapter that exposes the decoded state as a real USB/Bluetooth HID gamepad.

The packet decoder is already independent of that output layer: pressing A decodes as a, and the virtual HID mapper sends it as HID button 1 when the virtual device can be created.

Keyboard Bridge

--keyboard forwards decoded controller state into macOS keyboard events. This is not a real gamepad device, but it is the local path that can make games react without Apple's restricted virtual-HID entitlement.

Default mapping:

A -> Space
B -> Escape
X -> X
Y -> Y
LB/RB -> Q/E
LT/RT -> Z/C
Back/Start -> Tab/Return
D-pad -> arrow keys
left stick -> WASD
right stick -> IJKL
left/right stick clicks -> Control/Shift

Examples:

cargo run -- --controller 0 --keyboard
cargo run -- --controller 0 --keyboard --keyboard-map a=return
cargo run -- --controller 0 --keyboard --keyboard-map a=space --keyboard-map b=none
cargo run -- --controller 0 --keyboard --keyboard-deadzone 12000
cargo run -- --controller 0 --keyboard --keyboard-preset dolphin-gc
cargo run -- --controller 0 --keyboard --keyboard-preset dolphin-wii

On macOS, keyboard injection requires event-posting permission for the terminal/app running this process. --doctor checks that status, and --keyboard requests permission if it is missing.

Dolphin GameCube Mapper

--dolphin is a shortcut for the keyboard bridge with the dolphin-gc preset. It matches the GameCube controller bindings currently in Dolphin's GCPadNew.ini:

Xbox A -> Dolphin GameCube A (`X`)
Xbox B -> Dolphin GameCube B (`Z`)
Xbox X -> Dolphin GameCube X (`C`)
Xbox Y -> Dolphin GameCube Y (`S`)
Xbox LB/LT -> Dolphin GameCube L (`Q`)
Xbox RT -> Dolphin GameCube R (`W`)
Xbox RB -> Dolphin GameCube Z (`D`)
Xbox Start -> Dolphin Start (`Return`)
Xbox left stick -> Dolphin main stick (`Arrow Keys`)
Xbox right stick -> Dolphin C-stick (`IJKL`)
Xbox D-pad -> Dolphin D-pad (`T/G/F/H`)

Run this before or while Dolphin is focused:

cargo run -- --controller 0 --dolphin

Per-game tweaks still work:

cargo run -- --controller 0 --dolphin --keyboard-map rb=w

Dolphin Wii Mapper

--rhythm-heaven and --dolphin-wii use the Wii preset. It emits keyboard keys and mouse clicks for Wiimote A/B. For easier tapping in Rhythm Heaven Fever, Xbox B and Xbox X both map to Wiimote B.

Xbox A -> Wiimote A (`X` + left click)
Xbox B -> Wiimote B (`Z` + right click)
Xbox X -> Wiimote B (`Z` + right click)
Xbox Y -> Wiimote 2 (`2`)
Xbox Back/Start -> Wiimote -/+ (`Q`/`E`)
Xbox Guide -> Wiimote Home (`Return`)
Xbox left stick or D-pad -> Wiimote D-pad (`Arrow Keys`)

Run this for Rhythm Heaven Fever:

cargo run -- --controller 0 --rhythm-heaven

Rumble

--rumble-left and --rumble-right send the Xbox 360 wireless rumble command through the selected controller endpoint. Values are bytes from 0 to 255, and can be decimal or hex (0xff). The left motor is the large low-frequency motor and the right motor is the small high-frequency motor.

Use --rumble-ms to pulse for a duration and then send a stop command. Use --rumble-only to exit after the rumble command instead of entering the packet read loop.

macOS Notes

If claiming the interface fails, another driver may already own the device. The old 360Controller kernel extension or DriverKit components can conflict with libusb access.

If the read loop prints an interrupt I/O warning, the tool clears the interrupt endpoint and keeps running. This handles transient receiver/libusb hiccups without dropping the mapper process. If --list cannot see 045e:0291 even though macOS System Information shows it, unplug and replug the receiver, then run:

cargo run -- --list

The virtual HID layer is optional because the receiver packet decoder is useful on its own, and because the virtual gamepad path depends on Apple entitlement/signing policy.

Contributions

I am not accepting contributions for this repository. Pull requests are not wanted and will be closed automatically.

About

Slop-coded macOS userspace Xbox 360 Wireless Receiver prototype. Not accepting contributions.

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages