33#include < cmath>
44#include < algorithm>
55#include < cstring>
6+ #include < atomic>
67
78#ifdef _WIN32
89#define WIN32_LEAN_AND_MEAN
@@ -69,6 +70,17 @@ static float axisToMouseMotionF(float value, const AxisMouseMapping& mapping) {
6970 return processed * mapping.sensitivity ;
7071}
7172
73+ #ifdef __linux__
74+ // Global error handler state for X11 async error handling
75+ static std::atomic<int > g_x11_error_occurred{0 };
76+
77+ static int x11_error_handler (Display* /* display*/ , XErrorEvent* /* event*/ ) {
78+ // Don't crash on X11 errors - game may have grabbed focus
79+ g_x11_error_occurred.store (1 , std::memory_order_relaxed);
80+ return 0 ;
81+ }
82+ #endif
83+
7284// ── Construction / reset ─────────────────────────────────────────────────────
7385
7486GamepadInputRemapper::GamepadInputRemapper () {
@@ -459,17 +471,46 @@ bool GamepadInputRemapper::sendInput(const GamepadState& current,
459471
460472 return true ;
461473#elif defined(__linux__)
462- static Display* display = XOpenDisplay (nullptr );
474+ // FIX: Open and close X11 display per-call instead of caching.
475+ // When a game launches fullscreen, it takes control of X11 and the
476+ // cached display handle becomes invalid, causing X11 calls to block.
477+ // Also set up error handler to prevent crashes on X11 errors.
478+ g_x11_error_occurred.store (0 , std::memory_order_relaxed);
479+ XSetErrorHandler (x11_error_handler);
480+
481+ Display* display = XOpenDisplay (nullptr );
463482 if (!display) {
483+ XSetErrorHandler (nullptr );
464484 return false ;
465485 }
466486
487+ // Check if error occurred during display open
488+ if (g_x11_error_occurred.load (std::memory_order_relaxed)) {
489+ XCloseDisplay (display);
490+ XSetErrorHandler (nullptr );
491+ return false ;
492+ }
493+
494+ // Try to get input focus - this may fail during game launch
467495 Window root = DefaultRootWindow (display);
468- Window focus;
469- int revert_to;
496+ Window focus = 0 ;
497+ int revert_to = 0 ;
498+
499+ // Use XGetInputFocus with error checking
470500 XGetInputFocus (display, &focus, &revert_to);
501+ int focus_error = XSync (display, False);
502+ if (g_x11_error_occurred.load (std::memory_order_relaxed)) {
503+ XCloseDisplay (display);
504+ XSetErrorHandler (nullptr );
505+ return false ;
506+ }
471507
472508 for (const auto & e : events) {
509+ // Skip further processing if error occurred
510+ if (g_x11_error_occurred.load (std::memory_order_relaxed)) {
511+ break ;
512+ }
513+
473514 switch (e.type ) {
474515 case GamepadInputEvent::Type::Keyboard: {
475516 KeyCode keycode = static_cast <KeyCode>(e.keyboard .virtual_key );
@@ -511,7 +552,9 @@ bool GamepadInputRemapper::sendInput(const GamepadState& current,
511552 }
512553
513554 XFlush (display);
514- return true ;
555+ XCloseDisplay (display);
556+ XSetErrorHandler (nullptr );
557+ return !g_x11_error_occurred.load (std::memory_order_relaxed);
515558#else
516559 (void )events;
517560 return false ;
@@ -541,16 +584,34 @@ std::string GamepadInputRemapper::virtualKeyName(uint16_t vk) {
541584 snprintf (buf, sizeof (buf), " VK_0x%02X" , vk);
542585 return buf;
543586#elif defined(__linux__)
544- static Display* display = XOpenDisplay (nullptr );
587+ // FIX: Open and close display per-call instead of caching.
588+ // This ensures we get a fresh connection that works even after
589+ // a game has taken control of X11.
590+ Display* display = XOpenDisplay (nullptr );
545591 if (!display) {
546592 char buf[32 ];
547593 snprintf (buf, sizeof (buf), " VK_0x%02X" , vk);
548594 return buf;
549595 }
596+
597+ g_x11_error_occurred.store (0 , std::memory_order_relaxed);
598+ XSetErrorHandler (x11_error_handler);
599+
550600 KeyCode keycode = static_cast <KeyCode>(vk);
551- KeySym keysym = XKeycodeToKeysym (display, keycode, 0 );
601+ // Use XGetKeyboardMapping as fallback for XKeycodeToKeysym deprecation
602+ KeySym keysym = 0 ;
603+ int keysyms_per_keycode_return = 0 ;
604+ KeySym* keysym_map = XGetKeyboardMapping (display, keycode, 1 , &keysyms_per_keycode_return);
605+ if (keysym_map && keysyms_per_keycode_return > 0 ) {
606+ keysym = keysym_map[0 ];
607+ XFree (keysym_map);
608+ }
552609 const char * name = XKeysymToString (keysym);
553- if (name) {
610+
611+ XSetErrorHandler (nullptr );
612+ XCloseDisplay (display);
613+
614+ if (name && !g_x11_error_occurred.load (std::memory_order_relaxed)) {
554615 return std::string (name);
555616 }
556617 char buf[32 ];
@@ -563,4 +624,4 @@ std::string GamepadInputRemapper::virtualKeyName(uint16_t vk) {
563624#endif
564625}
565626
566- } // namespace gcpad
627+ } // namespace gcpad
0 commit comments