Просмотр исходного кода

x11: Use XInput2 events to pass through the keyboard ID to core key events

XInput2 keyboard handling has limitations: system keys that shouldn't be passed through when the keyboard isn't grabbed can be seen, and the text input system needs key events to flow through the X server to function properly (passing synthesized events through the filter function is not sufficient and doesn't work with non-Latin character sets).

The primary bit of information missing from the core X key events that XInput2 provides is the source device, so use the XInput2 slave keyboard device events to store that value, and apply it to core X key events with the same serial. XInput2 events always arrive before core events so this works universally.
Frank Praznik 3 недель назад
Родитель
Сommit
75a65e05e1
3 измененных файлов с 23 добавлено и 2 удалено
  1. 6 2
      src/video/x11/SDL_x11events.c
  2. 2 0
      src/video/x11/SDL_x11video.h
  3. 15 0
      src/video/x11/SDL_x11xinput2.c

+ 6 - 2
src/video/x11/SDL_x11events.c

@@ -1834,12 +1834,16 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
     case KeyPress:
     case KeyRelease:
     {
+        SDL_KeyboardID keyboardID = SDL_GLOBAL_KEYBOARD_ID;
         if (data->xinput2_keyboard_enabled) {
-            // This input is being handled by XInput2
+            // This input is being handled by XInput2.
             break;
+        } else if (xevent->xkey.serial == videodata->xinput_last_key_serial) {
+            // Use the device ID from the XInput2 event if the serials match.
+            keyboardID = videodata->xinput_last_keyboard_device;
         }
 
-        X11_HandleKeyEvent(_this, data, SDL_GLOBAL_KEYBOARD_ID, xevent);
+        X11_HandleKeyEvent(_this, data, keyboardID, xevent);
     } break;
 
     case MotionNotify:

+ 2 - 0
src/video/x11/SDL_x11video.h

@@ -140,6 +140,8 @@ struct SDL_VideoData
 
     SDL_XInput2DeviceInfo *mouse_device_info;
     unsigned long xinput_last_button_serial;
+    unsigned long xinput_last_key_serial;
+    int xinput_last_keyboard_device;
     int xinput_master_pointer_device;
     bool xinput_hierarchy_changed;
 

+ 15 - 0
src/video/x11/SDL_x11xinput2.c

@@ -303,6 +303,12 @@ bool X11_InitXinput2(SDL_VideoDevice *_this)
     eventmask.mask_len = sizeof(mask);
     eventmask.mask = mask;
 
+#ifndef USE_XINPUT2_KEYBOARD
+    // If not using the full keyboard handling, register for keypresses to get the event source devices.
+    XISetMask(mask, XI_KeyPress);
+    XISetMask(mask, XI_KeyRelease);
+#endif
+
     XISetMask(mask, XI_HierarchyChanged);
     X11_XISelectEvents(data->display, DefaultRootWindow(data->display), &eventmask, 1);
 
@@ -535,6 +541,8 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
     case XI_KeyRelease:
     {
         const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
+
+#ifdef XINPUT2_USE_KEYBOARD
         SDL_WindowData *windowdata = X11_FindWindow(videodata, xev->event);
         XEvent xevent;
 
@@ -564,6 +572,13 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
         xevent.xkey.same_screen = 1;
 
         X11_HandleKeyEvent(_this, windowdata, (SDL_KeyboardID)xev->sourceid, &xevent);
+#else
+        /* Keys are handled through core X events, however, note the device ID and
+         * associated serial, so that the source device ID can be passed through.
+         */
+        videodata->xinput_last_key_serial = xev->serial;
+        videodata->xinput_last_keyboard_device = xev->sourceid;
+#endif
     } break;
 
     case XI_RawButtonPress: