Play on macOS with a wired third-party Xbox One / Series controller (built and tested with the Turtle Beach REACT-R) by turning it into a keyboard and mouse.
No kernel extension, no driver install, no SIP changes: it's a small Python program that talks to the controller over USB from user space.
Wired Xbox One/Series controllers speak GIP, Microsoft's USB protocol, and show up as vendor-specific USB devices. macOS only supports Microsoft's own Xbox controllers, so third-party ones (Turtle Beach, PowerA, PDP, Hori…) are listed in System Information but ignored by games. The old 360Controller kext doesn't work on macOS 11 and later.
Ideally a driver would expose the controller as a virtual gamepad, but macOS
only allows that for apps holding an Apple-granted entitlement
(com.apple.developer.hid.virtual.device). TurtleMac works around it: it reads
the controller directly and sends keyboard and mouse events instead, using
per-game profiles you can edit.
- Powers on and reads any GIP controller via libusb (no driver needed)
- Per-game profiles in a commented TOML file
- Sticks as WASD/arrow keys, as an analog mouse (with dead zone and response curve) or as scrolling
- Key combinations (
cmd+tab,shift+w), mouse buttons, scroll wheel - Switch profile or pause from the controller, with rumble feedback
- Hot-plug: unplug and replug the controller at any time
- Test mode to see live input and raw USB packets
- macOS with Homebrew and Python 3.11+ (
brew install python) - Tested on a MacBook Air (Apple M5) running macOS 27 with a Turtle Beach REACT-R (
10f5:7018)
git clone https://github.com/dpugliese6/TurtleMac.git
cd TurtleMac
./setup.shsetup.sh installs libusb with Homebrew and creates a local .venv with
pyusb and
PyObjC (Quartz).
- Plug in the controller.
- Double-click
Play.commandin Finder, or run./Play.commandin Terminal. - The first time, macOS asks for the Accessibility permission, which is needed to simulate keyboard and mouse. Enable Terminal (or the app you launch it from, e.g. iTerm or VS Code) in System Settings → Privacy & Security → Accessibility, then quit and reopen that app and run TurtleMac again.
- Start your game and keep the Terminal window open in the background. Choose keyboard controls in the game. The game will still report "no controller", which is expected.
- Press
Ctrl+Cin Terminal to quit. All held keys are always released.
./Play.command # default profile
./Play.command --profile fps # a specific profile
./Play.command --test # show live input, no keys sent
./Play.command --test --raw # also print raw USB packets
./Play.command --list-keys # key and button names for config.tomlTo start it automatically, add Play.command to System Settings → General →
Login Items.
| Combination | Effect |
|---|---|
| Xbox + RB | next profile |
| Xbox + LB | previous profile |
| Xbox + Menu | pause / resume (to use the Mac normally) |
When switching profile the controller vibrates once for the 1st profile, twice for the 2nd, and so on.
Profiles live in config.toml. The included ones:
| Profile | For | Layout |
|---|---|---|
hollowknight (default) |
Hollow Knight | The game's Xbox layout mapped to its default keyboard bindings |
fps |
Shooters, Minecraft, action games | Left stick WASD, right stick mouse, RT/LT click |
arcade |
Platformers, emulators | Arrows + Z X C V |
desktop |
Using the Mac from the couch | Stick mouse, A/B click, right stick scroll |
A profile looks like this:
[profiles.mygame]
left_stick = "wasd" # wasd | arrows | mouse | scroll | none | { up = "w", ... }
right_stick = "mouse"
mouse_speed = 1400 # any [settings] option can be overridden per profile
[profiles.mygame.buttons]
A = "space"
RT = "mouse_left"
LS = "shift"
Y = "cmd+tab"Buttons: A B X Y LB RB LT RT LS RS VIEW MENU SHARE DPAD_UP DPAD_DOWN DPAD_LEFT DPAD_RIGHT.
The Xbox button is reserved for shortcuts. Key names refer to key positions on
a US keyboard. All options are documented in the comments of config.toml.
TurtleMac picks up any USB device with a GIP interface (class ff, subclass
47, protocol d0), so other wired Xbox One/Series controllers may work, but
only the REACT-R has been tested. Run ./Play.command --test: if the buttons
show up correctly, it works. Some controllers need extra start-up packets. The
Linux xpad
driver is the reference for those. Reports and pull requests are welcome.
- Works only with games that can be played with keyboard and mouse. Games that require a gamepad still won't see the controller.
- Sticks mapped to keys are digital (on/off). A stick mapped to the mouse is analog.
- Share and back paddles are untested. Check them with
--test --raw.
- "Waiting for the controller…": unplug and replug the cable, or try another port.
- "Cannot open the controller": another TurtleMac instance is probably running.
- Nothing happens in the game: check the Accessibility permission, and make sure the game's keyboard bindings match the profile.
- Mouse drifts: raise
stick_deadzone. - Mouse too slow or fast: change
mouse_speed.
- libusb opens the controller, sets its configuration and claims interface 0.
- TurtleMac sends the GIP power-on command (
05 20 <seq> 01 00) and acknowledges packets that request it, such as the Xbox button. - Input reports (
0x20) are decoded into buttons, triggers (10-bit) and sticks (16-bit). - A mapper turns state changes into Quartz
CGEvents (key, mouse, scroll) posted at the HID level. Sticks used as a mouse are sampled at 120 Hz and send relative deltas, so games that capture the cursor work too.
GIP protocol details come from the Linux kernel's xpad driver and the
xone project.
Not affiliated with or endorsed by Turtle Beach, Microsoft or Team Cherry. All trademarks belong to their respective owners.