From e5301a0593e91ca79757c8f2dc23c8fa63f22072 Mon Sep 17 00:00:00 2001
From: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
Date: Sat, 18 Apr 2009 22:20:35 +1000
Subject: [PATCH 2/3] gdimouse framework in place

---
 dlls/dinput/Makefile.in           |    1 +
 dlls/dinput/dinput_main.c         |    3 +
 dlls/dinput/dinput_private.h      |    4 +
 dlls/dinput/gdimouse.c            |  670 +++++++++++++++++++++++++++++++++++++
 dlls/winex11.drv/Makefile.in      |    1 +
 dlls/winex11.drv/dinput.c         |  185 ++++++++++
 dlls/winex11.drv/winex11.drv.spec |    5 +
 dlls/winex11.drv/x11drv.h         |    1 +
 dlls/winex11.drv/x11drv_main.c    |    4 +
 9 files changed, 874 insertions(+), 0 deletions(-)
 create mode 100644 dlls/dinput/gdimouse.c
 create mode 100644 dlls/winex11.drv/dinput.c

diff --git a/dlls/dinput/Makefile.in b/dlls/dinput/Makefile.in
index 16142ef..fa77e9c 100644
--- a/dlls/dinput/Makefile.in
+++ b/dlls/dinput/Makefile.in
@@ -12,6 +12,7 @@ C_SRCS = \
 	device.c \
 	dinput_main.c \
 	effect_linuxinput.c \
+	gdimouse.c \
 	joystick.c \
 	joystick_linux.c \
 	joystick_linuxinput.c \
diff --git a/dlls/dinput/dinput_main.c b/dlls/dinput/dinput_main.c
index 3986fb2..f16828c 100644
--- a/dlls/dinput/dinput_main.c
+++ b/dlls/dinput/dinput_main.c
@@ -77,6 +77,7 @@ static inline IDirectInput7W *IDirectInput7W_from_impl( IDirectInputImpl *iface
 /* Mouse type devices */
 static const struct dinput_device *dinput_mice[] =
 {
+    &gdimouse_device,
     &mouse_device
 };
 #define NB_DINPUT_MICE (sizeof(dinput_mice)/sizeof(dinput_mice[0]))
@@ -106,8 +107,10 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserv)
       case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls(inst);
         DINPUT_instance = inst;
+        gdimouse_init();
         break;
       case DLL_PROCESS_DETACH:
+        gdimouse_uninit();
         break;
     }
     return TRUE;
diff --git a/dlls/dinput/dinput_private.h b/dlls/dinput/dinput_private.h
index ddb9aa0..71b03fc 100644
--- a/dlls/dinput/dinput_private.h
+++ b/dlls/dinput/dinput_private.h
@@ -59,10 +59,14 @@ extern const struct dinput_device keyboard_device;
 extern const struct dinput_device joystick_linux_device;
 extern const struct dinput_device joystick_linuxinput_device;
 extern const struct dinput_device joystick_osx_device;
+extern const struct dinput_device gdimouse_device;
 
 extern void check_dinput_hooks(LPDIRECTINPUTDEVICE8A);
 typedef int (*DI_EVENT_PROC)(LPDIRECTINPUTDEVICE8A, WPARAM, LPARAM);
 
 extern void _dump_diactionformatA(LPDIACTIONFORMATA);
 
+extern void gdimouse_init();
+extern void gdimouse_uninit();
+
 #endif /* __WINE_DLLS_DINPUT_DINPUT_PRIVATE_H */
diff --git a/dlls/dinput/gdimouse.c b/dlls/dinput/gdimouse.c
new file mode 100644
index 0000000..4a0f3ee
--- /dev/null
+++ b/dlls/dinput/gdimouse.c
@@ -0,0 +1,670 @@
+/*                Graphics Device-backed DirectInput Mouse device
+ *
+ * Copyright 2009 Paul "TBBle" Hampson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "dinput.h"
+
+#include "dinput_private.h"
+#include "device_private.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dinput);
+
+/* Wine mouse driver object instances */
+#define WINE_MOUSE_X_AXIS_INSTANCE   0
+#define WINE_MOUSE_Y_AXIS_INSTANCE   1
+#define WINE_MOUSE_Z_AXIS_INSTANCE   2
+#define WINE_MOUSE_BUTTONS_INSTANCE  3
+
+static IDirectInputDevice8AVtbl GdiMouseAvt;
+static IDirectInputDevice8WVtbl GdiMouseWvt;
+
+typedef struct GdiMouseImpl GdiMouseImpl;
+
+struct GdiMouseImpl
+{
+    struct IDirectInputDevice2AImpl base;
+
+    /* GdiMouseAImpl */
+    /* This is for mouse reporting. */
+    DIMOUSESTATE2                   m_state;
+};
+
+static void gdimouse_callback(void *iface, DWORD instance, DWORD data);
+
+struct graphics_driver
+{
+    HMODULE module;
+    /* Function Pointers */
+    BOOL (*pSystemMouse)();
+    VOID (*pMouseAcquire)(BOOL foreground, BOOL exclusive,
+		typeof(gdimouse_callback) *callback, void *object);
+    VOID (*pMouseUnacquire)();
+};
+
+static struct graphics_driver *display_driver = NULL;
+
+void gdimouse_init()
+{
+    char buffer[MAX_PATH], libname[32], *name, *next;
+    HMODULE module = 0;
+    HKEY hkey;
+
+    if (display_driver)  /* already loaded */
+    {
+        ERR("display_driver already loaded\n");
+        return;
+    }
+
+    strcpy(buffer, "x11");  /* default value */
+    /* @@ Wine registry key: HKCU\Software\Wine\Drivers */
+    if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Drivers", &hkey))
+    {
+        DWORD type, count = sizeof(buffer);
+        RegQueryValueExA(hkey, "Graphics", 0, &type, (LPBYTE) buffer, &count);
+        RegCloseKey(hkey);
+    }
+
+    name = buffer;
+    while (name)
+    {
+        next = strchr(name, ',');
+        if (next) *next++ = 0;
+
+        snprintf(libname, sizeof(libname), "wine%s.drv", name);
+        if ((module = LoadLibraryA(libname)) != 0) break;
+        name = next;
+    }
+
+    display_driver = HeapAlloc(GetProcessHeap(), 0, sizeof(*display_driver));
+    display_driver->module = module;
+    display_driver->pSystemMouse = NULL;
+    display_driver->pMouseAcquire = NULL;
+    display_driver->pMouseUnacquire = NULL;
+
+    if (module)
+    {
+#define LOAD_FUNCPTR(f) if((display_driver->p##f = (void*)GetProcAddress(module, "wine_directinput_" #f)) == NULL) goto sym_not_found;
+        LOAD_FUNCPTR(MouseAcquire);
+        LOAD_FUNCPTR(MouseUnacquire);
+        /* SystemMouse !NULL implies all function pointers are !NULL */
+        LOAD_FUNCPTR(SystemMouse);
+#undef LOAD_FUNCPTR
+    }
+    sym_not_found:
+
+    if (display_driver->pSystemMouse && display_driver->pSystemMouse())
+    {
+        TRACE("Graphics driver provides Direct Input mouse support\n");
+        return;
+    }
+
+    TRACE("Hook thread emulates Direct Input mouse support\n");
+}
+
+void gdimouse_uninit()
+{
+    if (display_driver)
+    {
+        HeapFree(GetProcessHeap(), 0, display_driver);
+        display_driver = NULL;
+    }
+}
+
+const GUID DInput_GDI_Mouse_GUID = { /* 9e573ed8-7734-11d2-8d4a-23903fb6bdf7 */
+    0x9e573ed8, 0x7734, 0x11d2, {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
+};
+
+static void _dump_mouse_state(DIMOUSESTATE2 *m_state)
+{
+    int i;
+
+    if (!TRACE_ON(dinput)) return;
+
+    TRACE("(X: %d Y: %d Z: %d", m_state->lX, m_state->lY, m_state->lZ);
+    for (i = 0; i < 8; i++) TRACE(" B%d: %02x", i, m_state->rgbButtons[i]);
+    TRACE(")\n");
+}
+
+static void fill_gdimouse_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version) {
+    DWORD dwSize;
+    DIDEVICEINSTANCEA ddi;
+    
+    dwSize = lpddi->dwSize;
+
+    TRACE("%d %p\n", dwSize, lpddi);
+    
+    memset(lpddi, 0, dwSize);
+    memset(&ddi, 0, sizeof(ddi));
+
+    ddi.dwSize = dwSize;
+    ddi.guidInstance = GUID_SysMouse;/* DInput's GUID */
+    ddi.guidProduct = DInput_GDI_Mouse_GUID; /* Vendor's GUID */
+    if (version >= 0x0800)
+        ddi.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
+    else
+        ddi.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
+    strcpy(ddi.tszInstanceName, "Mouse");
+    strcpy(ddi.tszProductName, "GDI Wine Mouse");
+
+    memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
+}
+
+static void fill_gdimouse_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version) {
+    DWORD dwSize;
+    DIDEVICEINSTANCEW ddi;
+    
+    dwSize = lpddi->dwSize;
+
+    TRACE("%d %p\n", dwSize, lpddi);
+    
+    memset(lpddi, 0, dwSize);
+    memset(&ddi, 0, sizeof(ddi));
+
+    ddi.dwSize = dwSize;
+    ddi.guidInstance = GUID_SysMouse;/* DInput's GUID */
+    ddi.guidProduct = DInput_GDI_Mouse_GUID; /* Vendor's GUID */
+    if (version >= 0x0800)
+        ddi.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
+    else
+        ddi.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
+    MultiByteToWideChar(CP_ACP, 0, "Mouse", -1, ddi.tszInstanceName, MAX_PATH);
+    MultiByteToWideChar(CP_ACP, 0, "GDI Wine Mouse", -1, ddi.tszProductName, MAX_PATH);
+
+    memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
+}
+
+static BOOL gdimousedev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
+{
+    if (id != 0)
+        return FALSE;
+
+    if (!display_driver->pSystemMouse || ! display_driver->pSystemMouse())
+        return FALSE;
+
+    if ((dwDevType == 0) ||
+        ((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
+        (((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
+        TRACE("Enumerating the mouse device\n");
+        
+        fill_gdimouse_dideviceinstanceA(lpddi, version);
+        
+        return TRUE;
+    }
+    
+    return FALSE;
+}
+
+static BOOL gdimousedev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
+{
+    if (id != 0)
+        return FALSE;
+
+    if (!display_driver->pSystemMouse || ! display_driver->pSystemMouse())
+        return FALSE;
+
+    if ((dwDevType == 0) ||
+        ((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
+        (((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
+        TRACE("Enumerating the mouse device\n");
+        
+        fill_gdimouse_dideviceinstanceW(lpddi, version);
+        
+        return TRUE;
+    }
+    
+    return FALSE;
+}
+
+static GdiMouseImpl *alloc_device(REFGUID rguid, const void *mvt, IDirectInputImpl *dinput)
+{
+    GdiMouseImpl* newDevice;
+    LPDIDATAFORMAT df = NULL;
+    unsigned i;
+
+    newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(GdiMouseImpl));
+    if (!newDevice) return NULL;
+    newDevice->base.lpVtbl = mvt;
+    newDevice->base.ref = 1;
+    newDevice->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
+    newDevice->base.guid = *rguid;
+    InitializeCriticalSection(&newDevice->base.crit);
+    newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": GdiMouseImpl*->base.crit");
+    newDevice->base.dinput = dinput;
+
+    /* Create copy of default data format */
+    if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIMouse2.dwSize))) goto failed;
+    memcpy(df, &c_dfDIMouse2, c_dfDIMouse2.dwSize);
+    if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
+    memcpy(df->rgodf, c_dfDIMouse2.rgodf, df->dwNumObjs * df->dwObjSize);
+
+    /* Because we don't do any detection yet just modify instance and type */
+    for (i = 0; i < df->dwNumObjs; i++)
+        if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_AXIS)
+            df->rgodf[i].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_RELAXIS;
+        else
+            df->rgodf[i].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
+
+    newDevice->base.data_format.wine_df = df;
+    IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->base.dinput);
+    return newDevice;
+
+failed:
+    if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
+    HeapFree(GetProcessHeap(), 0, df);
+    HeapFree(GetProcessHeap(), 0, newDevice);
+    return NULL;
+}
+
+static HRESULT gdimousedev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
+{
+    if (!display_driver->pSystemMouse || ! display_driver->pSystemMouse())
+        return DIERR_DEVICENOTREG;
+
+    if ((IsEqualGUID(&GUID_SysMouse,rguid)) ||          /* Generic Mouse */
+        (IsEqualGUID(&DInput_GDI_Mouse_GUID,rguid))) {  /* GDI Mouse */
+        if ((riid == NULL) ||
+            IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
+            *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &GdiMouseAvt, dinput);
+            TRACE("Creating a Mouse device (%p)\n", *pdev);
+            if (!*pdev) return DIERR_OUTOFMEMORY;
+            return DI_OK;
+        } else
+            return DIERR_NOINTERFACE;
+    }
+    
+    return DIERR_DEVICENOTREG;
+}
+
+static HRESULT gdimousedev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
+{
+    if (!display_driver->pSystemMouse || ! display_driver->pSystemMouse())
+        return DIERR_DEVICENOTREG;
+
+    if ((IsEqualGUID(&GUID_SysMouse,rguid)) ||          /* Generic Mouse */
+        (IsEqualGUID(&DInput_GDI_Mouse_GUID,rguid))) {  /* GDI Mouse */
+        if ((riid == NULL) ||
+            IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
+            IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
+            *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &GdiMouseWvt, dinput);
+            TRACE("Creating a Mouse device (%p)\n", *pdev);
+            if (!*pdev) return DIERR_OUTOFMEMORY;
+            return DI_OK;
+        } else
+            return DIERR_NOINTERFACE;
+    }
+    
+    return DIERR_DEVICENOTREG;
+}
+
+const struct dinput_device gdimouse_device = {
+    "Wine GDI mouse driver",
+    gdimousedev_enum_deviceA,
+    gdimousedev_enum_deviceW,
+    gdimousedev_create_deviceA,
+    gdimousedev_create_deviceW
+};
+
+/******************************************************************************
+ *    GdiMouseA (DInput Mouse support from graphics driver)
+ */
+
+/* callback hook from the display driver */
+static void gdimouse_callback(void *iface, DWORD instance, DWORD data)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    TRACE("This %p, Instance %d, data %d\n", This, instance, data);
+}
+
+/******************************************************************************
+  *     Acquire : gets exclusive control of the mouse
+  */
+static HRESULT WINAPI GdiMouseAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    HRESULT res;
+    
+    TRACE("(this=%p)\n",This);
+
+    /* No exclusive support yet */
+    if (This->base.dwCoopLevel & DISCL_EXCLUSIVE)
+        return S_FALSE;
+
+    if ((res = IDirectInputDevice2AImpl_Acquire(iface)) != DI_OK) return res;
+
+    /* Init the mouse state */
+    This->m_state.lX = 0;
+    This->m_state.lY = 0;
+    This->m_state.lZ = 0;
+    This->m_state.rgbButtons[0] = GetKeyState(VK_LBUTTON) & 0x80;
+    This->m_state.rgbButtons[1] = GetKeyState(VK_RBUTTON) & 0x80;
+    This->m_state.rgbButtons[2] = GetKeyState(VK_MBUTTON) & 0x80;
+
+    display_driver->pMouseAcquire(This->base.dwCoopLevel & DISCL_FOREGROUND,
+        This->base.dwCoopLevel & DISCL_EXCLUSIVE, gdimouse_callback, This);
+    
+    return DI_OK;
+}
+
+/******************************************************************************
+  *     Unacquire : frees the mouse
+  */
+static HRESULT WINAPI GdiMouseAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    HRESULT res;
+    
+    TRACE("(this=%p)\n",This);
+
+    if ((res = IDirectInputDevice2AImpl_Unacquire(iface)) != DI_OK) return res;
+
+    display_driver->pMouseUnacquire();
+
+    return DI_OK;
+}
+/******************************************************************************
+  *     GetDeviceState : returns the "state" of the mouse.
+  *
+  */
+static HRESULT WINAPI GdiMouseAImpl_GetDeviceState(
+        LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
+) {
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+
+    if(This->base.acquired == 0) return DIERR_NOTACQUIRED;
+
+    TRACE("(this=%p,0x%08x,%p):\n", This, len, ptr);
+    _dump_mouse_state(&This->m_state);
+
+    EnterCriticalSection(&This->base.crit);
+    /* Copy the current mouse state */
+    fill_DataFormat(ptr, len, &This->m_state, &This->base.data_format);
+
+    /* Initialize the buffer when in relative mode */
+    if (!(This->base.data_format.user_df->dwFlags & DIDF_ABSAXIS))
+    {
+        This->m_state.lX = 0;
+        This->m_state.lY = 0;
+        This->m_state.lZ = 0;
+    }
+    LeaveCriticalSection(&This->base.crit);
+
+    return DI_OK;
+}
+
+/******************************************************************************
+  *     GetProperty : get input device properties
+  */
+static HRESULT WINAPI GdiMouseAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
+                                                REFGUID rguid,
+                                                LPDIPROPHEADER pdiph)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    
+    TRACE("(%p) %s,%p\n", This, debugstr_guid(rguid), pdiph);
+    _dump_DIPROPHEADER(pdiph);
+    
+    if (!HIWORD(rguid)) {
+        switch (LOWORD(rguid)) {
+            case (DWORD_PTR) DIPROP_GRANULARITY: {
+                LPDIPROPDWORD pr = (LPDIPROPDWORD) pdiph;
+                
+                /* We'll just assume that the app asks about the Z axis */
+                pr->dwData = WHEEL_DELTA;
+                
+                break;
+            }
+              
+            case (DWORD_PTR) DIPROP_RANGE: {
+                LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
+                
+                if ((pdiph->dwHow == DIPH_BYID) &&
+                    ((pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS)) ||
+                     (pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS)))) {
+                    /* Querying the range of either the X or the Y axis.  As I do
+                       not know the range, do as if the range were
+                       unrestricted...*/
+                    pr->lMin = DIPROPRANGE_NOMIN;
+                    pr->lMax = DIPROPRANGE_NOMAX;
+                }
+                
+                break;
+            }
+
+            default:
+                return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
+        }
+    }
+    
+    return DI_OK;
+}
+
+/******************************************************************************
+  *     GetCapabilities : get the device capabilities
+  */
+static HRESULT WINAPI GdiMouseAImpl_GetCapabilities(
+        LPDIRECTINPUTDEVICE8A iface,
+        LPDIDEVCAPS lpDIDevCaps)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    DIDEVCAPS devcaps;
+
+    TRACE("(this=%p,%p)\n",This,lpDIDevCaps);
+
+    if ((lpDIDevCaps->dwSize != sizeof(DIDEVCAPS)) && (lpDIDevCaps->dwSize != sizeof(DIDEVCAPS_DX3))) {
+        WARN("invalid parameter\n");
+        return DIERR_INVALIDPARAM;
+    }
+
+    devcaps.dwSize = lpDIDevCaps->dwSize;
+    devcaps.dwFlags = DIDC_ATTACHED;
+    if (This->base.dinput->dwVersion >= 0x0800)
+        devcaps.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
+    else
+        devcaps.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
+    devcaps.dwAxes = 3;
+    devcaps.dwButtons = 8;
+    devcaps.dwPOVs = 0;
+    devcaps.dwFFSamplePeriod = 0;
+    devcaps.dwFFMinTimeResolution = 0;
+    devcaps.dwFirmwareRevision = 100;
+    devcaps.dwHardwareRevision = 100;
+    devcaps.dwFFDriverVersion = 0;
+
+    memcpy(lpDIDevCaps, &devcaps, lpDIDevCaps->dwSize);
+    
+    return DI_OK;
+}
+
+/******************************************************************************
+  *     GetObjectInfo : get information about a device object such as a button
+  *                     or axis
+  */
+static HRESULT WINAPI GdiMouseWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
+        LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
+{
+    static const WCHAR x_axisW[] = {'X','-','A','x','i','s',0};
+    static const WCHAR y_axisW[] = {'Y','-','A','x','i','s',0};
+    static const WCHAR wheelW[] = {'W','h','e','e','l',0};
+    static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
+    HRESULT res;
+
+    res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
+    if (res != DI_OK) return res;
+
+    if      (IsEqualGUID(&pdidoi->guidType, &GUID_XAxis)) strcpyW(pdidoi->tszName, x_axisW);
+    else if (IsEqualGUID(&pdidoi->guidType, &GUID_YAxis)) strcpyW(pdidoi->tszName, y_axisW);
+    else if (IsEqualGUID(&pdidoi->guidType, &GUID_ZAxis)) strcpyW(pdidoi->tszName, wheelW);
+    else if (pdidoi->dwType & DIDFT_BUTTON)
+        wsprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType) - 3);
+
+    _dump_OBJECTINSTANCEW(pdidoi);
+    return res;
+}
+
+static HRESULT WINAPI GdiMouseAImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
+        LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
+{
+    HRESULT res;
+    DIDEVICEOBJECTINSTANCEW didoiW;
+    DWORD dwSize = pdidoi->dwSize;
+
+    didoiW.dwSize = sizeof(didoiW);
+    res = GdiMouseWImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W)iface, &didoiW, dwObj, dwHow);
+    if (res != DI_OK) return res;
+
+    memset(pdidoi, 0, pdidoi->dwSize);
+    memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
+    pdidoi->dwSize = dwSize;
+    WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
+                        sizeof(pdidoi->tszName), NULL, NULL);
+
+    return res;
+}
+
+/******************************************************************************
+  *     GetDeviceInfo : get information about a device's identity
+  */
+static HRESULT WINAPI GdiMouseAImpl_GetDeviceInfo(
+    LPDIRECTINPUTDEVICE8A iface,
+    LPDIDEVICEINSTANCEA pdidi)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    TRACE("(this=%p,%p)\n", This, pdidi);
+
+    if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)) {
+        WARN(" dinput3 not supporte yet...\n");
+    return DI_OK;
+    }
+
+    fill_gdimouse_dideviceinstanceA(pdidi, This->base.dinput->dwVersion);
+    
+    return DI_OK;
+}
+
+static HRESULT WINAPI GdiMouseWImpl_GetDeviceInfo(
+    LPDIRECTINPUTDEVICE8W iface,
+    LPDIDEVICEINSTANCEW pdidi)
+{
+    GdiMouseImpl *This = (GdiMouseImpl *)iface;
+    TRACE("(this=%p,%p)\n", This, pdidi);
+
+    if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW)) {
+        WARN(" dinput3 not supporte yet...\n");
+    return DI_OK;
+    }
+
+    fill_gdimouse_dideviceinstanceW(pdidi, This->base.dinput->dwVersion);
+    
+    return DI_OK;
+}
+
+
+static IDirectInputDevice8AVtbl GdiMouseAvt =
+{
+    IDirectInputDevice2AImpl_QueryInterface,
+    IDirectInputDevice2AImpl_AddRef,
+    IDirectInputDevice2AImpl_Release,
+    GdiMouseAImpl_GetCapabilities,
+    IDirectInputDevice2AImpl_EnumObjects,
+    GdiMouseAImpl_GetProperty,
+    IDirectInputDevice2AImpl_SetProperty,
+    GdiMouseAImpl_Acquire,
+    GdiMouseAImpl_Unacquire,
+    GdiMouseAImpl_GetDeviceState,
+    IDirectInputDevice2AImpl_GetDeviceData,
+    IDirectInputDevice2AImpl_SetDataFormat,
+    IDirectInputDevice2AImpl_SetEventNotification,
+    IDirectInputDevice2AImpl_SetCooperativeLevel,
+    GdiMouseAImpl_GetObjectInfo,
+    GdiMouseAImpl_GetDeviceInfo,
+    IDirectInputDevice2AImpl_RunControlPanel,
+    IDirectInputDevice2AImpl_Initialize,
+    IDirectInputDevice2AImpl_CreateEffect,
+    IDirectInputDevice2AImpl_EnumEffects,
+    IDirectInputDevice2AImpl_GetEffectInfo,
+    IDirectInputDevice2AImpl_GetForceFeedbackState,
+    IDirectInputDevice2AImpl_SendForceFeedbackCommand,
+    IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
+    IDirectInputDevice2AImpl_Escape,
+    IDirectInputDevice2AImpl_Poll,
+    IDirectInputDevice2AImpl_SendDeviceData,
+    IDirectInputDevice7AImpl_EnumEffectsInFile,
+    IDirectInputDevice7AImpl_WriteEffectToFile,
+    IDirectInputDevice8AImpl_BuildActionMap,
+    IDirectInputDevice8AImpl_SetActionMap,
+    IDirectInputDevice8AImpl_GetImageInfo
+};
+
+#if !defined(__STRICT_ANSI__) && defined(__GNUC__)
+# define XCAST(fun) (typeof(GdiMouseWvt.fun))
+#else
+# define XCAST(fun) (void*)
+#endif
+
+static IDirectInputDevice8WVtbl GdiMouseWvt =
+{
+    IDirectInputDevice2WImpl_QueryInterface,
+    XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
+    XCAST(Release)IDirectInputDevice2AImpl_Release,
+    XCAST(GetCapabilities)GdiMouseAImpl_GetCapabilities,
+    IDirectInputDevice2WImpl_EnumObjects,
+    XCAST(GetProperty)GdiMouseAImpl_GetProperty,
+    XCAST(SetProperty)IDirectInputDevice2AImpl_SetProperty,
+    XCAST(Acquire)GdiMouseAImpl_Acquire,
+    XCAST(Unacquire)GdiMouseAImpl_Unacquire,
+    XCAST(GetDeviceState)GdiMouseAImpl_GetDeviceState,
+    XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
+    XCAST(SetDataFormat)IDirectInputDevice2AImpl_SetDataFormat,
+    XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
+    XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
+    GdiMouseWImpl_GetObjectInfo,
+    GdiMouseWImpl_GetDeviceInfo,
+    XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
+    XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
+    XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
+    IDirectInputDevice2WImpl_EnumEffects,
+    IDirectInputDevice2WImpl_GetEffectInfo,
+    XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
+    XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
+    XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
+    XCAST(Escape)IDirectInputDevice2AImpl_Escape,
+    XCAST(Poll)IDirectInputDevice2AImpl_Poll,
+    XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
+    IDirectInputDevice7WImpl_EnumEffectsInFile,
+    IDirectInputDevice7WImpl_WriteEffectToFile,
+    IDirectInputDevice8WImpl_BuildActionMap,
+    IDirectInputDevice8WImpl_SetActionMap,
+    IDirectInputDevice8WImpl_GetImageInfo
+};
+#undef XCAST
diff --git a/dlls/winex11.drv/Makefile.in b/dlls/winex11.drv/Makefile.in
index 0bafb33..2d246c5 100644
--- a/dlls/winex11.drv/Makefile.in
+++ b/dlls/winex11.drv/Makefile.in
@@ -19,6 +19,7 @@ C_SRCS = \
 	dib_convert.c \
 	dib_dst_swap.c \
 	dib_src_swap.c \
+	dinput.c \
 	event.c \
 	graphics.c \
 	ime.c \
diff --git a/dlls/winex11.drv/dinput.c b/dlls/winex11.drv/dinput.c
new file mode 100644
index 0000000..0bf284e
--- /dev/null
+++ b/dlls/winex11.drv/dinput.c
@@ -0,0 +1,185 @@
+/*
+ * X11 DirectInput device support
+ *
+ * Copyright (C) 2009 Paul "TBBle" Hampson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include "x11drv.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(dinput);
+
+typedef void (*mousecallback)(void* iface, DWORD instance, DWORD data);
+
+#ifdef SONAME_LIBXI
+
+#include "wine/library.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XInput2.h>
+
+/* XInput stuff */
+static void *xinput_handle;
+static BOOL xi2 = FALSE;
+
+#define MAKE_FUNCPTR(f) static typeof(f) * p##f;
+MAKE_FUNCPTR(XIQueryVersion)
+MAKE_FUNCPTR(XIQueryDevice)
+MAKE_FUNCPTR(XIFreeDeviceInfo)
+#undef MAKE_FUNCPTR
+
+/* Wine mouse driver object instances */
+#define WINE_MOUSE_X_AXIS_INSTANCE   0
+#define WINE_MOUSE_Y_AXIS_INSTANCE   1
+#define WINE_MOUSE_Z_AXIS_INSTANCE   2
+#define WINE_MOUSE_BUTTONS_INSTANCE  3
+
+/***********************************************************************
+ * Mouse support functions
+ */
+
+/***********************************************************************
+ *              X11DRV_dinput_SystemMouse   (X11DRV.@)
+ *
+ * Report if we are able to supply a mouse device
+ */
+
+BOOL CDECL X11DRV_dinput_SystemMouse(BOOL* keyboard, BOOL* mouse)
+{
+    if (!xi2)
+        return FALSE;
+    return FALSE;
+}
+
+/***********************************************************************
+ *              X11DRV_dinput_MouseAcquire   (X11DRV.@)
+ *
+ * Take an interest in the mouse device
+ */
+
+VOID CDECL X11DRV_dinput_MouseAcquire(BOOL foreground, BOOL exclusive,
+    mousecallback callback, void *object)
+{
+    if (!xi2)
+    {
+        ERR("XInput2 unavailable, this should not be called\n");
+        return;
+    }
+    TRACE("Acquired mouse in %s %s for %p\n",
+        foreground?"foreground":"background",
+        exclusive?"exclusively":"non-exclusively",
+        object);
+}
+
+/***********************************************************************
+ *              X11DRV_dinput_MouseUnacquire   (X11DRV.@)
+ *
+ * Release our interest in the mouse device
+ */
+
+VOID CDECL X11DRV_dinput_MouseUnacquire()
+{
+    if (!xi2)
+    {
+        ERR("XInput2 unavailable, this should not be called\n");
+        return;
+    }
+    TRACE("Unacquired mouse\n");
+}
+
+/***********************************************************************
+ *              X11DRV_dinput_Init   (X11DRV.@)
+ *
+ * Initialise our subsystem
+ */
+
+void X11DRV_dinput_Init(Display* display)
+{
+    /* Confirm we have XI2, and grab our symbols */
+    int major = XI_2_Major;
+    int minor = XI_2_Minor;
+    xinput_handle = wine_dlopen(SONAME_LIBXI, RTLD_NOW, NULL, 0);
+    if (xinput_handle)
+    {
+#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(XIFreeDeviceInfo)
+#undef LOAD_FUNCPTR
+        wine_tsx11_lock();
+        if (pXIQueryVersion(display, &major, &minor) == Success &&
+            (major * 1000 + minor) >= (XI_2_Major * 1000 + XI_2_Minor))
+            xi2 = TRUE;
+        wine_tsx11_unlock();
+    }
+sym_not_found:
+    TRACE("XInput2 support: %s\n", xi2 ? "TRUE" : "FALSE");
+    return;
+}
+
+
+#else /* SONAME_LIBXI */
+
+/***********************************************************************
+ *              X11DRV_dinput_SystemMouse   (X11DRV.@)
+ *
+ * Report if we are able to supply a mouse device
+ */
+
+BOOL CDECL X11DRV_dinput_SystemMouse()
+{
+    return FALSE;
+}
+
+/***********************************************************************
+ *              X11DRV_dinput_MouseAcquire   (X11DRV.@)
+ *
+ * Take an interest in the mouse device
+ */
+
+VOID CDECL X11DRV_dinput_MouseAcquire(BOOL foreground, BOOL exclusive,
+    mousecallback callback)
+{
+    ERR("Should not be called when compiled without XInput2\n");
+}
+
+/***********************************************************************
+ *              X11DRV_dinput_MouseUnacquire   (X11DRV.@)
+ *
+ * Release our interest in the mouse device
+ */
+
+VOID CDECL X11DRV_dinput_MouseUnacquire()
+{
+    ERR("Should not be called when compiled without XInput2\n");
+}
+
+/***********************************************************************
+ *              X11DRV_dinput_Init   (X11DRV.@)
+ *
+ * Initialise our subsystem
+ */
+
+void CDECL X11DRV_dinput_Init()
+{
+    TRACE("XInput2 not available at compile time\n");
+}
+
+#endif /* SONAME_LIBXI */
diff --git a/dlls/winex11.drv/winex11.drv.spec b/dlls/winex11.drv/winex11.drv.spec
index fdd32b3..5f32214 100644
--- a/dlls/winex11.drv/winex11.drv.spec
+++ b/dlls/winex11.drv/winex11.drv.spec
@@ -163,3 +163,8 @@
 @ stdcall ImeProcessKey(long long long ptr)
 @ stdcall ImeGetRegisterWordStyle(long ptr)
 @ stdcall ImeGetImeMenuItems(long long long ptr ptr long)
