From: Carlos Garnacho Subject: [2/2 v2] winex11: Use XIAllDevices to select for XI_RawMotion Message-Id: <20161118114319.20173-2-carlosg@gnome.org> Date: Fri, 18 Nov 2016 12:43:19 +0100 In-Reply-To: <20161118114319.20173-1-carlosg@gnome.org> References: <20161118114319.20173-1-carlosg@gnome.org> 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 --- Changes since v1: New patch resulting from discussion at wine-devel ML dlls/winex11.drv/mouse.c | 187 +++++++++++++++++++++++++++++++--------------- dlls/winex11.drv/x11drv.h | 3 + 2 files changed, 130 insertions(+), 60 deletions(-) diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c index 4d20882..741b425 100644 --- a/dlls/winex11.drv/mouse.c +++ b/dlls/winex11.drv/mouse.c @@ -247,6 +247,41 @@ 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; + + free( thread_data->x_rel_valuator ); + free( thread_data->y_rel_valuator ); + thread_data->x_rel_valuator = NULL; + thread_data->y_rel_valuator = NULL; + + for (i = 0; i < n_valuators; i++) + { + XIValuatorClassInfo *class = (XIValuatorClassInfo *)valuators[i]; + + if (valuators[i]->type != XIValuatorClass) continue; + if (class->label == x11drv_atom( Rel_X ) || + (!class->label && class->number == 0 && class->mode == XIModeRelative)) + { + thread_data->x_rel_valuator = malloc( sizeof(XIValuatorClassInfo) ); + memcpy ( thread_data->x_rel_valuator, class, sizeof(XIValuatorClassInfo) ); + } + else if (class->label == x11drv_atom( Rel_Y ) || + (!class->label && class->number == 1 && class->mode == XIModeRelative)) + { + thread_data->y_rel_valuator = malloc( sizeof(XIValuatorClassInfo) ); + memcpy ( thread_data->y_rel_valuator, class, sizeof(XIValuatorClassInfo) ); + } + } +} + + +/*********************************************************************** * enable_xinput2 */ static void enable_xinput2(void) @@ -254,9 +289,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, j, count; + int count; if (!xinput2_available) return; @@ -271,28 +306,31 @@ static void enable_xinput2(void) } } if (data->xi2_state == xi_unavailable) return; + if (!pXIGetClientPointer( data->display, None, &data->xi2_core_pointer )) return; + + 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 ); - if (!pXIGetClientPointer( data->display, None, &data->xi2_core_pointer )) return; - data->xi2_devices = devices = pXIQueryDevice( data->display, XIAllDevices, &data->xi2_device_count ); + data->xi2_devices = pXIQueryDevice( data->display, XIAllDevices, &data->xi2_device_count ); + data->xi2_current_slave = 0; 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 ); + data->xi2_state = xi_enabled; #endif } @@ -303,9 +341,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 +350,17 @@ static void disable_xinput2(void) mask.mask = NULL; mask.mask_len = 0; - - 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 ); + mask.deviceid = XIAllDevices; + + pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 ); + pXIFreeDeviceInfo( data->xi2_devices ); + free (data->x_rel_valuator); + free (data->y_rel_valuator); + data->x_rel_valuator = NULL; + data->y_rel_valuator = NULL; data->xi2_devices = NULL; - data->xi2_device_count = 0; + data->xi2_core_pointer = 0; + data->xi2_current_slave = 0; #endif } @@ -1616,6 +1651,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 +1675,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; + XIValuatorClassInfo *x_rel, *y_rel; + if (!thread_data->x_rel_valuator || !thread_data->y_rel_valuator) 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 == 0) + { + 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 +1715,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) - { - if (devices[i].deviceid != event->deviceid) continue; - for (j = 0; j < devices[i].num_classes; j++) - { - 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); - } - } + for (i = 0; i <= max ( x_rel->number, y_rel->number ); i++) + { + if (!XIMaskIsSet( event->valuators.mask, i )) continue; + val = *values++; + if (i == x_rel->number) + { + 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 +1807,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..2a3245e 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -327,7 +327,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; + void *x_rel_valuator; + void *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