Description
On Batocera v43/v43.1 (X11), after exiting any game with the controller exit combo (hotkey+start), no further game can be launched: every new emulator dies within a second with
X connection to :N broken (explicit kill or server shutdown).
EmulationStation, openbox and X all stay alive and look healthy, xrandr/glxinfo -B answer normally, the X log records nothing at the moment of death, and restarting the ES session (/etc/init.d/S31emulationstation restart) "fixes" it — until the next hotkey exit. This did not occur on v41 on the same hardware.
Root cause
hotkeygen --permanent translates the pad exit combo into the emulator's native quit keystrokes on its persistent uinput keyboard (batocera hotkeys). In __handle_event, it presses the context's exit keys when the combo goes down and releases them when the combo goes up:
if begin:
send_keys(self.target, keys, True) # combo down -> e.g. ALT+F4 pressed
else:
send_keys(self.target, keys, False) # combo up -> ALT+F4 released
But a hotkey exit means the emulator quits while the player is still holding the combo, and emulatorlauncher then kills evmapy and resets the hotkeygen context (SIGHUP) mid-hold. When the player finally releases:
- the release event never reaches hotkeygen (evmapy is already dead), or
- it arrives after the context switch and maps to different keys (e.g. cemu context exit =
["KEY_LEFTALT","KEY_F4"], default ES context exit = "KEY_ESC"), so the release sends the wrong key.
Either way, the originally pressed quit keys are never released and stay held forever on the persistent virtual keyboard. Verified directly with active_keys() on the batocera hotkeys device after a cemu hotkey exit:
[('KEY_LEFTALT', 56), ('KEY_F4', 62)]
(A retroarch-context exit sticks KEY_LEFTSHIFT the same way: pressed SHIFT+ESC, released bare ESC after the context reset.)
Why it kills every subsequent game
X autorepeat fires the held ALT+F4 continuously, and the shipped openbox rc.xml binds A-F4 → Close. For a window that has not yet registered WM_DELETE_WINDOW — i.e. any emulator window during early init — openbox falls back to XKillClient, producing exactly the X connection to :N broken (explicit kill or server shutdown) seen in the emulator logs. Windowless clients (xrandr, glxinfo) are unaffected, which makes the failure masquerade as an emulator or GPU bug. A session restart only "fixes" it because a fresh X server ignores pre-existing key state on the still-held device.
Steps to reproduce (deterministic, no controller needed)
-
Launch any game through ES (the HTTP API works: curl -X POST -H "Content-Type: text/plain" --data "/path/to/rom" http://localhost:1234/launch).
-
While it runs, synthesize the combo signal evmapy would send, and hold it through the teardown (this mimics a player keeping the buttons pressed while the game exits, which is the natural way to use an exit combo):
import time, evdev
from evdev import ecodes
kb = evdev.UInput(name="repro", events={ecodes.EV_KEY: [ecodes.KEY_EXIT]})
time.sleep(0.5)
kb.write(ecodes.EV_KEY, ecodes.KEY_EXIT, 1); kb.syn() # combo down -> game exits
time.sleep(20) # held through teardown
kb.write(ecodes.EV_KEY, ecodes.KEY_EXIT, 0); kb.syn() # released long after
-
Check the virtual keyboard: ALT+F4 (or the context's exit keys) are now permanently held.
-
Launch any game — it dies instantly with the X connection broken error. All subsequent launches fail until the session (or just hotkeygen) is restarted.
Recovery / workaround
/etc/init.d/S90hotkeygen restart releases the stuck keys (the uinput device is destroyed) and fixes the session in under a second — no ES restart needed. As a workaround we installed a /userdata/system/scripts gameStop script that does exactly that after every game.
Suggested fix
hotkeygen should track which keys it has pressed on its target device and force-release all of them on SIGHUP / context change (and arguably also when a release event's action maps to different keys than the press did). That closes the race regardless of when the player lets go of the combo.
Environment
- Batocera v43.1, x86_64, X11 session (modesetting DDX), openbox
- Beelink SER8, Ryzen 7 8845HS / Radeon 780M iGPU
- Observed with both Cemu (ALT+F4 context) and libretro/RetroArch (SHIFT+ESC context)
Side observation while debugging (possibly a separate issue): every game stop logs an evmapy crash — FileNotFoundError: '/var/run/evmapy/evmapy.socket' in controller.cleanup() — so evmapy never shuts down cleanly.
Description
On Batocera v43/v43.1 (X11), after exiting any game with the controller exit combo (hotkey+start), no further game can be launched: every new emulator dies within a second with
EmulationStation, openbox and X all stay alive and look healthy,
xrandr/glxinfo -Banswer normally, the X log records nothing at the moment of death, and restarting the ES session (/etc/init.d/S31emulationstation restart) "fixes" it — until the next hotkey exit. This did not occur on v41 on the same hardware.Root cause
hotkeygen --permanenttranslates the pad exit combo into the emulator's native quit keystrokes on its persistent uinput keyboard (batocera hotkeys). In__handle_event, it presses the context's exit keys when the combo goes down and releases them when the combo goes up:But a hotkey exit means the emulator quits while the player is still holding the combo, and
emulatorlauncherthen kills evmapy and resets the hotkeygen context (SIGHUP) mid-hold. When the player finally releases:["KEY_LEFTALT","KEY_F4"], default ES context exit ="KEY_ESC"), so the release sends the wrong key.Either way, the originally pressed quit keys are never released and stay held forever on the persistent virtual keyboard. Verified directly with
active_keys()on thebatocera hotkeysdevice after a cemu hotkey exit:(A retroarch-context exit sticks
KEY_LEFTSHIFTthe same way: pressed SHIFT+ESC, released bare ESC after the context reset.)Why it kills every subsequent game
X autorepeat fires the held ALT+F4 continuously, and the shipped openbox rc.xml binds
A-F4→Close. For a window that has not yet registeredWM_DELETE_WINDOW— i.e. any emulator window during early init — openbox falls back toXKillClient, producing exactly theX connection to :N broken (explicit kill or server shutdown)seen in the emulator logs. Windowless clients (xrandr,glxinfo) are unaffected, which makes the failure masquerade as an emulator or GPU bug. A session restart only "fixes" it because a fresh X server ignores pre-existing key state on the still-held device.Steps to reproduce (deterministic, no controller needed)
Launch any game through ES (the HTTP API works:
curl -X POST -H "Content-Type: text/plain" --data "/path/to/rom" http://localhost:1234/launch).While it runs, synthesize the combo signal evmapy would send, and hold it through the teardown (this mimics a player keeping the buttons pressed while the game exits, which is the natural way to use an exit combo):
Check the virtual keyboard:
ALT+F4(or the context's exit keys) are now permanently held.Launch any game — it dies instantly with the
X connection brokenerror. All subsequent launches fail until the session (or just hotkeygen) is restarted.Recovery / workaround
/etc/init.d/S90hotkeygen restartreleases the stuck keys (the uinput device is destroyed) and fixes the session in under a second — no ES restart needed. As a workaround we installed a/userdata/system/scriptsgameStop script that does exactly that after every game.Suggested fix
hotkeygenshould track which keys it has pressed on its target device and force-release all of them on SIGHUP / context change (and arguably also when a release event's action maps to different keys than the press did). That closes the race regardless of when the player lets go of the combo.Environment
Side observation while debugging (possibly a separate issue): every game stop logs an evmapy crash —
FileNotFoundError: '/var/run/evmapy/evmapy.socket'incontroller.cleanup()— so evmapy never shuts down cleanly.