[v3] winex11: Use XIAllDevices to select for XI_RawMotion

Carlos Garnacho carlosg at gnome.org
Mon Dec 12 07:32:41 CST 2016


Change the strategy used to get raw motion from slave devices. Instead
of selecting for XI2 events for every slave device individually, do it
for XIAllDevices, and store the current device's relative X/Y valuators
so they can be quickly looked up in the XI_RawMotion events received.

The current device valuators are obtained through XI_DeviceChanged
events as those will be sent whenever a different slave device is used
to drive the master pointer.

In the case that the user just keeps using the same device when
enable_xinput2() is called, no XI_DeviceChanged event will be sent.
For this case, keep the device list obtained on enable_xinput2() and
perform the device lookup on the first XI_RawMotion event obtained.

Raw motion from other devices than those attached to the client pointer
will be ignored.

This makes Wine able to react to device hotplugs whithout listening
explicitly to device hierarchy changes. As the master pointer will
change its valuators to reflect those of the currently used slave,
it is enough to find the relative X/Y valuators in it to interpret
raw motion from the current slave, without further info about the
specific device.

Signed-off-by: Carlos Garnacho <carlosg at gnome.org>
---

 v3: Avoid new/free, use internal struct to store interesting valuator
     data. Fixed possible race condition between device info retrieval
     and event mask setup. Minor code style fixes.

 dlls/winex11.drv/mouse.c  | 185 +++++++++++++++++++++++++++++++---------------
 dlls/winex11.drv/x11drv.h |  10 +++
 2 files changed, 137 insertions(+), 58 deletions(-)

diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 186899f..23928ae 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -247,6 +247,43 @@ void sync_window_cursor( Window window )
 }
 
 /***********************************************************************
+ *              update_relative_valuators
+ */
+static void update_relative_valuators(XIAnyClassInfo **valuators, int n_valuators)
+{
+    struct x11drv_thread_data *thread_data = x11drv_thread_data();
+    int i;
+
+    thread_data->x_rel_valuator.number = -1;
+    thread_data->y_rel_valuator.number = -1;
+
+    for (i = 0; i < n_valuators; i++)
+    {
+        XIValuatorClassInfo *class = (XIValuatorClassInfo *)valuators[i];
+        struct x11drv_valuator_data *valuator_data = NULL;
+
+        if (valuators[i]->type != XIValuatorClass) continue;
+        if (class->label == x11drv_atom( Rel_X ) ||
+            (!class->label && class->number == 0 && class->mode == XIModeRelative))
+        {
+            valuator_data = &thread_data->x_rel_valuator;
+        }
+        else if (class->label == x11drv_atom( Rel_Y ) ||
+                 (!class->label && class->number == 1 && class->mode == XIModeRelative))
+        {
+            valuator_data = &thread_data->y_rel_valuator;
+        }
+
+        if (valuator_data) {
+            valuator_data->number = class->number;
+            valuator_data->min = class->min;
+            valuator_data->max = class->max;
+        }
+    }
+}
+
+
+/***********************************************************************
  *              enable_xinput2
  */
 static void enable_xinput2(void)
@@ -254,9 +291,9 @@ static void enable_xinput2(void)
 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
     struct x11drv_thread_data *data = x11drv_thread_data();
     XIEventMask mask;
-    XIDeviceInfo *devices;
+    XIDeviceInfo *pointer_info;
     unsigned char mask_bits[XIMaskLen(XI_LASTEVENT)];
-    int i;
+    int count;
 
     if (!xinput2_available) return;
 
@@ -271,28 +308,33 @@ static void enable_xinput2(void)
         }
     }
     if (data->xi2_state == xi_unavailable) return;
-
-    if (data->xi2_devices) pXIFreeDeviceInfo( data->xi2_devices );
     if (!pXIGetClientPointer( data->display, None, &data->xi2_core_pointer )) return;
-    data->xi2_devices = devices = pXIQueryDevice( data->display, XIAllDevices, &data->xi2_device_count );
 
     mask.mask     = mask_bits;
     mask.mask_len = sizeof(mask_bits);
