From: Derek Lesho Subject: Re: [PATCH v3 6/6] winex11.drv: Implement native mouse-movement raw-input using RawMotion. Message-Id: Date: Tue, 2 Jul 2019 20:50:54 -0400 In-Reply-To: <20190703004827.5960-1-dereklesho52@Gmail.com> References: <20190630022629.12152-6-dereklesho52@Gmail.com> <20190703004827.5960-1-dereklesho52@Gmail.com> I needed to add the default values for input.u.mi.d* to replace the XI_ButtonPress workaround, as that workaround broke native rawinput. On Tue, Jul 2, 2019 at 8:48 PM Derek Lesho wrote: > Signed-off-by: Derek Lesho > --- > dlls/winex11.drv/mouse.c | 101 ++++++++++++++++++++++++++------- > dlls/winex11.drv/x11drv.h | 6 +- > dlls/winex11.drv/x11drv_main.c | 10 ++++ > 3 files changed, 94 insertions(+), 23 deletions(-) > > diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c > index f737a306a5..8110bafb44 100644 > --- a/dlls/winex11.drv/mouse.c > +++ b/dlls/winex11.drv/mouse.c > @@ -284,11 +284,26 @@ static void update_relative_valuators(XIAnyClassInfo > **valuators, int n_valuator > } > #endif > > +/*********************************************************************** > + * inform_wineserver > + */ > +static void inform_wineserver(void) > +{ > + static int once = 0; > + if (!once) > + { > + RAWINPUT raw_input; > + raw_input.header.dwType = RIM_ENABLE_NATIVE_MOUSE_MOVE; > + __wine_send_raw_input(&raw_input); > + once = 1; > + } > +} > + > > /*********************************************************************** > - * enable_xinput2 > + * X11DRV_XInput2_Enable > */ > -static void enable_xinput2(void) > +void X11DRV_XInput2_Enable(void) > { > #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H > struct x11drv_thread_data *data = x11drv_thread_data(); > @@ -318,7 +333,6 @@ static void enable_xinput2(void) > memset( mask_bits, 0, sizeof(mask_bits) ); > XISetMask( mask_bits, XI_DeviceChanged ); > XISetMask( mask_bits, XI_RawMotion ); > - XISetMask( mask_bits, XI_ButtonPress ); > > pXISelectEvents( data->display, DefaultRootWindow( data->display ), > &mask, 1 ); > > @@ -337,19 +351,21 @@ static void enable_xinput2(void) > data->xi2_current_slave = 0; > > data->xi2_state = xi_enabled; > + > + inform_wineserver(); > #endif > } > > /*********************************************************************** > - * disable_xinput2 > + * X11DRV_XInput2_Disable > */ > -static void disable_xinput2(void) > +void X11DRV_XInput2_Disable(void) > { > #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H > struct x11drv_thread_data *data = x11drv_thread_data(); > XIEventMask mask; > > - if (data->xi2_state != xi_enabled) return; > + if (data->xi2_state < xi_enabled) return; > > TRACE( "disabling\n" ); > data->xi2_state = xi_disabled; > @@ -368,6 +384,21 @@ static void disable_xinput2(void) > #endif > } > > +static void use_xinput2_path(void) > +{ > + struct x11drv_thread_data *thread_data = x11drv_thread_data(); > + > + if (thread_data->xi2_state == xi_enabled) > + thread_data->xi2_state = xi_extra; > +} > + > +static void disable_xinput2_path(void) > +{ > + struct x11drv_thread_data *thread_data = x11drv_thread_data(); > + > + if (thread_data->xi2_state == xi_extra) > + thread_data->xi2_state = xi_enabled; > +} > > /*********************************************************************** > * grab_clipping_window > @@ -393,9 +424,9 @@ static BOOL grab_clipping_window( const RECT *clip ) > return TRUE; > > /* enable XInput2 unless we are already clipping */ > - if (!data->clip_hwnd) enable_xinput2(); > + if (!data->clip_hwnd) use_xinput2_path(); > > - if (data->xi2_state != xi_enabled) > + if (data->xi2_state < xi_extra) > { > WARN( "XInput2 not supported, refusing to clip to %s\n", > wine_dbgstr_rect(clip) ); > DestroyWindow( msg_hwnd ); > @@ -423,7 +454,7 @@ static BOOL grab_clipping_window( const RECT *clip ) > > if (!clipping_cursor) > { > - disable_xinput2(); > + disable_xinput2_path(); > DestroyWindow( msg_hwnd ); > return FALSE; > } > @@ -489,7 +520,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND > new_clip_hwnd ) > TRACE( "clip hwnd reset from %p\n", hwnd ); > data->clip_hwnd = 0; > data->clip_reset = GetTickCount(); > - disable_xinput2(); > + disable_xinput2_path(); > DestroyWindow( hwnd ); > } > else if (hwnd == GetForegroundWindow()) /* request to clip */ > @@ -1724,16 +1755,18 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie > *xev ) > { > XIRawEvent *event = xev->data; > const double *values = event->valuators.values; > + const double *raw_values = event->raw_values; > RECT virtual_rect; > INPUT input; > + RAWINPUT raw_input; > int i; > - double dx = 0, dy = 0, val; > + double dx = 0, dy = 0, raw_dx = 0, raw_dy = 0, val, raw_val; > struct x11drv_thread_data *thread_data = x11drv_thread_data(); > 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 (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 > @@ -1758,25 +1791,25 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie > *xev ) > 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 ); > - input.u.mi.dwExtraInfo = 0; > - input.u.mi.dx = 0; > - input.u.mi.dy = 0; > - > virtual_rect = get_virtual_screen_rect(); > > + /* sometimes we don't get X and Y valuators */ > + input.u.mi.dx = 0; > + input.u.mi.dy = 0; > + > for (i = 0; i <= max ( x_rel->number, y_rel->number ); i++) > { > if (!XIMaskIsSet( event->valuators.mask, i )) continue; > val = *values++; > + raw_val = *raw_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); > + > + raw_dx = raw_val; > } > if (i == y_rel->number) > { > @@ -1784,6 +1817,8 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie > *xev ) > if (y_rel->min < y_rel->max) > input.u.mi.dy = val * (virtual_rect.bottom - > virtual_rect.top) > / (y_rel->max - y_rel->min); > + > + raw_dy = raw_val; > } > } > > @@ -1793,10 +1828,32 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie > *xev ) > return FALSE; > } > > - TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, > dy ); > + raw_input.data.mouse.lLastX = raw_dx; > + raw_input.data.mouse.lLastY = raw_dy; > + raw_input.data.mouse.u.usButtonFlags = 0; > + raw_input.data.mouse.u.usButtonData = 0; > + raw_input.data.mouse.ulExtraInformation = 0; > + > + TRACE("raw event %f,%f\n", raw_dx, raw_dy); > + > + raw_input.header.dwType = RIM_TYPEMOUSE; > + > + if ( LIST_ENTRY((&g_x11_threads)->next, struct x11drv_thread_data, > entry) == thread_data ) > + __wine_send_raw_input( &raw_input ); > + > + if (thread_data->xi2_state == xi_extra) > + { > + input.u.mi.mouseData = 0; > + input.u.mi.dwFlags = MOUSEEVENTF_MOVE; > + input.u.mi.time = EVENT_x11_time_to_win32_time( > event->time ); > + input.u.mi.dwExtraInfo = 0; > + > + TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, > dx, dy ); > + > + input.type = INPUT_MOUSE; > + __wine_send_input( 0, &input ); > + } > > - input.type = INPUT_MOUSE; > - __wine_send_input( 0, &input ); > return TRUE; > } > > diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h > index a0308b0675..378c1d7508 100644 > --- a/dlls/winex11.drv/x11drv.h > +++ b/dlls/winex11.drv/x11drv.h > @@ -194,6 +194,8 @@ extern BOOL X11DRV_UnrealizePalette( HPALETTE hpal ) > DECLSPEC_HIDDEN; > > extern void X11DRV_Xcursor_Init(void) DECLSPEC_HIDDEN; > extern void X11DRV_XInput2_Init(void) DECLSPEC_HIDDEN; > +extern void X11DRV_XInput2_Enable(void) DECLSPEC_HIDDEN; > +extern void X11DRV_XInput2_Disable(void) DECLSPEC_HIDDEN; > > extern DWORD copy_image_bits( BITMAPINFO *info, BOOL is_r8g8b8, XImage > *image, > const struct gdi_image_bits *src_bits, > struct gdi_image_bits *dst_bits, > @@ -335,14 +337,16 @@ struct x11drv_thread_data > HWND clip_hwnd; /* message window stored in desktop > while clipping is active */ > DWORD clip_reset; /* time when clipping was last reset */ > HKL kbd_layout; /* active keyboard layout */ > - enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled } > xi2_state; /* XInput2 state */ > + enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled, > xi_extra } 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 */ > + struct list entry; /* Entry in global list of setup X11 > threads */ > }; > +extern struct list g_x11_threads DECLSPEC_HIDDEN; /* Global list of setup > X11 threads */ > > extern struct x11drv_thread_data *x11drv_init_thread_data(void) > DECLSPEC_HIDDEN; > extern DWORD thread_data_tls_index DECLSPEC_HIDDEN; > diff --git a/dlls/winex11.drv/x11drv_main.c > b/dlls/winex11.drv/x11drv_main.c > index e67a3c05a9..685b3dd0f4 100644 > --- a/dlls/winex11.drv/x11drv_main.c > +++ b/dlls/winex11.drv/x11drv_main.c > @@ -601,6 +601,9 @@ static BOOL process_attach(void) > } > > > +struct list g_x11_threads = LIST_INIT( g_x11_threads ); > + > + > /*********************************************************************** > * ThreadDetach (X11DRV.@) > */ > @@ -610,6 +613,9 @@ void CDECL X11DRV_ThreadDetach(void) > > if (data) > { > + list_remove( &data->entry ); > + X11DRV_XInput2_Disable(); > + > if (data->xim) XCloseIM( data->xim ); > if (data->font_set) XFreeFontSet( data->display, data->font_set ); > XCloseDisplay( data->display ); > @@ -680,6 +686,10 @@ struct x11drv_thread_data > *x11drv_init_thread_data(void) > > if (use_xim) X11DRV_SetupXIM(); > > + X11DRV_XInput2_Enable(); > + > + list_add_tail( &g_x11_threads, &data->entry ); > + > return data; > } > > -- > 2.21.0 > >
I needed to add the default values for input.u.mi.d* to replace the XI_ButtonPress workaround, as that workaround broke native rawinput.

On Tue, Jul 2, 2019 at 8:48 PM Derek Lesho <dereklesho52@gmail.com> wrote:
Signed-off-by: Derek Lesho <dereklesho52@Gmail.com>
---
 dlls/winex11.drv/mouse.c       | 101 ++++++++++++++++++++++++++-------
 dlls/winex11.drv/x11drv.h      |   6 +-
 dlls/winex11.drv/x11drv_main.c |  10 ++++
 3 files changed, 94 insertions(+), 23 deletions(-)

diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index f737a306a5..8110bafb44 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -284,11 +284,26 @@ static void update_relative_valuators(XIAnyClassInfo **valuators, int n_valuator
 }
 #endif

+/***********************************************************************
+ *              inform_wineserver
+ */
+static void inform_wineserver(void)
+{
+    static int once = 0;
+    if (!once)
+    {
+        RAWINPUT raw_input;
+        raw_input.header.dwType = RIM_ENABLE_NATIVE_MOUSE_MOVE;
+        __wine_send_raw_input(&raw_input);
+        once = 1;
+    }
+}
+

 /***********************************************************************
- *              enable_xinput2
+ *              X11DRV_XInput2_Enable
  */
-static void enable_xinput2(void)
+void X11DRV_XInput2_Enable(void)
 {
 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
     struct x11drv_thread_data *data = x11drv_thread_data();
@@ -318,7 +333,6 @@ static void enable_xinput2(void)
     memset( mask_bits, 0, sizeof(mask_bits) );
     XISetMask( mask_bits, XI_DeviceChanged );
     XISetMask( mask_bits, XI_RawMotion );
-    XISetMask( mask_bits, XI_ButtonPress );

     pXISelectEvents( data->display, DefaultRootWindow( data->display ), &mask, 1 );

@@ -337,19 +351,21 @@ static void enable_xinput2(void)
     data->xi2_current_slave = 0;

     data->xi2_state = xi_enabled;
+
+    inform_wineserver();
 #endif
 }

 /***********************************************************************
- *              disable_xinput2
+ *              X11DRV_XInput2_Disable
  */
