Skip to content

Rebind a joystick when it is plugged back in - #7888

Open
dustinspace217 wants to merge 2 commits into
scp-fs2open:masterfrom
dustinspace217:input/joystick-replug
Open

dustinspace217 wants to merge 2 commits into
scp-fs2open:masterfrom
dustinspace217:input/joystick-replug

Conversation

@dustinspace217

Copy link
Copy Markdown
Contributor

What?

A joystick or gamepad that gets unplugged and plugged back in during the game works again right away, instead of staying dead until you restart.

Why?

If your stick's USB cable gets bumped mid-mission, or you move it to another port, the game sees it come back but never gives it back its control slot. Steering and buttons do nothing until you quit and relaunch.

How?

When SDL reports a device being added, device_event_handler checks whether it already knows that stick by GUID. If it did, it refreshed the stored stick and returned early, skipping the code that puts the stick back in its Joy-0..3 slot. The unplug handler had already emptied that slot, so the stick stayed unassigned. Now a returning stick goes through the same binding code as a new one, which also restarts force feedback on Joy-0.

A few things in the same handler came along with it:

  • SDL sends an "added" event at startup for sticks that init() has already set up. Those are now skipped by instance ID, so a stick isn't bound twice on every launch.
  • Only a stick that's currently unplugged counts as one coming back. Two sticks of the same model share a GUID, so plugging in a second one used to take over the first. When several known sticks share a GUID, the saved choice now goes to the one that's plugged in.
  • The early exit when all four slots were full is gone. It skipped the rebind for a stick you'd picked again while it was unplugged.
  • The extra handle opened to check the new device is now closed. It leaked one handle per plug-in.
  • A line now goes to the log when a known stick reconnects.

Testing?

Linux Debug builds, master against this branch, SM1-01 on retail data. I used a virtual USB joystick that a script can unplug and replug, and measured the player ship's turn rate with the stick pushed:

  • One stick, three unplug/replug cycles: master never turns again after the first unplug; this branch turns at the same rate after every replug. OpenGL and Vulkan.
  • The same stick chosen in a previous session and set up at launch: bound once at startup, works after a replug. Same with in-game options turned off and the old Joy0GUID setting.
  • A virtual Xbox 360 controller (SDL treats it as a gamepad): dead after each replug on master, works on this branch.
  • Two sticks on Joy-0 and Joy-1: each comes back to its own slot. On master, replugging Joy-0 leaves it dead.
  • A brand-new stick plugged in mid-mission, a different stick chosen while the first is unplugged, all four slots full, and a second stick of the same model coming and going: each ends up where it should.
  • Open input handles after three replugs: master goes from 1 to 4, this branch stays at 1.

Not covered: real hardware, force feedback (the virtual stick has none), and Windows/macOS.

Anything Else?

Two older limits are left alone: with two identical sticks plugged in at once, the saved setting can't tell them apart (same as starting the game with both plugged in), and the unplug handler only clears the first slot a stick is in. Written with Claude Code; the investigation and testing ran on my machine.

When a joystick the game already knew about was reconnected, the
device handler refreshed it and returned before the slot-binding
code ran, so the stick stayed unassigned until the game restarted.
Fall through to the binding code instead, which also restarts force
feedback on Joy-0.

Along with that, in the same handler:
- Ignore the add events SDL sends at startup for sticks that init()
  already set up, so they are not bound a second time.
- Only treat a stick that is currently unplugged as one coming back.
  Two sticks of the same model share a GUID, and plugging in the
  second one used to take over the first. When several known sticks
  share a GUID, a saved choice now picks the one that is plugged in.
- Drop the shortcut that skipped binding when every slot was full;
  it skipped the rebind for a stick chosen again while unplugged.
- Close the extra handle opened to check the new device, which
  leaked one handle per plug-in.
- Keep the old handles open until the stick has its slot again, and
  log when a known stick reconnects.
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