瀏覽代碼

x11: Use XInput2 to handle pointer input while grabbed and touches are active

Core X11 events are used for absolute motion while the mouse is grabbed, as XInput doesn't receive motion events from the master pointer, and the position from the slave devices lags one report behind. However, pointer events emulated from multitouch events can't be filtered out when using core events, as the XInput flags are not passed through.

Use the absolute position from slave devices in XInput motion events while the mouse is grabbed and touch events are active to allow filtering out pointer events emulated from touch events. Pointer events in this case may be a report behind the master device, but receiving input from both the pointer and touches simultaneously is not likely to be a common occurrence.
Frank Praznik 6 天之前
父節點
當前提交
5fe69e73ba
共有 3 個文件被更改,包括 31 次插入6 次删除
  1. 1 1
      src/video/x11/SDL_x11events.c
  2. 29 5
      src/video/x11/SDL_x11xinput2.c
  3. 1 0
      src/video/x11/SDL_x11xinput2.h

+ 1 - 1
src/video/x11/SDL_x11events.c

@@ -1836,7 +1836,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
 
 
     case MotionNotify:
     case MotionNotify:
     {
     {
-        if (data->xinput2_mouse_enabled && !data->mouse_grabbed) {
+        if (X11_Xinput2HandlesMotionForWindow(data)) {
             // This input is being handled by XInput2
             // This input is being handled by XInput2
             break;
             break;
         }
         }

+ 29 - 5
src/video/x11/SDL_x11xinput2.c

@@ -38,6 +38,8 @@ static bool xinput2_initialized;
 #if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH)
 #if defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_SCROLLINFO) || defined(SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH)
 static bool xinput2_scrolling_supported;
 static bool xinput2_scrolling_supported;
 static bool xinput2_multitouch_supported;
 static bool xinput2_multitouch_supported;
+static bool xinput2_grabbed_touch_raised;
+static int xinput2_active_touch_count;
 #endif
 #endif
 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
 #ifdef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_GESTURE
 static bool xinput2_gesture_supported;
 static bool xinput2_gesture_supported;
@@ -673,15 +675,17 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
             }
             }
 #endif
 #endif
 
 
-            if (xev->deviceid == videodata->xinput_master_pointer_device) {
-                // Use the master device for non-relative motion, as the slave devices can seemingly lag behind.
+            /* Use the master device for non-relative motion, as the slave devices can seemingly lag behind,
+             * except when the mouse is grabbed and touches are active, as core input events are used for
+             * absolute motion while the mouse is grabbed, and core events don't have the XIPointerEmulated
+             * flag to filter out pointer events emulated from touch events.
+             */
+            SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
+            if (window && (xev->deviceid == videodata->xinput_master_pointer_device || (xinput2_active_touch_count && window->internal->mouse_grabbed))) {
                 SDL_Mouse *mouse = SDL_GetMouse();
                 SDL_Mouse *mouse = SDL_GetMouse();
                 if (!mouse->relative_mode) {
                 if (!mouse->relative_mode) {
-                    SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
-                    if (window) {
                         X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false);
                         X11_ProcessHitTest(_this, window->internal, (float)xev->event_x, (float)xev->event_y, false);
                         SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y);
                         SDL_SendMouseMotion(0, window, SDL_GLOBAL_MOUSE_ID, false, (float)xev->event_x, (float)xev->event_y);
-                    }
                 }
                 }
             }
             }
         }
         }
@@ -692,6 +696,7 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
     {
     {
         const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
         const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
         float x, y;
         float x, y;
+        ++xinput2_active_touch_count;
         SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
         SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
         xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
         xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
         SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_DOWN, x, y, 1.0);
         SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_DOWN, x, y, 1.0);
@@ -702,6 +707,9 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
         const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
         const XIDeviceEvent *xev = (const XIDeviceEvent *)cookie->data;
         float x, y;
         float x, y;
         SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
         SDL_Window *window = xinput2_get_sdlwindow(videodata, xev->event);
+        if (!--xinput2_active_touch_count && window && window->internal->mouse_grabbed) {
+            xinput2_grabbed_touch_raised = true;
+        }
         xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
         xinput2_normalize_touch_coordinates(window, xev->event_x, xev->event_y, &x, &y);
         SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_UP, x, y, 1.0);
         SDL_SendTouch(0, xev->sourceid, xev->detail, window, SDL_EVENT_FINGER_UP, x, y, 1.0);
     } break;
     } break;
@@ -743,6 +751,20 @@ void X11_HandleXinput2Event(SDL_VideoDevice *_this, XGenericEventCookie *cookie)
 
 
 void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
 void X11_InitXinput2Multitouch(SDL_VideoDevice *_this)
 {
 {
+    xinput2_grabbed_touch_raised = false;
+    xinput2_active_touch_count = 0;
+}
+
+bool X11_Xinput2HandlesMotionForWindow(SDL_WindowData *window_data)
+{
+    /* Send the active flag once more after the touch count is zero, to suppress the
+     * emulated motion event when the last touch is raised.
+     */
+    const bool ret = window_data->xinput2_mouse_enabled &&
+                     (!window_data->mouse_grabbed || xinput2_active_touch_count || xinput2_grabbed_touch_raised);
+    xinput2_grabbed_touch_raised = false;
+
+    return ret;
 }
 }
 
 
 void X11_Xinput2Select(SDL_VideoDevice *_this, SDL_Window *window)
 void X11_Xinput2Select(SDL_VideoDevice *_this, SDL_Window *window)
@@ -888,6 +910,8 @@ void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window)
         return;
         return;
     }
     }
 
 
+    xinput2_grabbed_touch_raised = false;
+
     mods.modifiers = XIAnyModifier;
     mods.modifiers = XIAnyModifier;
     mods.status = 0;
     mods.status = 0;
 
 

+ 1 - 0
src/video/x11/SDL_x11xinput2.h

@@ -41,5 +41,6 @@ extern void X11_Xinput2UngrabTouch(SDL_VideoDevice *_this, SDL_Window *window);
 extern bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
 extern bool X11_Xinput2SelectMouseAndKeyboard(SDL_VideoDevice *_this, SDL_Window *window);
 extern void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this);
 extern void X11_Xinput2UpdateDevices(SDL_VideoDevice *_this);
 extern void X11_Xinput2UpdatePointerMapping(SDL_VideoDevice *_this);
 extern void X11_Xinput2UpdatePointerMapping(SDL_VideoDevice *_this);
+extern bool X11_Xinput2HandlesMotionForWindow(SDL_WindowData *window_data);
 
 
 #endif // SDL_x11xinput2_h_
 #endif // SDL_x11xinput2_h_