From 766f7f6d1078d1ba706b1e571a66b27f064b6072 Mon Sep 17 00:00:00 2001
From: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
Date: Sun, 19 Apr 2009 15:15:43 +1000
Subject: [PATCH 3/3] Implement gdimouse handler, as best as I can

There's some important limitations here. Particularly, libXi
doesn't tell you if a mouse button was a press or a release
at the moment, so everything comes through as a press.

Also, at least for me, clicking in a window doesn't show events
while dragging nor the mouse release, although this is also an
XI2 problem.

Finally, grabs are not implemented in libXi, so exclusive mode
just doesn't work. I don't think the interface between modules
needs to be changed to support exclusive mode though, just the
code in winex11's X11DRV_dinput_MouseAcquire and
X11DRV_dinput_MouseUnacquire methods.

Also, I haven't done the test to confirm we have XInput2.h
available in configure yet.
---
 dlls/dinput/gdimouse.c    |   39 ++++++++++++++++++
 dlls/winex11.drv/dinput.c |   97 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 135 insertions(+), 1 deletions(-)

diff --git a/dlls/dinput/gdimouse.c b/dlls/dinput/gdimouse.c
index 4a0f3ee..f399a33 100644
--- a/dlls/dinput/gdimouse.c
+++ b/dlls/dinput/gdimouse.c
@@ -343,7 +343,46 @@ const struct dinput_device gdimouse_device = {
 static void gdimouse_callback(void *iface, DWORD instance, DWORD data)
 {
     GdiMouseImpl *This = (GdiMouseImpl *)iface;
+	int inst_id;
+	int value;
+
     TRACE("This %p, Instance %d, data %d\n", This, instance, data);
+
+	/* We can only handle 7 mouse buttons */
+	if (instance>WINE_MOUSE_BUTTONS_INSTANCE+7)
+		return;
+
+    EnterCriticalSection(&This->base.crit);
+	inst_id = DIDFT_MAKEINSTANCE(instance);
+	if (instance >= WINE_MOUSE_BUTTONS_INSTANCE)
+	{
+		inst_id |= DIDFT_PSHBUTTON;
+		value = data?0x80:0x0;
+		This->m_state.rgbButtons[instance-WINE_MOUSE_BUTTONS_INSTANCE] = value;
+	}
+	else
+	{
+		inst_id |= DIDFT_RELAXIS;
+		value = data;
+		switch(instance)
+		{
+		case WINE_MOUSE_X_AXIS_INSTANCE:
+			This->m_state.lX += value;
+			break;
+		case WINE_MOUSE_Y_AXIS_INSTANCE:
+			This->m_state.lY += value;
+			break;
+		case WINE_MOUSE_Z_AXIS_INSTANCE:
+			This->m_state.lZ += value;
+			break;
+		}
+	}
+	_dump_mouse_state(&This->m_state);
+	/* FIXME: Need to merge X and Y events with a single sequence number */
+	/* Change callback to X, Y, buttonmask? */
+	queue_event((LPDIRECTINPUTDEVICE8A)This, id_to_offset(&This->base.data_format, inst_id),
+		value, GetCurrentTime(), This->base.dinput->evsequence++);
+    LeaveCriticalSection(&This->base.crit);
 }
 
 /******************************************************************************
diff --git a/dlls/winex11.drv/dinput.c b/dlls/winex11.drv/dinput.c
index 0bf284e..a1c0d26 100644
--- a/dlls/winex11.drv/dinput.c
+++ b/dlls/winex11.drv/dinput.c
@@ -35,14 +35,22 @@ typedef void (*mousecallback)(void* iface, DWORD instance, DWORD data);
 #include <X11/Xlib.h>
 #include <X11/extensions/XInput2.h>
 
+/* These come from test_xi2.c in the xinput utility */
+#define BitIsOn(ptr, bit) (((BYTE *) (ptr))[(bit)>>3] & (1 << ((bit) & 7)))
+#define SetBit(ptr, bit)  (((BYTE *) (ptr))[(bit)>>3] |= (1 << ((bit) & 7)))
+
 /* XInput stuff */
 static void *xinput_handle;
 static BOOL xi2 = FALSE;
+static mousecallback inputcallback;
+static void* owner = NULL;
 
 #define MAKE_FUNCPTR(f) static typeof(f) * p##f;
 MAKE_FUNCPTR(XIQueryVersion)
 MAKE_FUNCPTR(XIQueryDevice)
+MAKE_FUNCPTR(XISelectEvent)
 MAKE_FUNCPTR(XIFreeDeviceInfo)
+MAKE_FUNCPTR(XIFreeEventData)
 #undef MAKE_FUNCPTR
 
 /* Wine mouse driver object instances */
@@ -54,6 +62,55 @@ MAKE_FUNCPTR(XIFreeDeviceInfo)
 /***********************************************************************
  * Mouse support functions
  */
+/* XGenericEvent handler */
+static void xge_handler(HWND hwnd, XEvent *event)
+{
+    XIEvent *xiev = (XIEvent *)event;
+    XIRawDeviceEvent *raw;
+    int button;
+
+    if (!inputcallback) goto cleanup;
+    switch (xiev->evtype)
+    {
+    case XI_RawEvent:
+        raw = (XIRawDeviceEvent *)xiev;
+        /* This is the wrong determiner, libxi isn't providing the
+           RawType wire field. */
+        switch(raw->detail)
+        {
+        case 0:
+            /* Motion */
+            if (BitIsOn(raw->valuators->mask, 0))
+                inputcallback( owner, WINE_MOUSE_X_AXIS_INSTANCE,
+                               raw->raw_values[0]);
+            if (BitIsOn(raw->valuators->mask, 1))
+                inputcallback( owner, WINE_MOUSE_Y_AXIS_INSTANCE,
+                               raw->raw_values[1]);
+            break;
+        default:
+            /* Button */
+            button = raw->detail - 1;
+            /* X counts left middle right, Win32 counts left right middle */
+            if (button == 1 || button == 2)
+                button ^= 0x3;
+            if (button == 3 || button == 4)
+            {   /* Scroll Wheel */
+                inputcallback( owner, WINE_MOUSE_Z_AXIS_INSTANCE,
+                               button == 0x3?1:-1 );
+            } else
+                /* We're missing the down/up information at the moment */
+                inputcallback( owner, WINE_MOUSE_BUTTONS_INSTANCE + button, 1 );
+        }
+        break;
+    default:
+        TRACE("EVENT type %d, expecting %d\n", xiev->evtype, XI_RawEvent);
+    }
+
+cleanup:
+    wine_tsx11_lock();
+    pXIFreeEventData(xiev);
+    wine_tsx11_unlock();
+}
 
 /***********************************************************************
  *              X11DRV_dinput_SystemMouse   (X11DRV.@)
@@ -65,7 +122,7 @@ BOOL CDECL X11DRV_dinput_SystemMouse(BOOL* keyboard, BOOL* mouse)
 {
     if (!xi2)
         return FALSE;
-    return FALSE;
+    return TRUE;
 }
 
 /***********************************************************************
@@ -77,11 +134,28 @@ BOOL CDECL X11DRV_dinput_SystemMouse(BOOL* keyboard, BOOL* mouse)
 VOID CDECL X11DRV_dinput_MouseAcquire(BOOL foreground, BOOL exclusive,
     mousecallback callback, void *object)
 {
+    struct x11drv_thread_data *data = x11drv_init_thread_data();
+    XIDeviceEventMask mask;
+
     if (!xi2)
     {
         ERR("XInput2 unavailable, this should not be called\n");
         return;
     }
+
+    owner = object;
+    inputcallback = callback;
+
+    /* VirtualCorePointer is always 2 */
+    mask.deviceid = 2;
+    mask.mask_len = 2;
+    mask.mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2);
+    SetBit(mask.mask, XI_RawEvent);
+    wine_tsx11_lock();
+    pXISelectEvent(data->display, DefaultRootWindow(data->display), &mask, 1);
+    wine_tsx11_unlock();
+    HeapFree(GetProcessHeap(), 0, mask.mask);
+
     TRACE("Acquired mouse in %s %s for %p\n",
         foreground?"foreground":"background",
         exclusive?"exclusively":"non-exclusively",
@@ -96,11 +170,26 @@ VOID CDECL X11DRV_dinput_MouseAcquire(BOOL foreground, BOOL exclusive,
 
 VOID CDECL X11DRV_dinput_MouseUnacquire()
 {
+    struct x11drv_thread_data *data = x11drv_init_thread_data();
+    XIDeviceEventMask mask;
     if (!xi2)
     {
         ERR("XInput2 unavailable, this should not be called\n");
         return;
     }
+
+    /* VirtualCorePointer is always 2 */
+    mask.deviceid = 2;
+    mask.mask_len = 2;
+    mask.mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 2);
+    wine_tsx11_lock();
+    pXISelectEvent(data->display, DefaultRootWindow(data->display), &mask, 1);
+    wine_tsx11_unlock();
+    HeapFree(GetProcessHeap(), 0, mask.mask);
+
+    inputcallback = NULL;
+    owner = NULL;
+
     TRACE("Unacquired mouse\n");
 }
 
@@ -121,7 +210,9 @@ void X11DRV_dinput_Init(Display* display)
 #define LOAD_FUNCPTR(f) if((p##f = wine_dlsym(xinput_handle, #f, NULL, 0)) == NULL) goto sym_not_found;
         LOAD_FUNCPTR(XIQueryVersion)
         LOAD_FUNCPTR(XIQueryDevice)
+        LOAD_FUNCPTR(XISelectEvent)
         LOAD_FUNCPTR(XIFreeDeviceInfo)
+        LOAD_FUNCPTR(XIFreeEventData)
 #undef LOAD_FUNCPTR
         wine_tsx11_lock();
         if (pXIQueryVersion(display, &major, &minor) == Success &&
@@ -129,6 +220,10 @@ void X11DRV_dinput_Init(Display* display)
             xi2 = TRUE;
         wine_tsx11_unlock();
     }
+    if (xi2)
+    {
+        X11DRV_register_event_handler(GenericEvent, xge_handler);
+    }
 sym_not_found:
     TRACE("XInput2 support: %s\n", xi2 ? "TRUE" : "FALSE");
     return;
-- 
1.6.2.2