+    mask.deviceid = XIAllDevices;
     memset( mask_bits, 0, sizeof(mask_bits) );
+    XISetMask( mask_bits, XI_DeviceChanged );
     XISetMask( mask_bits, XI_RawMotion );
     XISetMask( mask_bits, XI_ButtonPress );
 
-    for (i = 0; i < data->xi2_device_count; ++i)
-    {
-        if (devices[i].use == XISlavePointer && devices[i].attachment == data->xi2_core_pointer)
-        {
-            TRACE( "Device %u (%s) is attached to the core pointer\n",
-                   devices[i].deviceid, debugstr_a(devices[i].name) );
-            mask.deviceid = devices[i].deviceid;
-            pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
-            data->xi2_state = xi_enabled;
-        }
-    }
+    pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
+
+    pointer_info = pXIQueryDevice( data->display, data->xi2_core_pointer, &count );
+    update_relative_valuators( pointer_info->classes, pointer_info->num_classes );
+    pXIFreeDeviceInfo( pointer_info );
+
+    /* This device info list is only used to find the initial current slave if
+     * no XI_DeviceChanged events happened. If any hierarchy change occurred that
+     * might be relevant here (eg. user switching mice after (un)plugging), a
+     * XI_DeviceChanged event will point us to the right slave. So this list is
+     * 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_current_slave = 0;
+
+    data->xi2_state = xi_enabled;
 #endif
 }
 
@@ -303,9 +345,7 @@ static void disable_xinput2(void)
 {
 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
     struct x11drv_thread_data *data = x11drv_thread_data();
-    XIDeviceInfo *devices = data->xi2_devices;
     XIEventMask mask;
-    int i;
 
     if (data->xi2_state != xi_enabled) return;
 
@@ -314,18 +354,15 @@ static void disable_xinput2(void)
 
     mask.mask = NULL;
     mask.mask_len = 0;
+    mask.deviceid = XIAllDevices;
 
-    for (i = 0; i < data->xi2_device_count; ++i)
-    {
-        if (devices[i].use == XISlavePointer && devices[i].attachment == data->xi2_core_pointer)
-        {
-            mask.deviceid = devices[i].deviceid;
-            pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
-        }
-    }
-    pXIFreeDeviceInfo( devices );
+    pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );
+    pXIFreeDeviceInfo( data->xi2_devices );
+    data->x_rel_valuator.number = -1;
+    data->y_rel_valuator.number = -1;
     data->xi2_devices = NULL;
-    data->xi2_device_count = 0;
+    data->xi2_core_pointer = 0;
+    data->xi2_current_slave = 0;
 #endif
 }
 
@@ -1616,6 +1653,22 @@ BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
 
 /***********************************************************************
+ *           X11DRV_DeviceChanged
+ */
+static BOOL X11DRV_DeviceChanged( XGenericEventCookie *xev )
+{
+    XIDeviceChangedEvent *event = xev->data;
+    struct x11drv_thread_data *data = x11drv_thread_data();
+
+    if (event->deviceid != data->xi2_core_pointer) return FALSE;
+    if (event->reason != XISlaveSwitch) return FALSE;
+
+    update_relative_valuators( event->classes, event->num_classes );
+    data->xi2_current_slave = event->sourceid;
+    return TRUE;
+}
+
+/***********************************************************************
  *           X11DRV_RawMotion
  */
 static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
@@ -1624,14 +1677,38 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
     const double *values = event->valuators.values;
     RECT virtual_rect;
     INPUT input;
-    int i, j;
-    double dx = 0, dy = 0;
+    int i;
+    double dx = 0, dy = 0, val;
     struct x11drv_thread_data *thread_data = x11drv_thread_data();
-    XIDeviceInfo *devices = thread_data->xi2_devices;
+    struct x11drv_valuator_data *x_rel, *y_rel;
 
+    if (thread_data->x_rel_valuator.number < 0 || thread_data->y_rel_valuator.number < 0) return FALSE;
     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)
