How The Vault Handles Controllers

This is the guide to the machinery underneath the "one pad drives everything" promise — how The Vault reads controllers, how it keeps a live picture of what's connected, and how it decides the right way to feed each pad into each emulator. It's the reference we keep current as controller support grows.

One sentence: almost every controller problem is a timing and detection problem, not a mapping problem — so the design is built around a single, always-on source of truth for what's connected, plus a clear rule for who owns input in each emulator.

1. The core question: who owns the input?

Before anything else, The Vault classifies each emulator + controller pairing by who actually delivers the button presses to the game. That single decision determines whether reacting to a controller being turned on mid-game is our job or the emulator's. There are four models:

Native

The emulator reads the pad itself, through SDL or macOS GameController. We write the config at launch, then step out of the loop. The emulator owns hotplug.

Keyboard-forward

We read the pad ourselves and synthesize keystrokes for the emulator. Unlocks remaps, combos and macros, and works with any pad. We own hotplug.

BLE → DSU bridge

For pads macOS can't expose, we run a Bluetooth bridge that serves a virtual DSU (cemuhook) controller the emulator reads. We own hotplug.

Real-HID native

The emulator reads a real HID device directly and continuously scans for it (e.g. a Wii Remote through a Dolphin Bar). The emulator owns hotplug.

ModelHow the game gets inputHotplug owner
NativeEmulator reads the pad via SDL / GameControllerEmulator
Keyboard-forwardThe Vault synthesizes key eventsThe Vault
BLE→DSUA bridge serves a virtual DSU padThe Vault
Real-HID nativeEmulator reads the real HID device directlyEmulator

How this maps to the two modes you toggle: Controller mode covers three of these models — Native, BLE→DSU, and Real-HID — while Keyboard mode is exactly the Keyboard-forward model.

Why it matters: where we own input (keyboard-forward, BLE→DSU), we can retarget a running game live — swap which pad is Player 1 on the fly. Where the emulator owns input (native, real-HID), our job is launch-time correctness — write the right config from an always-current snapshot — and we cannot reconfigure a game that's already running.

2. How we monitor controllers — the Controller Registry

Older builds re-derived "what's connected" independently in every subsystem — the launcher counted one way, the forwarders another, each bridge a third, each at its own moment. That is precisely where timing bugs breed. The current design replaces all of it with one always-on Controller Registry that runs from the moment The Vault launches.

  • The live source of truth. Input sources report into the registry, and it's the truth behind the monitor UI and the Wii / GameCube / Switch 2 presence-and-liveness picture. Consumers (launchers, forwarders, bridges, Showcase navigation) are being migrated onto it — some subscribe to its change events today, while others (like the launch path, which still counts pads directly) are still on their own detection.
  • Always current. Because it's live from launch, "who is Player 1 right now?" is answerable instantly at any moment, and a controller turned on late is a clean event, not a race.
  • Stable identity, never the name. A controller is identified by (transport, vendor ID, product ID) — never its display name, because several NSO pads all report as "mFi Controller."
  • Player slots by connection order. Controllers fill P1 … P4 in the order they come on. When one disconnects, the slots below it shift up so a lone survivor becomes Player 1 (see handoff, below).

Three kinds of source feed the registry, all normalized behind one interface:

  • macOS GameController — DualSense, Xbox, 8BitDo and other MFi pads (connect / disconnect notifications).
  • IOHIDManager — the Wii Remote (via the Mayflash Dolphin Bar), with rate-based liveness. (NSO HID pads are read over HID during gameplay but aren't wired into the registry yet.)
  • BLE bridge callbacks — the GameCube and Switch 2 Pro Bluetooth bridges report their pads as they connect and drop.
The Controller Monitor

Open Settings → Controllers — always available in every build — to watch the registry live: each player slot, the controller in it, and whether it's live or present-but-idle. Power a pad on or off and the slots re-shuffle in front of you — the fastest way to confirm what The Vault thinks is connected. (Developer builds also expose the same view as a standalone floating window via ⌘ ⌥ ⇧ C.)

The live Connected Controllers monitor in Settings → Controllers
The live monitor in Settings → Controllers (a standalone ⌘⌥⇧C window exists in developer builds): P1–P4 slots, each with its controller and a live (green) / present-idle (orange) dot — here a DualSense and an NSO GameCube pad, both live.

3. Presence is not liveness

Enumeration lies, so the registry tracks whether a controller is actually live, not merely whether the OS lists it:

  • The Mayflash "Dolphin Bar" streams idle reports at ~4 Hz with no remote synced at all and exposes four USB ports whether or not anything is paired. A live Wii Remote streams at ~100 Hz. We tell them apart by report rate (≥6 reports in the last 300 ms ⇒ live), never by "a device exists."
  • NSO N64 / SNES / NES / Genesis pads report as non-extended on recent macOS and aren't cleanly visible through GameController, so The Vault reads them over HID.
  • Because of quirks like these, liveness is always stream/rate based, and identity is always (transport, VID, PID).

4. How each controller is handled, per emulator

This is the map from "which controller in which emulator" to "which input model, and can we hot-swap it live." Live hot-swap means you can turn Player 1's pad off mid-game and have another take over without relaunching.

Emulator / platformControllerInput modelLive hot-swap
Dolphin — GameCubeNSO GameCube / Switch 2 ProBLE→DSU✅ Yes
Dolphin — GameCubeDualSense / standard padNative (SDL)⚠️ Relaunch
Dolphin — WiiReal Wii Remote (Dolphin Bar)Real-HID nativeEmulator scans continuously
Dolphin — WiiDualSense (emulated Wii Remote)Native (SDL)⚠️ Relaunch
Cemu — Wii UWii U GamePad pairingBLE→DSU✅ Yes
Astris — SwitchAny padKeyboard-forward✅ Yes
RetroArchNSO N64/SNES/NES/Genesis, hand-mapped padsNative (RetroArch mfi/hid)Emulator's hotplug
RetroArchGeneric-pad macros / turboKeyboard-forward✅ Yes
PS2 (ARMSX2)DualSense (hotkey / macro forwarding)Keyboard-forward✅ Yes
PS2 (ARMSX2)Other padsNative (SDL)Emulator's SDL hotplug
RPCS3, PPSSPP, Azahar, melonDS, ShadPS4Any padNative (SDL / GameController)Emulator's hotplug
Flycast — DreamcastAny padNative (SDL, via RetroArch)Emulator's SDL hotplug

"Relaunch" means the emulator binds to a specific device at launch and doesn't reload its controller config while running — so switching to a different controller needs the game to be relaunched. See the handoff section for why.

5. Live controller handoff during a game

When you turn controllers on and off while a game is running, what happens depends entirely on the input model:

Where The Vault owns input (keyboard-forward & BLE→DSU)

These retarget live, so all three everyday cases just work:

  • Player 1 turns off → the surviving pad shifts down into the Player 1 slot and keeps playing. (For BLE→DSU this is a live re-slot of the virtual DSU ports; for keyboard-forward the forwarder simply reads whatever pad is live.)
  • No controllers → turn one on → it becomes Player 1.
  • All off but one → that one is Player 1.

Slots follow connection order, so if the original Player 1 pad comes back later it rejoins as the next free slot (i.e. Player 2) rather than bumping the pad that took over.

Where the emulator owns input (native SDL / real-HID)

Native emulators (RetroArch, RPCS3, and Dolphin/PS2 with a DualSense or standard pad) link SDL and read the pad themselves. They bind to a specific device at launch and load their controller config once — there's no external "reload controllers" signal. So if you power that pad off and turn on a different one, the emulator is still polling the pad named in its config and won't pick up the replacement. The only way to change it is to relaunch the game with fresh config.

The exception is real-HID (a real Wii Remote via the Dolphin Bar): Dolphin scans for it continuously, so powering the remote on connects it without a relaunch.

Practical takeaway: to swap controllers mid-game, use the pads where The Vault owns input — NSO pads on Dolphin GameCube, or any pad on Astris/Switch. For a DualSense in Dolphin or RetroArch, save-state and relaunch to change pads.

6. What The Vault sets up at launch

Whatever the model, launch reads the registry snapshot and configures the emulator for the pads present right now:

  • Native / real-HID → writes the per-controller button map and picks the right input driver (e.g. RetroArch's mfi for most NSO pads, hid for the Genesis pad), swapping A/B where Nintendo pads are labeled opposite the standard layout.
  • Keyboard-forward → starts the forwarder bound to the live slots.
  • BLE→DSU → prewarms the DSU ports for the live pads and hands the bridge its slots.
  • Everywhere → establishes the in-game hotkey scheme: hold the modifier and press R (Save), L (Load), Start (Quit), or ▲/▼ (slots). The modifier button differs per pad. See Hotkeys.

This page is the living reference for The Vault's controller architecture. Per-controller mapping and setup live in the pages listed under Controllers.