>From 4c9fc2325e13e0fc662f23261221d3c75ee8ef2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20Bernon?= <rbernon@codeweavers.com>
Date: Tue, 23 Jul 2019 14:10:44 +0200
Subject: [PATCH] winex11.drv: Directly listen to master XInput2 devices if
 supported

Under XInput2 protocol version < 2.1, raw events should not be received
if pointer grab is active. However slave device events are still
received regardless of this specification and Wine implemented a
workaround to get raw events during pointer grabs by listening to these
slave device events.

By advertising to support XInput2 protocol version >= 2.1, where raw
events are sent even during pointer grabs, it is possible to simplify
the code by listening to master device events only.
---
 dlls/winex11.drv/event.c | 42 +-------------------------------
 dlls/winex11.drv/mouse.c | 52 +++++++++++++++++++++++++++++-----------
 2 files changed, 39 insertions(+), 55 deletions(-)

diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index 90f139dd0fa..6ecdf3a47e7 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -274,46 +274,6 @@ enum event_merge_action
     MERGE_IGNORE    /* ignore the new event, keep the old one */
 };
 
-/***********************************************************************
- *           merge_raw_motion_events
- */
-#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
-static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next )
-{
-    int i, j, k;
-    unsigned char mask;
-
-    if (!prev->valuators.mask_len) return MERGE_HANDLE;
-    if (!next->valuators.mask_len) return MERGE_HANDLE;
-
-    mask = prev->valuators.mask[0] | next->valuators.mask[0];
-    if (mask == next->valuators.mask[0])  /* keep next */
-    {
-        for (i = j = k = 0; i < 8; i++)
-        {
-            if (XIMaskIsSet( prev->valuators.mask, i ))
-                next->valuators.values[j] += prev->valuators.values[k++];
-            if (XIMaskIsSet( next->valuators.mask, i )) j++;
-        }
-        TRACE( "merging duplicate GenericEvent\n" );
-        return MERGE_DISCARD;
-    }
-    if (mask == prev->valuators.mask[0])  /* keep prev */
-    {
-        for (i = j = k = 0; i < 8; i++)
-        {
-            if (XIMaskIsSet( next->valuators.mask, i ))
-                prev->valuators.values[j] += next->valuators.values[k++];
-            if (XIMaskIsSet( prev->valuators.mask, i )) j++;
-        }
-        TRACE( "merging duplicate GenericEvent\n" );
-        return MERGE_IGNORE;
-    }
-    /* can't merge events with disjoint masks */
-    return MERGE_HANDLE;
-}
-#endif
-
 /***********************************************************************
  *           merge_events
  *
@@ -365,7 +325,7 @@ static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
             if (next->xcookie.extension != xinput2_opcode) break;
             if (next->xcookie.evtype != XI_RawMotion) break;
             if (x11drv_thread_data()->warp_serial) break;
-            return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
+            return MERGE_HANDLE;
 #endif
         }
         break;
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 93023e13c4b..5d72e7bd6d3 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -132,6 +132,8 @@ static Cursor create_cursor( HANDLE handle );
 
 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
 static BOOL xinput2_available;
+static int xinput2_version_major = 2;
+static int xinput2_version_minor = 1;
 static BOOL broken_rawevents;
 #define MAKE_FUNCPTR(f) static typeof(f) * p##f
 MAKE_FUNCPTR(XIGetClientPointer);
@@ -303,8 +305,11 @@ static void enable_xinput2(void)
 
     if (data->xi2_state == xi_unknown)
     {
-        int major = 2, minor = 0;
-        if (!pXIQueryVersion( data->display, &major, &minor )) data->xi2_state = xi_disabled;
+        if (!pXIQueryVersion( data->display, &xinput2_version_major, &xinput2_version_minor ))
+        {
+            TRACE( "XInput2 v%d.%d available\n", xinput2_version_major, xinput2_version_minor );
+            data->xi2_state = xi_disabled;
+        }
         else
         {
             data->xi2_state = xi_unavailable;
@@ -316,12 +321,18 @@ static void enable_xinput2(void)
 
     mask.mask     = mask_bits;
     mask.mask_len = sizeof(mask_bits);
-    mask.deviceid = XIAllDevices;
+    mask.deviceid = XIAllMasterDevices;
     memset( mask_bits, 0, sizeof(mask_bits) );
     XISetMask( mask_bits, XI_DeviceChanged );
     XISetMask( mask_bits, XI_RawMotion );
     XISetMask( mask_bits, XI_ButtonPress );
 
+    /* XInput 2.0 has a problematic behavior where master pointer will
+     * not send raw events to the root window whenever a grab is active
+     */
+    if (xinput2_version_major == 2 && xinput2_version_minor == 0)
+        mask.deviceid = XIAllDevices;
+
     pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
 
     pointer_info = pXIQueryDevice( data->display, data->xi2_core_pointer, &count );
@@ -335,7 +346,7 @@ static void enable_xinput2(void)
      * safe to be obtained statically at enable_xinput2() time.
      */
     if (data->xi2_devices) pXIFreeDeviceInfo( data->xi2_devices );
-    data->xi2_devices = pXIQueryDevice( data->display, XIAllDevices, &data->xi2_device_count );
+    data->xi2_devices = pXIQueryDevice( data->display, mask.deviceid, &data->xi2_device_count );
     data->xi2_current_slave = 0;
 
     data->xi2_state = xi_enabled;
@@ -358,7 +369,13 @@ static void disable_xinput2(void)
 
     mask.mask = NULL;
     mask.mask_len = 0;
-    mask.deviceid = XIAllDevices;
+    mask.deviceid = XIAllMasterDevices;
+
+    /* XInput 2.0 has a problematic behavior where master pointer will
+     * not send raw events to the root window whenever a grab is active
+     */
+    if (xinput2_version_major == 2 && xinput2_version_minor == 0)
+        mask.deviceid = XIAllDevices;
 
     pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
     pXIFreeDeviceInfo( data->xi2_devices );
@@ -1797,25 +1814,32 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
     if (!event->valuators.mask_len) return FALSE;
     if (thread_data->xi2_state != xi_enabled) return FALSE;
 
-    /* If there is no slave currently detected, no previous motion nor device
-     * change events were received. Look it up now on the device list in this
-     * case.
-     */
-    if (!thread_data->xi2_current_slave)
+    if (xinput2_version_major == 2 && xinput2_version_minor == 0)
     {
         XIDeviceInfo *devices = thread_data->xi2_devices;
 
-        for (i = 0; i < thread_data->xi2_device_count; i++)
+        /* If there is no slave currently detected, no previous motion nor device
+         * change events were received. Look it up now on the device list in this
+         * case.
+         */
+        for (i = 0; !thread_data->xi2_current_slave && i < thread_data->xi2_device_count; i++)
         {
             if (devices[i].use != XISlavePointer) continue;
             if (devices[i].deviceid != event->deviceid) continue;
             if (devices[i].attachment != thread_data->xi2_core_pointer) continue;
             thread_data->xi2_current_slave = event->deviceid;
-            break;
         }
-    }
 
-    if (event->deviceid != thread_data->xi2_current_slave) return FALSE;
+        /* Only listen to slave device events on XInput == 2.0 */
+        if (event->deviceid != thread_data->xi2_current_slave)
+            return FALSE;
+    }
+    else
+    {
+        /* Only listen to master device events on XInput >= 2.1 */
+        if (event->deviceid != thread_data->xi2_core_pointer)
+            return FALSE;
+    }
 
     x_rel = &thread_data->x_rel_valuator;
     y_rel = &thread_data->y_rel_valuator;
-- 
2.20.1

