Skip to content

Controller support without Steam: Seaglass's own Xbox controller (ViGEmBus + HidHide) - #60

Merged
ApolloF merged 4 commits into
mainfrom
ApolloF/Controller-Input
Oct 17, 2026
Merged

ApolloF merged 4 commits into
mainfrom
ApolloF/Controller-Input

Conversation

@ApolloF

@ApolloF ApolloF commented Oct 17, 2026

Copy link
Copy Markdown
Owner

What

Seaglass now gives Xbox-only games its own virtual Xbox controller, so a DualSense, DS4 or Switch Pro works in them without Steam.

  • SDL3 (already embedded) reads the real controller in a new pad.Bridge mode.
  • A pure-Go ViGEmBus client (internal/vpad, no cgo) plugs in a virtual Xbox 360 pad and passes rumble back.
  • HidHide hides the real controller from the game while it runs.

The design and the options considered are in docs/design/controller-without-steam.md.

How it behaves

  • Per-game mode: a new Xbox controller mode (padMode: "xbox"). Native and Steam Input stay.
  • Auto mode: a PlayStation controller plus a game that supports Xbox controllers but not PlayStation ones starts with Seaglass's controller.
    • Without ViGEmBus, the game starts directly with a hint to set it up. There is no Steam fallback, by the maintainer's choice.
    • padTranslator: "steam" in Settings still routes Auto through Steam Input.
  • Settings → Big picture → Xbox controller:
    • Status row and Set up. Set up downloads the official ViGEmBus 1.22.0 and HidHide 1.5.230 installers, checks them against pinned SHA-256 hashes and runs them; Windows asks for permission for each.
    • Translator choice.
    • Hide toggle.
    • Unhide controller, in both the desktop and big picture Settings.
  • Hiding: no HidHide release has the per-process block list yet (it is only in the unreleased 1.7 tags). So Seaglass uses HidHide's saved block list:
    • It writes each controller it adds to %APPDATA%\Seaglass\hidden-controllers.json before adding it.
    • It removes exactly those entries at game end, on every start and quit (so a crash is undone at the next start), and from Unhide controller.
    • It also puts its own exe on HidHide's allow list, because HidHide blocks the hiding app too.
    • A HidHide with the per-process list is used automatically when present.
  • The PS button stays Seaglass's overlay button and is not forwarded to the game.
  • Playtime tracking: the game starts directly, so Seaglass has its process id.

Checks run (claude-dev)

  • go vet ./internal/...: passes.
  • CGO_ENABLED=1 go test -race -count=1 ./...: passes. In one earlier run internal/library TestConcurrentFlush failed on a temp-dir cleanup (The directory is not empty). That is a Windows flake in a package this PR only touches in a comment; it passed 5/5 on rerun.
  • npm --prefix frontend run check (0 errors) and npm --prefix frontend test (85 passed).
  • go run ./tools/notices -check: passes.
  • wails3 build: passes.
  • Live test against a real ViGEmBus 1.22 installed on claude-dev, with no physical controller: WL_REAL_VIGEM=1 go test -run RealViGEm -v ./internal/vpad passes.
    • The pad gets XInput slot 0, XInputGetState returns exactly the buttons, sticks and triggers fed in, and the slot disconnects on unplug.
    • This test found a real bug, fixed here: the bus ends good report submits with other errors, and ViGEmClient only treats "access denied" as a failure.
    • Also verified: when the owning process exits without unplugging (crash), ViGEmBus removes the pad, and all four XInput slots report not connected.
  • Mock UI checked in both themes at 1280 px and at 980 px, the app's minimum window width: setup states, Ready, Restart, and controllers hidden.

How to test on a real PC (needs a real controller)

  1. Settings → Big picture → Xbox controller → Set up. Expect two installers, each with a UAC prompt, and possibly a request to restart Windows. Afterwards the status reads "Ready."
    • If HidHide's own app shows hiding as off, turn it on. Seaglass says so in the status line.
  2. DualSense over USB in a game that supports Xbox controllers but not PlayStation ones, with the game on Auto or Xbox controller:
    • The launch panel says "Your controller works as an Xbox controller in this game."
    • Buttons, sticks and triggers work, and rumble reaches the DualSense.
    • The game doesn't see the DualSense too (no double input). Check this in a game that also reads HID directly if you have one.
  3. The same over Bluetooth. Input should work. Rumble over Bluetooth is not expected: enhanced reports stay off on purpose (see the 1.x changelog on DirectInput games).
  4. PS button during the game: it opens Seaglass's overlay and does nothing in the game.
  5. Unplug and replug the controller mid-game: the game keeps its controller, and input resumes.
  6. Crash case: end Seaglass in Task Manager mid-game.
    • The virtual pad disappears.
    • The DualSense stays hidden from other apps until Seaglass starts again. On start it shows the controller again: check in HidHide's app or in another app.
    • Unhide controller in Settings does the same by hand.
  7. Optional: a DS4 or Switch Pro, and one anti-cheat game (some refuse to start while ViGEmBus is in use; set that game to Native).
  8. Live test on your PC:
    set WL_REAL_VIGEM=1
    go test -run RealViGEm -v ./internal/vpad
    

ViGEmBus on claude-dev (left installed for now)

  • Installed with:
    gsudo powershell -File install.ps1
    
    which runs ViGEmBus_1.22.0_x64_x86_arm64.exe /qn /norestart (SHA-256 89220a78…014c6a, signed by Nefarius Software Solutions e.U.).
  • To uninstall:
    gsudo msiexec /x {966606F3-2745-49E9-BF15-5C3EAA4E9077} /qn /norestart
    
    or Settings → Apps → Installed apps → ViGEm Bus Driver → Uninstall. Then restart.

Known limits

  • ViGEmBus is archived (1.22.0, 2023). It works and is signed today, and a VIIPER/usbip-win2 backend can replace it behind vpad.Bridge if a Windows update breaks it.
  • HidHide's saved block list survives a crash until Seaglass next starts. Unhide controller covers that gap.
  • On a Seaglass upgrade from 1.10, Auto games that went through Steam Input now start directly with a setup hint until the drivers are set up.

Built: internal/vpad, pad Bridge mode, launch route and controller step,
settings, UI, mock, bindings, docs. Checks pass locally (go test, -race,
npm check/test, notices).
Unfinished: wails3 build not run, mock UI not reviewed in both themes or at
narrow width, live ViGEm test not run, HidHide v1.5.230 lacks the session
block list (decision pending), main-session review of the diff, PR.
Found with the live test on a real ViGEmBus 1.22: the bus ends a report
submit with other errors while the report still arrives, so only access
denied counts. Older buses without the wait-ready request still work,
and the rumble loop only ends on unplug or Close.
… Settings

No HidHide release has the per-process list yet, so Seaglass uses the saved
block list. It notes what it adds in hidden-controllers.json first and takes
exactly those off at game end, start, quit and from Unhide controller. Auto
without ViGEmBus starts directly with the setup hint (no Steam fallback).
@ApolloF
ApolloF merged commit 4cd5cfe into main Oct 17, 2026
1 check passed
@ApolloF
ApolloF deleted the ApolloF/Controller-Input branch October 17, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant