Skip to content

macOS BLE Gamepad Support: Xbox Controller not detected via gilrs #1

@amiable-dev

Description

@amiable-dev

Problem

Xbox Wireless Controller connected via Bluetooth Low Energy (BLE) is not detected by Conductor on macOS, even though the controller is connected and functional with macOS system.

Environment

  • Platform: macOS (Apple Silicon)
  • Controller: Xbox Wireless Controller (Product ID: 0x0B22, Vendor ID: 0x045E)
  • Connection: Bluetooth Low Energy (BLE)
  • Current Library: gilrs 0.10

Root Cause Analysis

Investigation Results

Controller is connected to macOS:

Xbox Wireless Controller:
    Address: 3C:FA:06:18:BD:A5
    Vendor ID: 0x045E (Microsoft)
    Product ID: 0x0B22
    Minor Type: Gamepad
    Services: BLE

Conductor code is correct:

  • ListDevices IPC command properly enumerates both MIDI and HID devices
  • HidDeviceManager::list_gamepads() calls gilrs correctly
  • Test confirms: Found 0 game controllers

gilrs doesn't detect BLE controllers on macOS:

  • gilrs uses IOKit framework which primarily supports USB HID devices
  • BLE controllers are exposed via different macOS APIs
  • macOS requires native GameController framework for BLE gamepad support

Test Evidence

cargo test --package conductor-daemon test_list_gamepads -- --nocapture
// Output: Found 0 game controllers

Proposed Solutions

Option 1: Dynamic Library Plugin Architecture ⭐ (Recommended)

Architecture:

conductor-daemon: 3MB (no gamepad libs)
├── plugins/
    ├── libconductor-backend-gilrs.dylib (~500KB)
    ├── libconductor-backend-sdl2.dylib (~2MB)  
    └── libconductor-backend-macos.dylib (~100KB)

Config:
[device]
gamepad_backend = "sdl2"  # Runtime selection

Implementation:

  • Define GamepadBackend trait with C ABI
  • Use libloading crate for dynamic library loading
  • Load backend based on config.toml setting
  • Users download only needed plugins

Pros:

  • ✅ Smallest core binary (3MB vs 5-8MB)
  • ✅ Runtime config selection (no recompile)
  • ✅ Download on-demand (install just needed backend)
  • ✅ Hot-swap backends (change config, reload)

Cons:

  • Distribution complexity (multiple files)
  • Version compatibility management
  • Security considerations (loading external code)

Effort: ~6-8 hours


Option 2: Separate Backend Binaries (Simpler)

Architecture:

conductor-daemon: 3MB (coordinator only)
conductor-backend-gilrs: 1MB (standalone binary)
conductor-backend-sdl2: 3MB (standalone binary)
conductor-backend-macos: 500KB (standalone binary)

Config:
[device]
gamepad_backend = "sdl2"

Implementation:

  • Each backend is a separate executable
  • Daemon spawns backend as subprocess
  • Communicate via stdin/stdout (JSON protocol)
  • Reuse existing IPC infrastructure

Pros:

  • ✅ Simple to implement (no FFI/ABI concerns)
  • ✅ Process isolation (backend crash safe)
  • ✅ Easy distribution (separate binaries)
  • ✅ Runtime selection via config
  • ✅ Can distribute via cargo install conductor-backend-sdl2
  • ✅ Fits existing architecture (already have IPC)

Cons:

  • Extra process overhead (~1-2ms latency)
  • Multiple binaries to distribute

Effort: ~4-6 hours


Option 3: Cargo Features (Current Approach)

Architecture:

[features]
default = ["gilrs-backend"]
gilrs-backend = ["gilrs"]
sdl2-backend = ["sdl2"]
macos-native = []  # macOS only

# User builds with:
cargo build --features sdl2-backend

Pros:

  • ✅ Smallest binary (only one backend compiled)
  • ✅ No runtime overhead
  • ✅ Simple implementation

Cons:

  • ❌ Requires recompile to switch backends
  • ❌ User must know to use correct feature
  • ❌ Larger binary if multiple features enabled

Effort: ~2-3 hours


Option 4: Immediate SDL2 Integration

Quick Fix: Replace gilrs with sdl2 globally

  • Works on macOS BLE controllers
  • Proven, battle-tested
  • ~2MB binary size increase

Pros:

  • ✅ Fastest path to working Xbox support (1 hour)
  • ✅ Works on all platforms

Cons:

  • ❌ Larger binary for everyone
  • ❌ No user choice

Effort: ~1 hour


Backend Comparison

Backend macOS BLE macOS USB Linux Windows Binary Size
gilrs +500KB
SDL2 +2MB
macOS GameController +100KB

Recommendation

Phase 1: Implement Option 2 (Separate Backend Binaries)

  • Best balance of flexibility, simplicity, and binary size
  • Fits existing IPC architecture
  • Process isolation provides safety
  • Easy to distribute and update

Phase 2: Add backend implementations

  1. conductor-backend-sdl2 (immediate - fixes macOS BLE)
  2. conductor-backend-macos (later - optimal macOS performance)
  3. Keep conductor-backend-gilrs (default for Linux/Windows)

Phase 3 (Optional): Convert to dynamic library plugins (Option 1) if needed

Implementation Checklist

  • Define GamepadBackend trait in conductor-core
  • Create conductor-backend-gilrs binary (extract existing code)
  • Create conductor-backend-sdl2 binary (new)
  • Add subprocess spawning to input_manager.rs
  • Implement JSON protocol for backend communication
  • Add gamepad_backend config option
  • Update documentation
  • Add macOS-specific testing guide
  • Create releases for all backends

References

Labels

enhancement, architecture, macos, gamepad, v3.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions