dinput: Move mouse event queue into base device class.
[wine] / dlls / dinput / mouse.c
1 /*              DirectInput Mouse device
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <stdarg.h>
26 #include <string.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winerror.h"
33 #include "dinput.h"
34
35 #include "dinput_private.h"
36 #include "device_private.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 #define MOUSE_HACK
41
42 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
43
44 /* Wine mouse driver object instances */
45 #define WINE_MOUSE_X_AXIS_INSTANCE   0
46 #define WINE_MOUSE_Y_AXIS_INSTANCE   1
47 #define WINE_MOUSE_Z_AXIS_INSTANCE   2
48 #define WINE_MOUSE_L_BUTTON_INSTANCE 0
49 #define WINE_MOUSE_R_BUTTON_INSTANCE 1
50 #define WINE_MOUSE_M_BUTTON_INSTANCE 2
51 #define WINE_MOUSE_D_BUTTON_INSTANCE 3
52
53 /* ------------------------------- */
54 /* Wine mouse internal data format */
55 /* ------------------------------- */
56
57 /* Constants used to access the offset array */
58 #define WINE_MOUSE_X_POSITION 0
59 #define WINE_MOUSE_Y_POSITION 1
60 #define WINE_MOUSE_Z_POSITION 2
61 #define WINE_MOUSE_L_POSITION 3
62 #define WINE_MOUSE_R_POSITION 4
63 #define WINE_MOUSE_M_POSITION 5
64
65 typedef struct {
66     LONG lX;
67     LONG lY;
68     LONG lZ;
69     BYTE rgbButtons[4];
70 } Wine_InternalMouseData;
71
72 #define WINE_INTERNALMOUSE_NUM_OBJS 6
73
74 static const DIOBJECTDATAFORMAT Wine_InternalMouseObjectFormat[WINE_INTERNALMOUSE_NUM_OBJS] = {
75     { &GUID_XAxis,   FIELD_OFFSET(Wine_InternalMouseData, lX),
76           DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS, 0 },
77     { &GUID_YAxis,   FIELD_OFFSET(Wine_InternalMouseData, lY),
78           DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS, 0 },
79     { &GUID_ZAxis,   FIELD_OFFSET(Wine_InternalMouseData, lZ),
80           DIDFT_MAKEINSTANCE(WINE_MOUSE_Z_AXIS_INSTANCE) | DIDFT_RELAXIS, 0 },
81     { &GUID_Button, (FIELD_OFFSET(Wine_InternalMouseData, rgbButtons)) + 0,
82           DIDFT_MAKEINSTANCE(WINE_MOUSE_L_BUTTON_INSTANCE) | DIDFT_PSHBUTTON, 0 },
83     { &GUID_Button, (FIELD_OFFSET(Wine_InternalMouseData, rgbButtons)) + 1,
84           DIDFT_MAKEINSTANCE(WINE_MOUSE_R_BUTTON_INSTANCE) | DIDFT_PSHBUTTON, 0 },
85     { &GUID_Button, (FIELD_OFFSET(Wine_InternalMouseData, rgbButtons)) + 2,
86           DIDFT_MAKEINSTANCE(WINE_MOUSE_M_BUTTON_INSTANCE) | DIDFT_PSHBUTTON, 0 }
87 };
88
89 static const DIDATAFORMAT Wine_InternalMouseFormat = {
90     0, /* dwSize - unused */
91     0, /* dwObjsize - unused */
92     0, /* dwFlags - unused */
93     sizeof(Wine_InternalMouseData),
94     WINE_INTERNALMOUSE_NUM_OBJS, /* dwNumObjs */
95     (LPDIOBJECTDATAFORMAT) Wine_InternalMouseObjectFormat
96 };
97
98 static const IDirectInputDevice8AVtbl SysMouseAvt;
99 static const IDirectInputDevice8WVtbl SysMouseWvt;
100
101 typedef struct SysMouseImpl SysMouseImpl;
102
103 typedef enum {
104     WARP_DONE,   /* Warping has been done */
105     WARP_NEEDED, /* Warping is needed */
106     WARP_STARTED /* Warping has been done, waiting for the warp event */
107 } WARP_STATUS;
108
109 struct SysMouseImpl
110 {
111     struct IDirectInputDevice2AImpl base;
112     
113     IDirectInputImpl               *dinput;
114     
115     /* The current data format and the conversion between internal
116        and external data formats */
117     DIDATAFORMAT                   *df;
118     DataFormat                     *wine_df;
119     int                             offset_array[WINE_INTERNALMOUSE_NUM_OBJS];
120     
121     /* SysMouseAImpl */
122     BYTE                            absolute;
123     /* Previous position for relative moves */
124     LONG                            prevX, prevY;
125     /* These are used in case of relative -> absolute transitions */
126     POINT                           org_coords;
127     POINT                           mapped_center;
128     DWORD                           win_centerX, win_centerY;
129     /* warping: whether we need to move mouse back to middle once we
130      * reach window borders (for e.g. shooters, "surface movement" games) */
131     WARP_STATUS                     need_warp;
132     DWORD                           last_warped;
133     
134     /* This is for mouse reporting. */
135     Wine_InternalMouseData          m_state;
136 };
137
138 /* FIXME: This is ugly and not thread safe :/ */
139 static IDirectInputDevice8A* current_lock = NULL;
140
141 static GUID DInput_Wine_Mouse_GUID = { /* 9e573ed8-7734-11d2-8d4a-23903fb6bdf7 */
142     0x9e573ed8,
143     0x7734,
144     0x11d2,
145     {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
146 };
147
148 static void fill_mouse_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version) {
149     DWORD dwSize;
150     DIDEVICEINSTANCEA ddi;
151     
152     dwSize = lpddi->dwSize;
153
154     TRACE("%d %p\n", dwSize, lpddi);
155     
156     memset(lpddi, 0, dwSize);
157     memset(&ddi, 0, sizeof(ddi));
158
159     ddi.dwSize = dwSize;
160     ddi.guidInstance = GUID_SysMouse;/* DInput's GUID */
161     ddi.guidProduct = DInput_Wine_Mouse_GUID; /* Vendor's GUID */
162     if (version >= 0x0800)
163         ddi.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
164     else
165         ddi.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
166     strcpy(ddi.tszInstanceName, "Mouse");
167     strcpy(ddi.tszProductName, "Wine Mouse");
168
169     memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
170 }
171
172 static void fill_mouse_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version) {
173     DWORD dwSize;
174     DIDEVICEINSTANCEW ddi;
175     
176     dwSize = lpddi->dwSize;
177
178     TRACE("%d %p\n", dwSize, lpddi);
179     
180     memset(lpddi, 0, dwSize);
181     memset(&ddi, 0, sizeof(ddi));
182
183     ddi.dwSize = dwSize;
184     ddi.guidInstance = GUID_SysMouse;/* DInput's GUID */
185     ddi.guidProduct = DInput_Wine_Mouse_GUID; /* Vendor's GUID */
186     if (version >= 0x0800)
187         ddi.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
188     else
189         ddi.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
190     MultiByteToWideChar(CP_ACP, 0, "Mouse", -1, ddi.tszInstanceName, MAX_PATH);
191     MultiByteToWideChar(CP_ACP, 0, "Wine Mouse", -1, ddi.tszProductName, MAX_PATH);
192
193     memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
194 }
195
196 static BOOL mousedev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
197 {
198     if (id != 0)
199         return FALSE;
200
201     if ((dwDevType == 0) ||
202         ((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
203         (((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
204         TRACE("Enumerating the mouse device\n");
205         
206         fill_mouse_dideviceinstanceA(lpddi, version);
207         
208         return TRUE;
209     }
210     
211     return FALSE;
212 }
213
214 static BOOL mousedev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
215 {
216     if (id != 0)
217         return FALSE;
218
219     if ((dwDevType == 0) ||
220         ((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
221         (((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
222         TRACE("Enumerating the mouse device\n");
223         
224         fill_mouse_dideviceinstanceW(lpddi, version);
225         
226         return TRUE;
227     }
228     
229     return FALSE;
230 }
231
232 static SysMouseImpl *alloc_device(REFGUID rguid, const void *mvt, IDirectInputImpl *dinput)
233 {
234     int offset_array[WINE_INTERNALMOUSE_NUM_OBJS] = {
235         FIELD_OFFSET(Wine_InternalMouseData, lX),
236         FIELD_OFFSET(Wine_InternalMouseData, lY),
237         FIELD_OFFSET(Wine_InternalMouseData, lZ),
238         FIELD_OFFSET(Wine_InternalMouseData, rgbButtons) + 0,
239         FIELD_OFFSET(Wine_InternalMouseData, rgbButtons) + 1,
240         FIELD_OFFSET(Wine_InternalMouseData, rgbButtons) + 2
241     };
242     SysMouseImpl* newDevice;
243     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysMouseImpl));
244     newDevice->base.lpVtbl = mvt;
245     newDevice->base.ref = 1;
246     newDevice->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
247     memcpy(&newDevice->base.guid, rguid, sizeof(*rguid));
248     InitializeCriticalSection(&newDevice->base.crit);
249
250     /* Per default, Wine uses its internal data format */
251     newDevice->df = (DIDATAFORMAT *) &Wine_InternalMouseFormat;
252     memcpy(newDevice->offset_array, offset_array, WINE_INTERNALMOUSE_NUM_OBJS * sizeof(int));
253     newDevice->wine_df = HeapAlloc(GetProcessHeap(), 0, sizeof(DataFormat));
254     newDevice->wine_df->size = 0;
255     newDevice->wine_df->internal_format_size = Wine_InternalMouseFormat.dwDataSize;
256     newDevice->wine_df->dt = NULL;
257     newDevice->dinput = dinput;
258
259     return newDevice;
260 }
261
262 static HRESULT mousedev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
263 {
264     if ((IsEqualGUID(&GUID_SysMouse,rguid)) ||             /* Generic Mouse */
265         (IsEqualGUID(&DInput_Wine_Mouse_GUID,rguid))) { /* Wine Mouse */
266         if ((riid == NULL) ||
267             IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
268             IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
269             IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
270             IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
271             *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &SysMouseAvt, dinput);
272             TRACE("Creating a Mouse device (%p)\n", *pdev);
273             return DI_OK;
274         } else
275             return DIERR_NOINTERFACE;
276     }
277     
278     return DIERR_DEVICENOTREG;
279 }
280
281 static HRESULT mousedev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
282 {
283     if ((IsEqualGUID(&GUID_SysMouse,rguid)) ||             /* Generic Mouse */
284         (IsEqualGUID(&DInput_Wine_Mouse_GUID,rguid))) { /* Wine Mouse */
285         if ((riid == NULL) ||
286             IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
287             IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
288             IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
289             IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
290             *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &SysMouseWvt, dinput);
291             TRACE("Creating a Mouse device (%p)\n", *pdev);
292             return DI_OK;
293         } else
294             return DIERR_NOINTERFACE;
295     }
296     
297     return DIERR_DEVICENOTREG;
298 }
299
300 const struct dinput_device mouse_device = {
301     "Wine mouse driver",
302     mousedev_enum_deviceA,
303     mousedev_enum_deviceW,
304     mousedev_create_deviceA,
305     mousedev_create_deviceW
306 };
307
308 /******************************************************************************
309  *      SysMouseA (DInput Mouse support)
310  */
311
312 /******************************************************************************
313   *     Release : release the mouse buffer.
314   */
315 static ULONG WINAPI SysMouseAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
316 {
317     SysMouseImpl *This = (SysMouseImpl *)iface;
318     ULONG ref;
319  
320     ref = InterlockedDecrement(&This->base.ref);
321     if (ref)
322         return ref;
323
324     set_dinput_hook(WH_MOUSE_LL, NULL);
325
326     /* Free the data queue */
327     HeapFree(GetProcessHeap(), 0, This->base.data_queue);
328     DeleteCriticalSection(&This->base.crit);
329     
330     /* Free the DataFormat */
331     if (This->df != &(Wine_InternalMouseFormat)) {
332         HeapFree(GetProcessHeap(), 0, This->df->rgodf);
333         HeapFree(GetProcessHeap(), 0, This->df);
334     }
335     
336     HeapFree(GetProcessHeap(),0,This);
337     return 0;
338 }
339
340 /******************************************************************************
341   *     SetDataFormat : the application can choose the format of the data
342   *   the device driver sends back with GetDeviceState.
343   *
344   *   For the moment, only the "standard" configuration (c_dfDIMouse) is supported
345   *   in absolute and relative mode.
346   */
347 static HRESULT WINAPI SysMouseAImpl_SetDataFormat(
348         LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
349 )
350 {
351     SysMouseImpl *This = (SysMouseImpl *)iface;
352     
353     TRACE("(this=%p,%p)\n",This,df);
354     
355     _dump_DIDATAFORMAT(df);
356     
357     /* Tests under windows show that a call to SetDataFormat always sets the mouse
358        in relative mode whatever the dwFlags value (DIDF_ABSAXIS/DIDF_RELAXIS).
359        To switch in absolute mode, SetProperty must be used. */
360     This->absolute = 0;
361     
362     /* Store the new data format */
363     This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
364     memcpy(This->df, df, df->dwSize);
365     This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
366     memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
367     
368     /* Prepare all the data-conversion filters */
369     This->wine_df = create_DataFormat(&(Wine_InternalMouseFormat), df, This->offset_array);
370     
371     return DI_OK;
372 }
373
374 /* low-level mouse hook */
375 static LRESULT CALLBACK dinput_mouse_hook( int code, WPARAM wparam, LPARAM lparam )
376 {
377     MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam;
378     SysMouseImpl* This = (SysMouseImpl*) current_lock;
379     DWORD dwCoop;
380     int wdata;
381
382     if (code != HC_ACTION) return CallNextHookEx( 0, code, wparam, lparam );
383
384     EnterCriticalSection(&This->base.crit);
385     dwCoop = This->base.dwCoopLevel;
386
387     if (wparam == WM_MOUSEMOVE) {
388         if (This->absolute) {
389             if (hook->pt.x != This->prevX)
390                 queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_X_POSITION],
391                             hook->pt.x, hook->time, This->dinput->evsequence);
392             if (hook->pt.y != This->prevY)
393                 queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_Y_POSITION],
394                             hook->pt.y, hook->time, This->dinput->evsequence);
395         } else {
396             /* Now, warp handling */
397             if ((This->need_warp == WARP_STARTED) &&
398                 (hook->pt.x == This->mapped_center.x) && (hook->pt.y == This->mapped_center.y)) {
399                 /* Warp has been done... */
400                 This->need_warp = WARP_DONE;
401                 goto end;
402             }
403             
404             /* Relative mouse input with absolute mouse event : the real fun starts here... */
405             if ((This->need_warp == WARP_NEEDED) ||
406                 (This->need_warp == WARP_STARTED)) {
407                 if (hook->pt.x != This->prevX)
408                     queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_X_POSITION],
409                                 hook->pt.x - This->prevX, hook->time, This->dinput->evsequence);
410                 if (hook->pt.y != This->prevY)
411                     queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_Y_POSITION],
412                                 hook->pt.y - This->prevY, hook->time, This->dinput->evsequence);
413             } else {
414                 /* This is the first time the event handler has been called after a
415                    GetDeviceData or GetDeviceState. */
416                 if (hook->pt.x != This->mapped_center.x) {
417                     queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_X_POSITION],
418                                 hook->pt.x - This->mapped_center.x, hook->time, This->dinput->evsequence);
419                     This->need_warp = WARP_NEEDED;
420                 }
421                 
422                 if (hook->pt.y != This->mapped_center.y) {
423                     queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_Y_POSITION],
424                                 hook->pt.y - This->mapped_center.y, hook->time, This->dinput->evsequence);
425                     This->need_warp = WARP_NEEDED;
426                 }
427             }
428         }
429         
430         This->prevX = hook->pt.x;
431         This->prevY = hook->pt.y;
432         
433         if (This->absolute) {
434             This->m_state.lX = hook->pt.x;
435             This->m_state.lY = hook->pt.y;
436         } else {
437             This->m_state.lX = hook->pt.x - This->mapped_center.x;
438             This->m_state.lY = hook->pt.y - This->mapped_center.y;
439         }
440     }
441     
442     TRACE(" msg %x pt %d %d (W=%d)\n",
443           wparam, hook->pt.x, hook->pt.y, (!This->absolute) && This->need_warp );
444     
445     switch(wparam) {
446         case WM_LBUTTONDOWN:
447             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_L_POSITION],
448                         0x80, hook->time, This->dinput->evsequence);
449             This->m_state.rgbButtons[0] = 0x80;
450             break;
451         case WM_LBUTTONUP:
452             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_L_POSITION],
453                         0x00, hook->time, This->dinput->evsequence);
454             This->m_state.rgbButtons[0] = 0x00;
455             break;
456         case WM_RBUTTONDOWN:
457             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_R_POSITION],
458                         0x80, hook->time, This->dinput->evsequence);
459             This->m_state.rgbButtons[1] = 0x80;
460             break;
461         case WM_RBUTTONUP:
462             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_R_POSITION],
463                         0x00, hook->time, This->dinput->evsequence);
464             This->m_state.rgbButtons[1] = 0x00;
465             break;
466         case WM_MBUTTONDOWN:
467             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_M_POSITION],
468                         0x80, hook->time, This->dinput->evsequence);
469             This->m_state.rgbButtons[2] = 0x80;
470             break;
471         case WM_MBUTTONUP:
472             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_M_POSITION],
473                         0x00, hook->time, This->dinput->evsequence);
474             This->m_state.rgbButtons[2] = 0x00;
475             break;
476         case WM_MOUSEWHEEL:
477             wdata = (short)HIWORD(hook->mouseData);
478             queue_event((LPDIRECTINPUTDEVICE8A)This, This->offset_array[WINE_MOUSE_Z_POSITION],
479                         wdata, hook->time, This->dinput->evsequence);
480             This->m_state.lZ += wdata;
481             break;
482     }
483     
484     TRACE("(X: %d - Y: %d   L: %02x M: %02x R: %02x)\n",
485           This->m_state.lX, This->m_state.lY,
486           This->m_state.rgbButtons[0], This->m_state.rgbButtons[2], This->m_state.rgbButtons[1]);
487     
488     This->dinput->evsequence++;
489
490   end:
491     /* Mouse moved -> send event if asked */
492     if (This->base.hEvent) SetEvent(This->base.hEvent);
493     
494     LeaveCriticalSection(&This->base.crit);
495     
496     /* Ignore message */
497     if (dwCoop & DISCL_EXCLUSIVE) return 1;
498
499     /* Pass the events down to previous handlers (e.g. win32 input) */
500     return CallNextHookEx( 0, code, wparam, lparam );
501 }
502
503 static BOOL dinput_window_check(SysMouseImpl* This) {
504     RECT rect;
505     DWORD centerX, centerY;
506
507     /* make sure the window hasn't moved */
508     if(!GetWindowRect(This->base.win, &rect))
509         return FALSE;
510     centerX = (rect.right  - rect.left) / 2;
511     centerY = (rect.bottom - rect.top ) / 2;
512     if (This->win_centerX != centerX || This->win_centerY != centerY) {
513         This->win_centerX = centerX;
514         This->win_centerY = centerY;
515     }
516     This->mapped_center.x = This->win_centerX;
517     This->mapped_center.y = This->win_centerY;
518     MapWindowPoints(This->base.win, HWND_DESKTOP, &This->mapped_center, 1);
519     return TRUE;
520 }
521
522
523 /******************************************************************************
524   *     Acquire : gets exclusive control of the mouse
525   */
526 static HRESULT WINAPI SysMouseAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
527 {
528     SysMouseImpl *This = (SysMouseImpl *)iface;
529     RECT  rect;
530     POINT point;
531     HRESULT res;
532     
533     TRACE("(this=%p)\n",This);
534
535     if ((res = IDirectInputDevice2AImpl_Acquire(iface)) != DI_OK) return res;
536
537     /* Store (in a global variable) the current lock */
538     current_lock = (IDirectInputDevice8A*)This;
539     
540     /* Init the mouse state */
541     GetCursorPos( &point );
542     if (This->absolute) {
543       This->m_state.lX = point.x;
544       This->m_state.lY = point.y;
545       This->prevX = point.x;
546       This->prevY = point.y;
547     } else {
548       This->m_state.lX = 0;
549       This->m_state.lY = 0;
550       This->org_coords = point;
551     }
552     This->m_state.lZ = 0;
553     This->m_state.rgbButtons[0] = GetKeyState(VK_LBUTTON) & 0x80;
554     This->m_state.rgbButtons[1] = GetKeyState(VK_RBUTTON) & 0x80;
555     This->m_state.rgbButtons[2] = GetKeyState(VK_MBUTTON) & 0x80;
556     
557     /* Install our mouse hook */
558     if (This->base.dwCoopLevel & DISCL_EXCLUSIVE)
559       ShowCursor(FALSE); /* hide cursor */
560     set_dinput_hook(WH_MOUSE_LL, dinput_mouse_hook);
561     
562     /* Get the window dimension and find the center */
563     GetWindowRect(This->base.win, &rect);
564     This->win_centerX = (rect.right  - rect.left) / 2;
565     This->win_centerY = (rect.bottom - rect.top ) / 2;
566     
567     /* Warp the mouse to the center of the window */
568     if (This->absolute == 0) {
569       This->mapped_center.x = This->win_centerX;
570       This->mapped_center.y = This->win_centerY;
571       MapWindowPoints(This->base.win, HWND_DESKTOP, &This->mapped_center, 1);
572       TRACE("Warping mouse to %d - %d\n", This->mapped_center.x, This->mapped_center.y);
573       SetCursorPos( This->mapped_center.x, This->mapped_center.y );
574       This->last_warped = GetCurrentTime();
575
576 #ifdef MOUSE_HACK
577       This->need_warp = WARP_DONE;
578 #else
579       This->need_warp = WARP_STARTED;
580 #endif
581     }
582         
583     return DI_OK;
584 }
585
586 /******************************************************************************
587   *     Unacquire : frees the mouse
588   */
589 static HRESULT WINAPI SysMouseAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
590 {
591     SysMouseImpl *This = (SysMouseImpl *)iface;
592     HRESULT res;
593     
594     TRACE("(this=%p)\n",This);
595
596     if ((res = IDirectInputDevice2AImpl_Unacquire(iface)) != DI_OK) return res;
597
598     set_dinput_hook(WH_MOUSE_LL, NULL);
599     if (This->base.dwCoopLevel & DISCL_EXCLUSIVE)
600         ShowCursor(TRUE); /* show cursor */
601
602     /* No more locks */
603     if (current_lock == (IDirectInputDevice8A*) This)
604       current_lock = NULL;
605     else
606       ERR("this(%p) != current_lock(%p)\n", This, current_lock);
607
608     /* And put the mouse cursor back where it was at acquire time */
609     if (This->absolute == 0) {
610       TRACE(" warping mouse back to (%d , %d)\n", This->org_coords.x, This->org_coords.y);
611       SetCursorPos(This->org_coords.x, This->org_coords.y);
612     }
613         
614     return DI_OK;
615 }
616
617 /******************************************************************************
618   *     GetDeviceState : returns the "state" of the mouse.
619   *
620   *   For the moment, only the "standard" return structure (DIMOUSESTATE) is
621   *   supported.
622   */
623 static HRESULT WINAPI SysMouseAImpl_GetDeviceState(
624         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
625 ) {
626     SysMouseImpl *This = (SysMouseImpl *)iface;
627
628     if(This->base.acquired == 0) return DIERR_NOTACQUIRED;
629
630     EnterCriticalSection(&This->base.crit);
631     TRACE("(this=%p,0x%08x,%p):\n", This, len, ptr);
632     TRACE("(X: %d - Y: %d - Z: %d  L: %02x M: %02x R: %02x)\n",
633           This->m_state.lX, This->m_state.lY, This->m_state.lZ,
634           This->m_state.rgbButtons[0], This->m_state.rgbButtons[2], This->m_state.rgbButtons[1]);
635     
636     /* Copy the current mouse state */
637     fill_DataFormat(ptr, &(This->m_state), This->wine_df);
638     
639     /* Initialize the buffer when in relative mode */
640     if (This->absolute == 0) {
641         This->m_state.lX = 0;
642         This->m_state.lY = 0;
643         This->m_state.lZ = 0;
644     }
645     
646     /* Check if we need to do a mouse warping */
647     if (This->need_warp == WARP_NEEDED && (GetCurrentTime() - This->last_warped > 10)) {
648         if(!dinput_window_check(This))
649         {
650             LeaveCriticalSection(&This->base.crit);
651             return DIERR_GENERIC;
652         }
653         TRACE("Warping mouse to %d - %d\n", This->mapped_center.x, This->mapped_center.y);
654         SetCursorPos( This->mapped_center.x, This->mapped_center.y );
655         This->last_warped = GetCurrentTime();
656
657 #ifdef MOUSE_HACK
658         This->need_warp = WARP_DONE;
659 #else
660         This->need_warp = WARP_STARTED;
661 #endif
662     }
663     
664     LeaveCriticalSection(&This->base.crit);
665     
666     return DI_OK;
667 }
668
669 /******************************************************************************
670   *     GetDeviceData : gets buffered input data.
671   */
672 static HRESULT WINAPI SysMouseAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
673         DWORD dodsize, LPDIDEVICEOBJECTDATA dod, LPDWORD entries, DWORD flags)
674 {
675     SysMouseImpl *This = (SysMouseImpl *)iface;
676     HRESULT res;
677
678     res = IDirectInputDevice2AImpl_GetDeviceData(iface, dodsize, dod, entries, flags);
679     if (FAILED(res)) return res;
680     
681     /* Check if we need to do a mouse warping */
682     if (This->need_warp == WARP_NEEDED && (GetCurrentTime() - This->last_warped > 10)) {
683         if(!dinput_window_check(This))
684             return DIERR_GENERIC;
685         TRACE("Warping mouse to %d - %d\n", This->mapped_center.x, This->mapped_center.y);
686         SetCursorPos( This->mapped_center.x, This->mapped_center.y );
687         This->last_warped = GetCurrentTime();
688
689 #ifdef MOUSE_HACK
690         This->need_warp = WARP_DONE;
691 #else
692         This->need_warp = WARP_STARTED;
693 #endif
694     }
695     return res;
696 }
697
698 /******************************************************************************
699   *     SetProperty : change input device properties
700   */
701 static HRESULT WINAPI SysMouseAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
702                                             REFGUID rguid,
703                                             LPCDIPROPHEADER ph)
704 {
705     SysMouseImpl *This = (SysMouseImpl *)iface;
706     
707     TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
708     
709     if (!HIWORD(rguid)) {
710         switch (LOWORD(rguid)) {
711             case (DWORD) DIPROP_AXISMODE: {
712                 LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
713                 This->absolute = !(pd->dwData);
714                 TRACE("Using %s coordinates mode now\n", This->absolute ? "absolute" : "relative");
715                 break;
716             }
717             default:
718                 return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
719         }
720     }
721     
722     return DI_OK;
723 }
724
725 /******************************************************************************
726   *     GetProperty : get input device properties
727   */
728 static HRESULT WINAPI SysMouseAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
729                                                 REFGUID rguid,
730                                                 LPDIPROPHEADER pdiph)
731 {
732     SysMouseImpl *This = (SysMouseImpl *)iface;
733     
734     TRACE("(%p) %s,%p\n", This, debugstr_guid(rguid), pdiph);
735     _dump_DIPROPHEADER(pdiph);
736     
737     if (!HIWORD(rguid)) {
738         switch (LOWORD(rguid)) {
739             case (DWORD) DIPROP_GRANULARITY: {
740                 LPDIPROPDWORD pr = (LPDIPROPDWORD) pdiph;
741                 
742                 /* We'll just assume that the app asks about the Z axis */
743                 pr->dwData = WHEEL_DELTA;
744                 
745                 break;
746             }
747               
748             case (DWORD) DIPROP_RANGE: {
749                 LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
750                 
751                 if ((pdiph->dwHow == DIPH_BYID) &&
752                     ((pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS)) ||
753                      (pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS)))) {
754                     /* Querying the range of either the X or the Y axis.  As I do
755                        not know the range, do as if the range were
756                        unrestricted...*/
757                     pr->lMin = DIPROPRANGE_NOMIN;
758                     pr->lMax = DIPROPRANGE_NOMAX;
759                 }
760                 
761                 break;
762             }
763
764             default:
765                 return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
766         }
767     }
768     
769     return DI_OK;
770 }
771
772 /******************************************************************************
773   *     GetCapabilities : get the device capablitites
774   */
775 static HRESULT WINAPI SysMouseAImpl_GetCapabilities(
776         LPDIRECTINPUTDEVICE8A iface,
777         LPDIDEVCAPS lpDIDevCaps)
778 {
779     SysMouseImpl *This = (SysMouseImpl *)iface;
780     DIDEVCAPS devcaps;
781
782     TRACE("(this=%p,%p)\n",This,lpDIDevCaps);
783
784     if ((lpDIDevCaps->dwSize != sizeof(DIDEVCAPS)) && (lpDIDevCaps->dwSize != sizeof(DIDEVCAPS_DX3))) {
785         WARN("invalid parameter\n");
786         return DIERR_INVALIDPARAM;
787     }
788
789     devcaps.dwSize = lpDIDevCaps->dwSize;
790     devcaps.dwFlags = DIDC_ATTACHED;
791     if (This->dinput->dwVersion >= 0x0800)
792         devcaps.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
793     else
794         devcaps.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
795     devcaps.dwAxes = 3;
796     devcaps.dwButtons = 3;
797     devcaps.dwPOVs = 0;
798     devcaps.dwFFSamplePeriod = 0;
799     devcaps.dwFFMinTimeResolution = 0;
800     devcaps.dwFirmwareRevision = 100;
801     devcaps.dwHardwareRevision = 100;
802     devcaps.dwFFDriverVersion = 0;
803
804     memcpy(lpDIDevCaps, &devcaps, lpDIDevCaps->dwSize);
805     
806     return DI_OK;
807 }
808
809
810 /******************************************************************************
811   *     EnumObjects : enumerate the different buttons and axis...
812   */
813 static HRESULT WINAPI SysMouseAImpl_EnumObjects(
814         LPDIRECTINPUTDEVICE8A iface,
815         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
816         LPVOID lpvRef,
817         DWORD dwFlags)
818 {
819     SysMouseImpl *This = (SysMouseImpl *)iface;
820     DIDEVICEOBJECTINSTANCEA ddoi;
821     
822     TRACE("(this=%p,%p,%p,%08x)\n", This, lpCallback, lpvRef, dwFlags);
823     if (TRACE_ON(dinput)) {
824         TRACE("  - flags = ");
825         _dump_EnumObjects_flags(dwFlags);
826         TRACE("\n");
827     }
828     
829     /* Only the fields till dwFFMaxForce are relevant */
830     memset(&ddoi, 0, sizeof(ddoi));
831     ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
832     
833     /* In a mouse, we have : two relative axis and three buttons */
834     if ((dwFlags == DIDFT_ALL) ||
835         (dwFlags & DIDFT_AXIS)) {
836         /* X axis */
837         ddoi.guidType = GUID_XAxis;
838         ddoi.dwOfs = This->offset_array[WINE_MOUSE_X_POSITION];
839         ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS;
840         strcpy(ddoi.tszName, "X-Axis");
841         _dump_OBJECTINSTANCEA(&ddoi);
842         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
843         
844         /* Y axis */
845         ddoi.guidType = GUID_YAxis;
846         ddoi.dwOfs = This->offset_array[WINE_MOUSE_Y_POSITION];
847         ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS;
848         strcpy(ddoi.tszName, "Y-Axis");
849         _dump_OBJECTINSTANCEA(&ddoi);
850         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
851         
852         /* Z axis */
853         ddoi.guidType = GUID_ZAxis;
854         ddoi.dwOfs = This->offset_array[WINE_MOUSE_Z_POSITION];
855         ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_Z_AXIS_INSTANCE) | DIDFT_RELAXIS;
856         strcpy(ddoi.tszName, "Z-Axis");
857         _dump_OBJECTINSTANCEA(&ddoi);
858         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
859     }
860
861     if ((dwFlags == DIDFT_ALL) ||
862         (dwFlags & DIDFT_BUTTON)) {
863         ddoi.guidType = GUID_Button;
864         
865         /* Left button */
866         ddoi.dwOfs = This->offset_array[WINE_MOUSE_L_POSITION];
867         ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_L_BUTTON_INSTANCE) | DIDFT_PSHBUTTON;
868         strcpy(ddoi.tszName, "Left-Button");
869         _dump_OBJECTINSTANCEA(&ddoi);
870         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
871         
872         /* Right button */
873         ddoi.dwOfs = This->offset_array[WINE_MOUSE_R_POSITION];
874         ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_R_BUTTON_INSTANCE) | DIDFT_PSHBUTTON;
875         strcpy(ddoi.tszName, "Right-Button");
876         _dump_OBJECTINSTANCEA(&ddoi);
877         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
878         
879         /* Middle button */
880         ddoi.dwOfs = This->offset_array[WINE_MOUSE_M_POSITION];
881         ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_M_BUTTON_INSTANCE) | DIDFT_PSHBUTTON;
882         strcpy(ddoi.tszName, "Middle-Button");
883         _dump_OBJECTINSTANCEA(&ddoi);
884         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
885     }
886     
887     return DI_OK;
888 }
889
890 static HRESULT WINAPI SysMouseWImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface, LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback, LPVOID lpvRef,DWORD dwFlags)
891 {
892     SysMouseImpl *This = (SysMouseImpl *)iface;
893     
894     device_enumobjects_AtoWcb_data data;
895     
896     data.lpCallBack = lpCallback;
897     data.lpvRef = lpvRef;
898     
899     return SysMouseAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
900 }
901
902 /******************************************************************************
903   *     GetDeviceInfo : get information about a device's identity
904   */
905 static HRESULT WINAPI SysMouseAImpl_GetDeviceInfo(
906         LPDIRECTINPUTDEVICE8A iface,
907         LPDIDEVICEINSTANCEA pdidi)
908 {
909     SysMouseImpl *This = (SysMouseImpl *)iface;
910     TRACE("(this=%p,%p)\n", This, pdidi);
911
912     if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)) {
913         WARN(" dinput3 not supporte yet...\n");
914         return DI_OK;
915     }
916
917     fill_mouse_dideviceinstanceA(pdidi, This->dinput->dwVersion);
918     
919     return DI_OK;
920 }
921
922 static HRESULT WINAPI SysMouseWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, LPDIDEVICEINSTANCEW pdidi)
923 {
924     SysMouseImpl *This = (SysMouseImpl *)iface;
925     TRACE("(this=%p,%p)\n", This, pdidi);
926
927     if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW)) {
928         WARN(" dinput3 not supporte yet...\n");
929         return DI_OK;
930     }
931
932     fill_mouse_dideviceinstanceW(pdidi, This->dinput->dwVersion);
933     
934     return DI_OK;
935 }
936
937
938 static const IDirectInputDevice8AVtbl SysMouseAvt =
939 {
940     IDirectInputDevice2AImpl_QueryInterface,
941     IDirectInputDevice2AImpl_AddRef,
942     SysMouseAImpl_Release,
943     SysMouseAImpl_GetCapabilities,
944     SysMouseAImpl_EnumObjects,
945     SysMouseAImpl_GetProperty,
946     SysMouseAImpl_SetProperty,
947     SysMouseAImpl_Acquire,
948     SysMouseAImpl_Unacquire,
949     SysMouseAImpl_GetDeviceState,
950     SysMouseAImpl_GetDeviceData,
951     SysMouseAImpl_SetDataFormat,
952     IDirectInputDevice2AImpl_SetEventNotification,
953     IDirectInputDevice2AImpl_SetCooperativeLevel,
954     IDirectInputDevice2AImpl_GetObjectInfo,
955     SysMouseAImpl_GetDeviceInfo,
956     IDirectInputDevice2AImpl_RunControlPanel,
957     IDirectInputDevice2AImpl_Initialize,
958     IDirectInputDevice2AImpl_CreateEffect,
959     IDirectInputDevice2AImpl_EnumEffects,
960     IDirectInputDevice2AImpl_GetEffectInfo,
961     IDirectInputDevice2AImpl_GetForceFeedbackState,
962     IDirectInputDevice2AImpl_SendForceFeedbackCommand,
963     IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
964     IDirectInputDevice2AImpl_Escape,
965     IDirectInputDevice2AImpl_Poll,
966     IDirectInputDevice2AImpl_SendDeviceData,
967     IDirectInputDevice7AImpl_EnumEffectsInFile,
968     IDirectInputDevice7AImpl_WriteEffectToFile,
969     IDirectInputDevice8AImpl_BuildActionMap,
970     IDirectInputDevice8AImpl_SetActionMap,
971     IDirectInputDevice8AImpl_GetImageInfo
972 };
973
974 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
975 # define XCAST(fun)     (typeof(SysMouseWvt.fun))
976 #else
977 # define XCAST(fun)     (void*)
978 #endif
979
980 static const IDirectInputDevice8WVtbl SysMouseWvt =
981 {
982     IDirectInputDevice2WImpl_QueryInterface,
983     XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
984     XCAST(Release)SysMouseAImpl_Release,
985     XCAST(GetCapabilities)SysMouseAImpl_GetCapabilities,
986     SysMouseWImpl_EnumObjects,
987     XCAST(GetProperty)SysMouseAImpl_GetProperty,
988     XCAST(SetProperty)SysMouseAImpl_SetProperty,
989     XCAST(Acquire)SysMouseAImpl_Acquire,
990     XCAST(Unacquire)SysMouseAImpl_Unacquire,
991     XCAST(GetDeviceState)SysMouseAImpl_GetDeviceState,
992     XCAST(GetDeviceData)SysMouseAImpl_GetDeviceData,
993     XCAST(SetDataFormat)SysMouseAImpl_SetDataFormat,
994     XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
995     XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
996     IDirectInputDevice2WImpl_GetObjectInfo,
997     SysMouseWImpl_GetDeviceInfo,
998     XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
999     XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1000     XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
1001     IDirectInputDevice2WImpl_EnumEffects,
1002     IDirectInputDevice2WImpl_GetEffectInfo,
1003     XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
1004     XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1005     XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1006     XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1007     XCAST(Poll)IDirectInputDevice2AImpl_Poll,
1008     XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1009     IDirectInputDevice7WImpl_EnumEffectsInFile,
1010     IDirectInputDevice7WImpl_WriteEffectToFile,
1011     IDirectInputDevice8WImpl_BuildActionMap,
1012     IDirectInputDevice8WImpl_SetActionMap,
1013     IDirectInputDevice8WImpl_GetImageInfo
1014 };
1015 #undef XCAST