-static void disable_xinput2(void)
+void X11DRV_XInput2_Disable(void)
 {
 #ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
     struct x11drv_thread_data *data = x11drv_thread_data();
     XIEventMask mask;

-    if (data->xi2_state != xi_enabled) return;
+    if (data->xi2_state < xi_enabled) return;

     TRACE( "disabling\n" );
     data->xi2_state = xi_disabled;
@@ -368,6 +384,21 @@ static void disable_xinput2(void)
 #endif
 }

+static void use_xinput2_path(void)
+{
+    struct x11drv_thread_data *thread_data = x11drv_thread_data();
+
+    if (thread_data->xi2_state == xi_enabled)
+        thread_data->xi2_state = xi_extra;
+}
+
+static void disable_xinput2_path(void)
+{
+    struct x11drv_thread_data *thread_data = x11drv_thread_data();
+
+    if (thread_data->xi2_state == xi_extra)
+        thread_data->xi2_state = xi_enabled;
+}

 /***********************************************************************
  *             grab_clipping_window
@@ -393,9 +424,9 @@ static BOOL grab_clipping_window( const RECT *clip )
         return TRUE;

     /* enable XInput2 unless we are already clipping */
-    if (!data->clip_hwnd) enable_xinput2();
+    if (!data->clip_hwnd) use_xinput2_path();

-    if (data->xi2_state != xi_enabled)
+    if (data->xi2_state < xi_extra)
     {
         WARN( "XInput2 not supported, refusing to clip to %s\n", wine_dbgstr_rect(clip) );
         DestroyWindow( msg_hwnd );
@@ -423,7 +454,7 @@ static BOOL grab_clipping_window( const RECT *clip )

     if (!clipping_cursor)
     {
-        disable_xinput2();
+        disable_xinput2_path();
         DestroyWindow( msg_hwnd );
         return FALSE;
     }
@@ -489,7 +520,7 @@ LRESULT clip_cursor_notify( HWND hwnd, HWND new_clip_hwnd )
         TRACE( "clip hwnd reset from %p\n", hwnd );
         data->clip_hwnd = 0;
         data->clip_reset = GetTickCount();
-        disable_xinput2();
+        disable_xinput2_path();
         DestroyWindow( hwnd );
     }
     else if (hwnd == GetForegroundWindow())  /* request to clip */
@@ -1724,16 +1755,18 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
 {
     XIRawEvent *event = xev->data;
     const double *values = event->valuators.values;
+    const double *raw_values = event->raw_values;
     RECT virtual_rect;
     INPUT input;
+    RAWINPUT raw_input;
     int i;
-    double dx = 0, dy = 0, val;
+    double dx = 0, dy = 0, raw_dx = 0, raw_dy = 0, val, raw_val;
     struct x11drv_thread_data *thread_data = x11drv_thread_data();
     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 (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
@@ -1758,25 +1791,25 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
     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 );
-    input.u.mi.dwExtraInfo = 0;
-    input.u.mi.dx          = 0;
-    input.u.mi.dy          = 0;
-
     virtual_rect = get_virtual_screen_rect();

+    /* sometimes we don't get X and Y valuators */
+    input.u.mi.dx = 0;
+    input.u.mi.dy = 0;
+
     for (i = 0; i <= max ( x_rel->number, y_rel->number ); i++)
     {
         if (!XIMaskIsSet( event->valuators.mask, i )) continue;
         val = *values++;
+        raw_val = *raw_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);
+
+            raw_dx = raw_val;
         }
         if (i == y_rel->number)
         {
@@ -1784,6 +1817,8 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
             if (y_rel->min < y_rel->max)
                 input.u.mi.dy = val * (virtual_rect.bottom - virtual_rect.top)
                                     / (y_rel->max - y_rel->min);
+
+            raw_dy = raw_val;
         }
     }

@@ -1793,10 +1828,32 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
         return FALSE;
     }

-    TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy );
+    raw_input.data.mouse.lLastX = raw_dx;
+    raw_input.data.mouse.lLastY = raw_dy;
+    raw_input.data.mouse.u.usButtonFlags = 0;
+    raw_input.data.mouse.u.usButtonData = 0;
+    raw_input.data.mouse.ulExtraInformation = 0;
+
+    TRACE("raw event %f,%f\n",  raw_dx, raw_dy);
+
+    raw_input.header.dwType = RIM_TYPEMOUSE;
+
+    if ( LIST_ENTRY((&g_x11_threads)->next, struct x11drv_thread_data, entry) == thread_data )
+        __wine_send_raw_input( &raw_input );
+
+    if (thread_data->xi2_state == xi_extra)
+    {
+        input.u.mi.mouseData   = 0;
+        input.u.mi.dwFlags     = MOUSEEVENTF_MOVE;
+        input.u.mi.time        = EVENT_x11_time_to_win32_time( event->time );
+        input.u.mi.dwExtraInfo = 0;
+
+        TRACE( "pos %d,%d (event %f,%f)\n", input.u.mi.dx, input.u.mi.dy, dx, dy );
+
+        input.type = INPUT_MOUSE;
+        __wine_send_input( 0, &input );
+    }

-    input.type = INPUT_MOUSE;
-    __wine_send_input( 0, &input );
     return TRUE;
 }

diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index a0308b0675..378c1d7508 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -194,6 +194,8 @@ extern BOOL X11DRV_UnrealizePalette( HPALETTE hpal ) DECLSPEC_HIDDEN;

 extern void X11DRV_Xcursor_Init(void) DECLSPEC_HIDDEN;
 extern void X11DRV_XInput2_Init(void) DECLSPEC_HIDDEN;
+extern void X11DRV_XInput2_Enable(void) DECLSPEC_HIDDEN;
+extern void X11DRV_XInput2_Disable(void) DECLSPEC_HIDDEN;

 extern DWORD copy_image_bits( BITMAPINFO *info, BOOL is_r8g8b8, XImage *image,
                               const struct gdi_image_bits *src_bits, struct gdi_image_bits *dst_bits,
@@ -335,14 +337,16 @@ struct x11drv_thread_data
     HWND     clip_hwnd;            /* message window stored in desktop while clipping is active */
     DWORD    clip_reset;           /* time when clipping was last reset */
     HKL      kbd_layout;           /* active keyboard layout */
-    enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled } xi2_state; /* XInput2 state */
+    enum { xi_unavailable = -1, xi_unknown, xi_disabled, xi_enabled, xi_extra } 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 */
+    struct   list entry;           /* Entry in global list of setup X11 threads */
 };
+extern struct list g_x11_threads DECLSPEC_HIDDEN; /* Global list of setup X11 threads */

 extern struct x11drv_thread_data *x11drv_init_thread_data(void) DECLSPEC_HIDDEN;
 extern DWORD thread_data_tls_index DECLSPEC_HIDDEN;
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
index e67a3c05a9..685b3dd0f4 100644
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -601,6 +601,9 @@ static BOOL process_attach(void)
 }


+struct list g_x11_threads = LIST_INIT( g_x11_threads );
+
+
 /***********************************************************************
  *           ThreadDetach (X11DRV.@)
  */
@@ -610,6 +613,9 @@ void CDECL X11DRV_ThreadDetach(void)

     if (data)
     {
+        list_remove( &data->entry );
+        X11DRV_XInput2_Disable();
+
         if (data->xim) XCloseIM( data->xim );
         if (data->font_set) XFreeFontSet( data->display, data->font_set );
         XCloseDisplay( data->display );
@@ -680,6 +686,10 @@ struct x11drv_thread_data *x11drv_init_thread_data(void)

     if (use_xim) X11DRV_SetupXIM();

+    X11DRV_XInput2_Enable();
+
+    list_add_tail( &g_x11_threads, &data->entry );
+
     return data;
 }

--
2.21.0