+
+# DirectInput Interface
+@ cdecl wine_directinput_SystemMouse() X11DRV_dinput_SystemMouse
+@ cdecl wine_directinput_MouseAcquire(long long ptr ptr) X11DRV_dinput_MouseAcquire
+@ cdecl wine_directinput_MouseUnacquire() X11DRV_dinput_MouseUnacquire
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 7d0b2ba..44d83a0 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -753,6 +753,7 @@ extern void X11DRV_expect_error( Display *display, x11drv_error_callback callbac
 extern int X11DRV_check_error(void);
 extern void X11DRV_X_to_window_rect( struct x11drv_win_data *data, RECT *rect );
 extern void xinerama_init( unsigned int width, unsigned int height );
+extern void X11DRV_dinput_Init( Display* display );
 
 extern void X11DRV_init_desktop( Window win, unsigned int width, unsigned int height );
 extern void X11DRV_resize_desktop(unsigned int width, unsigned int height);
diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c
index 020d6b5..531082c 100644
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -549,6 +549,10 @@ static BOOL process_attach(void)
     X11DRV_InitClipboard();
     if (use_xim) use_xim = X11DRV_InitXIM( input_style );
 
+#ifdef SONAME_LIBXI
+    X11DRV_dinput_Init( gdi_display);
+#endif
+
     return TRUE;
 }
 
-- 
1.6.2.2