+    {
+        XIDeviceInfo *devices = thread_data->xi2_devices;
+
+        for (i = 0; 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;
+
+    x_rel = &thread_data->x_rel_valuator;
+    y_rel = &thread_data->y_rel_valuator;
+
     input.u.mi.mouseData   = 0;
     input.u.mi.dwFlags     = MOUSEEVENTF_MOVE;
     input.u.mi.time        = EVENT_x11_time_to_win32_time( event->time );
@@ -1640,36 +1717,25 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
     input.u.mi.dy          = 0;
 
     virtual_rect = get_virtual_screen_rect();
-    for (i = 0; i < thread_data->xi2_device_count; ++i)
+
+    for (i = 0; i <= max ( x_rel->number, y_rel->number ); i++)
     {
-        if (devices[i].deviceid != event->deviceid) continue;
-        for (j = 0; j < devices[i].num_classes; j++)
+        if (!XIMaskIsSet( event->valuators.mask, i )) continue;
+        val = *values++;
+        if (i == x_rel->number)
         {
-            XIValuatorClassInfo *class = (XIValuatorClassInfo *)devices[i].classes[j];
-
-            if (devices[i].classes[j]->type != XIValuatorClass) continue;
-            if (XIMaskIsSet( event->valuators.mask, class->number ))
-            {
-                double val = *values++;
-                if (class->label == x11drv_atom( Rel_X ) ||
-                    (!class->label && class->number == 0 && class->mode == XIModeRelative))
-                {
-                    input.u.mi.dx = dx = val;
-                    if (class->min < class->max)
-                        input.u.mi.dx = val * (virtual_rect.right - virtual_rect.left)
-                                            / (class->max - class->min);
-                }
-                else if (class->label == x11drv_atom( Rel_Y ) ||
-                         (!class->label && class->number == 1 && class->mode == XIModeRelative))
-                {
-                    input.u.mi.dy = dy = val;
-                    if (class->min < class->max)
-                        input.u.mi.dy = val * (virtual_rect.bottom - virtual_rect.top)
-                                            / (class->max - class->min);
-                }
-            }
+            input.u.mi.dx = dx = val;
+            if (x_rel->min < x_rel->max)
+                input.u.mi.dx = val * (virtual_rect.right - virtual_rect.left)
+                                    / (x_rel->max - x_rel->min);
+        }
+        if (i == y_rel->number)
+        {
+            input.u.mi.dy = dy = val;
+            if (y_rel->min < y_rel->max)
+                input.u.mi.dy = val * (virtual_rect.bottom - virtual_rect.top)
+                                    / (y_rel->max - y_rel->min);
         }
-        break;
     }
 
     if (broken_rawevents && is_old_motion_event( xev->serial ))
@@ -1743,6 +1809,9 @@ BOOL X11DRV_GenericEvent( HWND hwnd, XEvent *xev )
 
     switch (event->evtype)
     {
+    case XI_DeviceChanged:
+        ret = X11DRV_DeviceChanged( event );
+        break;
     case XI_RawMotion:
         ret = X11DRV_RawMotion( event );
         break;
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 5e8d85f..f125289 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -309,6 +309,13 @@ struct x11drv_escape_flush_gl_drawable
  * X11 USER driver
  */
 
+struct x11drv_valuator_data
+{
+    double min;
+    double max;
+    int number;
+};
+
 struct x11drv_thread_data
 {
     Display *display;
@@ -327,7 +334,10 @@ struct x11drv_thread_data
     enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled } xi2_state; /* XInput2 state */
     void    *xi2_devices;          /* list of XInput2 devices (valid when state is enabled) */
     int      xi2_device_count;
+    struct x11drv_valuator_data x_rel_valuator;
+    struct x11drv_valuator_data y_rel_valuator;
     int      xi2_core_pointer;     /* XInput2 core pointer id */
+    int      xi2_current_slave;    /* Current slave driving the Core pointer */
 };
 
 extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN;
-- 
2.9.3




More information about the wine-patches mailing list