Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 

Repository files navigation

gamepad-cli

Control macOS with a Nintendo Switch Pro Controller. Designed for Claude Code — navigate spaces, trigger dictation, and drive the terminal, all from your gamepad.

Quick Start

make && ./gamepad-cli

Requirements

  • macOS 14+ (Sonoma)
  • Accessibility permission: System Settings → Privacy & Security → Accessibility → enable your terminal
  • Nintendo Switch Pro Controller paired via Bluetooth

Usage

./gamepad-cli              # English (default)
./gamepad-cli --lang zh    # 中文
./gamepad-cli --lang en    # English
./gamepad-cli --quiet      # Suppress button logs
./gamepad-cli -q           # Same as --quiet

Language auto-detected from LANG:

LANG=zh_CN.UTF-8 ./gamepad-cli      # 中文
LANG=en_US.UTF-8 ./gamepad-cli      # English

Press Ctrl+C to quit.

Controller Layout (Switch Pro)

        ZL    L              R    ZR       ← Shoulders/Triggers
    ┌─────────────────────────────────┐
    │   ┌───┐             ┌───┐       │
    │   │十字│   HOME     │ X │       │  Face: X(top)  A(right)
    │   │D-pad│           │Y A│       │        Y(left)  B(bottom)
    │   └───┘             │ B │       │
    │                     └───┘       │
    │  L3(按下)          R3(按下)      │  Stick clicks
    │  [左摇杆]          [右摇杆]      │  Analog sticks
    └─────────────────────────────────┘
         -        +

GCController mapping: L = leftShoulder, R = rightShoulder, ZL = leftTrigger, ZR = rightTrigger.

Mappings

Base Layer

Control Action Implementation
A (B button) Enter Keyboard event
B (A button) Escape Keyboard event
X (X button) Interrupt (Ctrl+C) Keyboard event
Y (Y button) Paste (Cmd+V) Keyboard event
D-pad ↑ Arrow Up Keyboard event
D-pad ↓ Arrow Down Keyboard event
D-pad ← Arrow Left Keyboard event
D-pad → Arrow Right Keyboard event
ZL (left trigger) Voice dictation Fn+Option (hold to speak)
ZR (right trigger) Enter Keyboard event
L3 (left stick click) Left click Mouse event
R3 (right stick click) Right click Mouse event
+ (Menu) Type "/" (slash cmd) Keyboard event
- (Options) Type "/clear" Keyboard event
Left stick Scroll wheel Scroll event
Right stick Mouse cursor Mouse move event

L Layer (hold L / left shoulder)

Combo Action Key
L + D-pad ← Previous Tab Cmd+Shift+[
L + D-pad → Next Tab Cmd+Shift+]
L + D-pad ↑ Close Tab Cmd+W
L + D-pad ↓ New Tab Cmd+T
L + Y Copy Cmd+C
L + B Delete Delete
L + L3 Left Click Mouse
L + R3 Right Click Mouse
L + Left stick ←/→ Previous/Next App Cmd+Shift+Tab / Cmd+Tab

R Layer (hold R / right shoulder)

Combo Action Implementation
R + D-pad ↑ Mission Control /usr/bin/open -a "Mission Control"
R + D-pad ↓ App Exposé open -a "Mission Control" --args -frontmost
R + D-pad ← Previous Space CGS private API
R + D-pad → Next Space CGS private API

Voice Dictation (ZL / left trigger)

Hold ZL to start macOS dictation (Fn+Option), release to commit recognized text. This triggers the native macOS dictation service — a short microphone popover appears while speaking, then text is inserted on release.

The implementation uses kCGEventFlagMaskSecondaryFn (the Fn key) combined with Option, matching the physical keyboard shortcut for Mac dictation.

Space Switching (R + D-pad)

Space switching uses the private CGS (CoreGraphics Services / SkyLight) API rather than simulated keyboard events. macOS WindowServer filters synthetic Ctrl+arrow events for Mission Control gestures, so keyboard injection doesn't work.

The CGS API (CGSManagedDisplaySetCurrentSpace) is the same approach used by window managers like AeroSpace and yabai.

Mission Control and App Exposé are triggered via /usr/bin/open, which is the only reliable way to invoke these UIs programmatically on macOS 14+.

Build

make          # compile (auto code-signs for stable permissions)
make run      # compile and run
make clean    # remove binary

Builds with clang, links against GameController, CoreGraphics, Foundation, and ApplicationServices frameworks.

Technical Notes

  • No entitlement required: Uses GCController.shouldMonitorBackgroundEvents for background controller input — works without special signing.
  • Ad-hoc code signing: The Makefile runs codesign --sign - after compilation to give the binary a stable identity for TCC (Accessibility) permissions.
  • 60Hz polling: Analog sticks and triggers are polled at 60fps via NSTimer with radial deadzone processing (0.3 threshold).
  • Auto-repeat: Held buttons (D-pad arrows, L-layer D-pad, L-layer Delete) repeat with 400ms initial delay and 50ms repeat rate.

Troubleshooting

Keyboard/mouse events not working: Ensure your terminal has Accessibility permission. Open System Settings → Privacy & Security → Accessibility and add/enable your terminal app.

Voice dictation not working: Verify macOS dictation is enabled: System Settings → Keyboard → Dictation → toggle ON. The shortcut is Fn+Option (handled automatically by the tool).

Space switching not working: The CGS API requires no additional permissions beyond Accessibility. If spaces don't switch, verify Mission Control spaces are enabled: System Settings → Desktop & Dock → Mission Control.

Controller not detected: Ensure Bluetooth is on and the controller is paired. The tool auto-discovers wireless controllers on startup and handles hot-plug.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages