Added regedit unit test, a couple minor changes to regedit.
[wine] / dlls / dinput / mouse / main.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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include <string.h>
24 #ifdef HAVE_SYS_ERRNO_H
25 # include <sys/errno.h>
26 #endif
27
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "dinput.h"
33
34 #include "dinput_private.h"
35 #include "device_private.h"
36 #include "wine/debug.h"
37
38 #define MOUSE_HACK
39
40 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
41
42 /* Wine mouse driver object instances */
43 #define WINE_MOUSE_X_AXIS_INSTANCE 0x0001
44 #define WINE_MOUSE_Y_AXIS_INSTANCE 0x0002
45 #define WINE_MOUSE_Z_AXIS_INSTANCE 0x0004
46 #define WINE_MOUSE_L_BUTTON_INSTANCE 0x0008
47 #define WINE_MOUSE_R_BUTTON_INSTANCE 0x0010
48 #define WINE_MOUSE_M_BUTTON_INSTANCE 0x0020
49
50 /* ------------------------------- */
51 /* Wine mouse internal data format */
52 /* ------------------------------- */
53
54 /* Constants used to access the offset array */
55 #define WINE_MOUSE_X_POSITION 0
56 #define WINE_MOUSE_Y_POSITION 1
57 #define WINE_MOUSE_Z_POSITION 2
58 #define WINE_MOUSE_L_POSITION 3
59 #define WINE_MOUSE_R_POSITION 4
60 #define WINE_MOUSE_M_POSITION 5
61
62 typedef struct {
63   LONG lX;
64   LONG lY;
65   LONG lZ;
66   BYTE rgbButtons[4];
67 } Wine_InternalMouseData;
68
69 #define WINE_INTERNALMOUSE_NUM_OBJS 6
70
71 static DIOBJECTDATAFORMAT Wine_InternalMouseObjectFormat[WINE_INTERNALMOUSE_NUM_OBJS] = {
72   { &GUID_XAxis,   FIELD_OFFSET(Wine_InternalMouseData, lX),
73       DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS, 0 },
74   { &GUID_YAxis,   FIELD_OFFSET(Wine_InternalMouseData, lY),
75       DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS, 0 },
76   { &GUID_ZAxis,   FIELD_OFFSET(Wine_InternalMouseData, lZ),
77       DIDFT_MAKEINSTANCE(WINE_MOUSE_Z_AXIS_INSTANCE) | DIDFT_RELAXIS, 0 },
78   { &GUID_Button, (FIELD_OFFSET(Wine_InternalMouseData, rgbButtons)) + 0,
79       DIDFT_MAKEINSTANCE(WINE_MOUSE_L_BUTTON_INSTANCE) | DIDFT_PSHBUTTON, 0 },
80   { &GUID_Button, (FIELD_OFFSET(Wine_InternalMouseData, rgbButtons)) + 1,
81       DIDFT_MAKEINSTANCE(WINE_MOUSE_R_BUTTON_INSTANCE) | DIDFT_PSHBUTTON, 0 },
82   { &GUID_Button, (FIELD_OFFSET(Wine_InternalMouseData, rgbButtons)) + 2,
83       DIDFT_MAKEINSTANCE(WINE_MOUSE_M_BUTTON_INSTANCE) | DIDFT_PSHBUTTON, 0 }
84 };
85
86 static DIDATAFORMAT Wine_InternalMouseFormat = {
87   0, /* dwSize - unused */
88   0, /* dwObjsize - unused */
89   0, /* dwFlags - unused */
90   sizeof(Wine_InternalMouseData),
91   WINE_INTERNALMOUSE_NUM_OBJS, /* dwNumObjs */
92   Wine_InternalMouseObjectFormat
93 };
94
95 static ICOM_VTABLE(IDirectInputDevice8A) SysMouseAvt;
96 typedef struct SysMouseAImpl SysMouseAImpl;
97
98 typedef enum {
99   WARP_NEEDED,  /* Warping is needed */
100   WARP_STARTED, /* Warping has been done, waiting for the warp event */
101   WARP_DONE     /* Warping has been done */
102 } WARP_STATUS;
103
104 struct SysMouseAImpl
105 {
106         LPVOID                          lpVtbl;
107         DWORD                           ref;
108         GUID                            guid;
109
110         IDirectInputAImpl *dinput;
111
112         /* The current data format and the conversion between internal
113            and external data formats */
114         LPDIDATAFORMAT                  df;
115         DataFormat                     *wine_df;
116         int                             offset_array[WINE_INTERNALMOUSE_NUM_OBJS];
117
118         /* SysMouseAImpl */
119         BYTE                            absolute;
120         /* Previous position for relative moves */
121         LONG                            prevX, prevY;
122         HHOOK                           hook;
123         HWND                            win;
124         DWORD                           dwCoopLevel;
125         POINT                           mapped_center;
126         DWORD                           win_centerX, win_centerY;
127         LPDIDEVICEOBJECTDATA            data_queue;
128         int                             queue_head, queue_tail, queue_len;
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         int                             acquired;
133         HANDLE                          hEvent;
134         CRITICAL_SECTION                crit;
135
136         /* This is for mouse reporting. */
137         Wine_InternalMouseData          m_state;
138 };
139
140 static GUID DInput_Wine_Mouse_GUID = { /* 9e573ed8-7734-11d2-8d4a-23903fb6bdf7 */
141   0x9e573ed8,
142   0x7734,
143   0x11d2,
144   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
145 };
146
147 /* FIXME: This is ugly and not thread safe :/ */
148 static IDirectInputDevice8A* current_lock = NULL;
149
150
151 static BOOL mousedev_enum_device(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi)
152 {
153   if ((dwDevType == 0) || (dwDevType == DIDEVTYPE_MOUSE)) {
154     TRACE("Enumerating the mouse device\n");
155
156     /* Return mouse */
157     lpddi->guidInstance = GUID_SysMouse;/* DInput's GUID */
158     lpddi->guidProduct = DInput_Wine_Mouse_GUID; /* Vendor's GUID */
159     lpddi->dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_UNKNOWN << 8);
160     strcpy(lpddi->tszInstanceName, "Mouse");
161     strcpy(lpddi->tszProductName, "Wine Mouse");
162
163     return TRUE;
164   }
165
166   return FALSE;
167 }
168
169 static SysMouseAImpl *alloc_device(REFGUID rguid, LPVOID mvt, IDirectInputAImpl *dinput)
170 {
171     int offset_array[WINE_INTERNALMOUSE_NUM_OBJS] = {
172       FIELD_OFFSET(Wine_InternalMouseData, lX),
173       FIELD_OFFSET(Wine_InternalMouseData, lY),
174       FIELD_OFFSET(Wine_InternalMouseData, lZ),
175       FIELD_OFFSET(Wine_InternalMouseData, rgbButtons) + 0,
176       FIELD_OFFSET(Wine_InternalMouseData, rgbButtons) + 1,
177       FIELD_OFFSET(Wine_InternalMouseData, rgbButtons) + 2
178     };
179     SysMouseAImpl* newDevice;
180     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysMouseAImpl));
181     newDevice->ref = 1;
182     newDevice->lpVtbl = mvt;
183     InitializeCriticalSection(&(newDevice->crit));
184     memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
185
186     /* Per default, Wine uses its internal data format */
187     newDevice->df = &Wine_InternalMouseFormat;
188     memcpy(newDevice->offset_array, offset_array, WINE_INTERNALMOUSE_NUM_OBJS * sizeof(int));
189     newDevice->wine_df = (DataFormat *) HeapAlloc(GetProcessHeap(), 0, sizeof(DataFormat));
190     newDevice->wine_df->size = 0;
191     newDevice->wine_df->internal_format_size = Wine_InternalMouseFormat.dwDataSize;
192     newDevice->wine_df->dt = NULL;
193     newDevice->dinput = dinput;
194
195     return newDevice;
196 }
197
198 static HRESULT mousedev_create_device(IDirectInputAImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
199 {
200   if ((IsEqualGUID(&GUID_SysMouse,rguid)) ||             /* Generic Mouse */
201       (IsEqualGUID(&DInput_Wine_Mouse_GUID,rguid))) { /* Wine Mouse */
202     if ((riid == NULL) ||
203         IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
204         IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
205         IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
206         IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
207       *pdev=(IDirectInputDeviceA*) alloc_device(rguid, &SysMouseAvt, dinput);
208       TRACE("Creating a Mouse device (%p)\n", *pdev);
209       return DI_OK;
210     } else
211       return DIERR_NOINTERFACE;
212   }
213
214   return DIERR_DEVICENOTREG;
215 }
216
217 static dinput_device mousedev = {
218   100,
219   mousedev_enum_device,
220   mousedev_create_device
221 };
222
223 DECL_GLOBAL_CONSTRUCTOR(mousedev_register) { dinput_register_device(&mousedev); }
224
225 /******************************************************************************
226  *      SysMouseA (DInput Mouse support)
227  */
228
229 /******************************************************************************
230   *     Release : release the mouse buffer.
231   */
232 static ULONG WINAPI SysMouseAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
233 {
234         ICOM_THIS(SysMouseAImpl,iface);
235
236         This->ref--;
237         if (This->ref)
238                 return This->ref;
239
240         /* Free the data queue */
241         if (This->data_queue != NULL)
242           HeapFree(GetProcessHeap(),0,This->data_queue);
243
244         if (This->hook) {
245           UnhookWindowsHookEx( This->hook );
246           if (This->dwCoopLevel & DISCL_EXCLUSIVE)
247             ShowCursor(TRUE); /* show cursor */
248         }
249         DeleteCriticalSection(&(This->crit));
250
251         /* Free the DataFormat */
252         if (This->df != &(Wine_InternalMouseFormat)) {
253           HeapFree(GetProcessHeap(), 0, This->df->rgodf);
254           HeapFree(GetProcessHeap(), 0, This->df);
255         }
256
257         HeapFree(GetProcessHeap(),0,This);
258         return 0;
259 }
260
261
262 /******************************************************************************
263   *     SetCooperativeLevel : store the window in which we will do our
264   *   grabbing.
265   */
266 static HRESULT WINAPI SysMouseAImpl_SetCooperativeLevel(
267         LPDIRECTINPUTDEVICE8A iface,HWND hwnd,DWORD dwflags
268 )
269 {
270   ICOM_THIS(SysMouseAImpl,iface);
271
272   TRACE("(this=%p,0x%08lx,0x%08lx)\n",This,(DWORD)hwnd,dwflags);
273
274   if (TRACE_ON(dinput))
275     _dump_cooperativelevel_DI(dwflags);
276
277   /* Store the window which asks for the mouse */
278   if (!hwnd)
279     hwnd = GetDesktopWindow();
280   This->win = hwnd;
281   This->dwCoopLevel = dwflags;
282
283   return 0;
284 }
285
286
287 /******************************************************************************
288   *     SetDataFormat : the application can choose the format of the data
289   *   the device driver sends back with GetDeviceState.
290   *
291   *   For the moment, only the "standard" configuration (c_dfDIMouse) is supported
292   *   in absolute and relative mode.
293   */
294 static HRESULT WINAPI SysMouseAImpl_SetDataFormat(
295         LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
296 )
297 {
298   ICOM_THIS(SysMouseAImpl,iface);
299   int i;
300
301   TRACE("(this=%p,%p)\n",This,df);
302
303   TRACE("(df.dwSize=%ld)\n",df->dwSize);
304   TRACE("(df.dwObjsize=%ld)\n",df->dwObjSize);
305   TRACE("(df.dwFlags=0x%08lx)\n",df->dwFlags);
306   TRACE("(df.dwDataSize=%ld)\n",df->dwDataSize);
307   TRACE("(df.dwNumObjs=%ld)\n",df->dwNumObjs);
308
309   for (i=0;i<df->dwNumObjs;i++) {
310
311     TRACE("df.rgodf[%d].guid %s (%p)\n",i, debugstr_guid(df->rgodf[i].pguid), df->rgodf[i].pguid);
312     TRACE("df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
313     TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
314     TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
315   }
316
317   /* Check if the mouse is in absolute or relative mode */
318   if (df->dwFlags == DIDF_ABSAXIS)
319     This->absolute = 1;
320   else if (df->dwFlags == DIDF_RELAXIS)
321     This->absolute = 0;
322   else
323     ERR("Neither absolute nor relative flag set\n");
324
325   /* Store the new data format */
326   This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
327   memcpy(This->df, df, df->dwSize);
328   This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
329   memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
330
331   /* Prepare all the data-conversion filters */
332   This->wine_df = create_DataFormat(&(Wine_InternalMouseFormat), df, This->offset_array);
333
334   return 0;
335 }
336
337 /* low-level mouse hook */
338 static LRESULT CALLBACK dinput_mouse_hook( int code, WPARAM wparam, LPARAM lparam )
339 {
340     LRESULT ret;
341     MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam;
342     SysMouseAImpl* This = (SysMouseAImpl*) current_lock;
343     DWORD dwCoop;
344     static long last_event = 0;
345     int wdata;
346
347     if (code != HC_ACTION) return CallNextHookEx( This->hook, code, wparam, lparam );
348
349     EnterCriticalSection(&(This->crit));
350     dwCoop = This->dwCoopLevel;
351
352     /* Only allow mouse events every 10 ms.
353      * This is to allow the cursor to start acceleration before
354      * the warps happen. But if it involves a mouse button event we
355      * allow it since we dont want to loose the clicks.
356      */
357     if (((GetCurrentTime() - last_event) < 10)
358         && wparam == WM_MOUSEMOVE)
359       goto end;
360     else last_event = GetCurrentTime();
361
362     /* Mouse moved -> send event if asked */
363     if (This->hEvent)
364         SetEvent(This->hEvent);
365
366     if (wparam == WM_MOUSEMOVE) {
367         if (This->absolute) {
368           if (hook->pt.x != This->prevX)
369             GEN_EVENT(This->offset_array[WINE_MOUSE_X_POSITION], hook->pt.x, hook->time, 0);
370           if (hook->pt.y != This->prevY)
371             GEN_EVENT(This->offset_array[WINE_MOUSE_Y_POSITION], hook->pt.y, hook->time, 0);
372         } else {
373           /* Now, warp handling */
374           if ((This->need_warp == WARP_STARTED) &&
375               (hook->pt.x == This->mapped_center.x) && (hook->pt.y == This->mapped_center.y)) {
376             /* Warp has been done... */
377             This->need_warp = WARP_DONE;
378             goto end;
379           }
380
381           /* Relative mouse input with absolute mouse event : the real fun starts here... */
382           if ((This->need_warp == WARP_NEEDED) ||
383               (This->need_warp == WARP_STARTED)) {
384             if (hook->pt.x != This->prevX)
385               GEN_EVENT(This->offset_array[WINE_MOUSE_X_POSITION], hook->pt.x - This->prevX, hook->time, (This->dinput->evsequence)++);
386             if (hook->pt.y != This->prevY)
387               GEN_EVENT(This->offset_array[WINE_MOUSE_Y_POSITION], hook->pt.y - This->prevY, hook->time, (This->dinput->evsequence)++);
388           } else {
389             /* This is the first time the event handler has been called after a
390                GetDeviceData or GetDeviceState. */
391             if (hook->pt.x != This->mapped_center.x) {
392               GEN_EVENT(This->offset_array[WINE_MOUSE_X_POSITION], hook->pt.x - This->mapped_center.x, hook->time, (This->dinput->evsequence)++);
393               This->need_warp = WARP_NEEDED;
394             }
395
396             if (hook->pt.y != This->mapped_center.y) {
397               GEN_EVENT(This->offset_array[WINE_MOUSE_Y_POSITION], hook->pt.y - This->mapped_center.y, hook->time, (This->dinput->evsequence)++);
398               This->need_warp = WARP_NEEDED;
399             }
400           }
401         }
402
403         This->prevX = hook->pt.x;
404         This->prevY = hook->pt.y;
405
406         if (This->absolute) {
407           This->m_state.lX = hook->pt.x;
408           This->m_state.lY = hook->pt.y;
409         } else {
410           This->m_state.lX = hook->pt.x - This->mapped_center.x;
411           This->m_state.lY = hook->pt.y - This->mapped_center.y;
412         }
413     }
414
415     TRACE(" msg %x pt %ld %ld (W=%d)\n",
416           wparam, hook->pt.x, hook->pt.y, (!This->absolute) && This->need_warp );
417
418     switch(wparam)
419     {
420     case WM_LBUTTONDOWN:
421         GEN_EVENT(This->offset_array[WINE_MOUSE_L_POSITION], 0xFF,
422                   hook->time, This->dinput->evsequence++);
423         This->m_state.rgbButtons[0] = 0xFF;
424         break;
425     case WM_LBUTTONUP:
426         GEN_EVENT(This->offset_array[WINE_MOUSE_L_POSITION], 0x00,
427                   hook->time, This->dinput->evsequence++);
428         This->m_state.rgbButtons[0] = 0x00;
429         break;
430     case WM_RBUTTONDOWN:
431         GEN_EVENT(This->offset_array[WINE_MOUSE_R_POSITION], 0xFF,
432                   hook->time, This->dinput->evsequence++);
433         This->m_state.rgbButtons[1] = 0xFF;
434         break;
435     case WM_RBUTTONUP:
436         GEN_EVENT(This->offset_array[WINE_MOUSE_R_POSITION], 0x00,
437                   hook->time, This->dinput->evsequence++);
438         This->m_state.rgbButtons[1] = 0x00;
439         break;
440     case WM_MBUTTONDOWN:
441         GEN_EVENT(This->offset_array[WINE_MOUSE_M_POSITION], 0xFF,
442                   hook->time, This->dinput->evsequence++);
443         This->m_state.rgbButtons[2] = 0xFF;
444         break;
445     case WM_MBUTTONUP:
446         GEN_EVENT(This->offset_array[WINE_MOUSE_M_POSITION], 0x00,
447                   hook->time, This->dinput->evsequence++);
448         This->m_state.rgbButtons[2] = 0x00;
449         break;
450     case WM_MOUSEWHEEL:
451         wdata = (short)HIWORD(hook->mouseData);
452         GEN_EVENT(This->offset_array[WINE_MOUSE_Z_POSITION], wdata,
453                   hook->time, This->dinput->evsequence++);
454         This->m_state.lZ += wdata;
455         break;
456     }
457
458   TRACE("(X: %ld - Y: %ld   L: %02x M: %02x R: %02x)\n",
459         This->m_state.lX, This->m_state.lY,
460         This->m_state.rgbButtons[0], This->m_state.rgbButtons[2], This->m_state.rgbButtons[1]);
461
462 end:
463   LeaveCriticalSection(&(This->crit));
464
465   if (dwCoop & DISCL_NONEXCLUSIVE)
466   { /* pass the events down to previous handlers (e.g. win32 input) */
467       ret = CallNextHookEx( This->hook, code, wparam, lparam );
468   }
469   else ret = 1;  /* ignore message */
470   return ret;
471 }
472
473
474 static void dinput_window_check(SysMouseAImpl* This)
475 {
476   RECT rect;
477   DWORD centerX, centerY;
478
479   /* make sure the window hasn't moved */
480   GetWindowRect(This->win, &rect);
481   centerX = (rect.right  - rect.left) / 2;
482   centerY = (rect.bottom - rect.top ) / 2;
483   if (This->win_centerX != centerX || This->win_centerY != centerY) {
484     This->win_centerX = centerX;
485     This->win_centerY = centerY;
486   }
487   This->mapped_center.x = This->win_centerX;
488   This->mapped_center.y = This->win_centerY;
489   MapWindowPoints(This->win, HWND_DESKTOP, &This->mapped_center, 1);
490 }
491
492
493 /******************************************************************************
494   *     Acquire : gets exclusive control of the mouse
495   */
496 static HRESULT WINAPI SysMouseAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
497 {
498   ICOM_THIS(SysMouseAImpl,iface);
499   RECT  rect;
500
501   TRACE("(this=%p)\n",This);
502
503   if (This->acquired == 0) {
504     POINT point;
505
506     /* Store (in a global variable) the current lock */
507     current_lock = (IDirectInputDevice8A*)This;
508
509     /* Init the mouse state */
510     if (This->absolute) {
511       GetCursorPos( &point );
512       This->m_state.lX = point.x;
513       This->m_state.lY = point.y;
514       This->prevX = point.x;
515       This->prevY = point.y;
516     } else {
517       This->m_state.lX = 0;
518       This->m_state.lY = 0;
519     }
520     This->m_state.lZ = 0;
521     This->m_state.rgbButtons[0] = (GetKeyState(VK_LBUTTON) ? 0xFF : 0x00);
522     This->m_state.rgbButtons[1] = (GetKeyState(VK_MBUTTON) ? 0xFF : 0x00);
523     This->m_state.rgbButtons[2] = (GetKeyState(VK_RBUTTON) ? 0xFF : 0x00);
524
525     /* Install our mouse hook */
526     if (This->dwCoopLevel & DISCL_EXCLUSIVE)
527       ShowCursor(FALSE); /* hide cursor */
528     This->hook = SetWindowsHookExW( WH_MOUSE_LL, dinput_mouse_hook, 0, 0 );
529
530     /* Get the window dimension and find the center */
531     GetWindowRect(This->win, &rect);
532     This->win_centerX = (rect.right  - rect.left) / 2;
533     This->win_centerY = (rect.bottom - rect.top ) / 2;
534
535     /* Warp the mouse to the center of the window */
536     if (This->absolute == 0) {
537       This->mapped_center.x = This->win_centerX;
538       This->mapped_center.y = This->win_centerY;
539       MapWindowPoints(This->win, HWND_DESKTOP, &This->mapped_center, 1);
540       TRACE("Warping mouse to %ld - %ld\n", This->mapped_center.x, This->mapped_center.y);
541       SetCursorPos( This->mapped_center.x, This->mapped_center.y );
542 #ifdef MOUSE_HACK
543       This->need_warp = WARP_DONE;
544 #else
545       This->need_warp = WARP_STARTED;
546 #endif
547     }
548
549     This->acquired = 1;
550     return DI_OK;
551   }
552   return S_FALSE;
553 }
554
555 /******************************************************************************
556   *     Unacquire : frees the mouse
557   */
558 static HRESULT WINAPI SysMouseAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
559 {
560     ICOM_THIS(SysMouseAImpl,iface);
561
562     TRACE("(this=%p)\n",This);
563
564     if (This->acquired)
565     {
566         /* Reinstall previous mouse event handler */
567         if (This->hook) {
568           UnhookWindowsHookEx( This->hook );
569           This->hook = 0;
570           if (This->dwCoopLevel & DISCL_EXCLUSIVE)
571             ShowCursor(TRUE); /* show cursor */
572         }
573
574         /* No more locks */
575         current_lock = NULL;
576
577         /* Unacquire device */
578         This->acquired = 0;
579     }
580     else
581         ERR("Unacquiring a not-acquired device !!!\n");
582
583     return DI_OK;
584 }
585
586 /******************************************************************************
587   *     GetDeviceState : returns the "state" of the mouse.
588   *
589   *   For the moment, only the "standard" return structure (DIMOUSESTATE) is
590   *   supported.
591   */
592 static HRESULT WINAPI SysMouseAImpl_GetDeviceState(
593         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
594 ) {
595   ICOM_THIS(SysMouseAImpl,iface);
596
597   EnterCriticalSection(&(This->crit));
598   TRACE("(this=%p,0x%08lx,%p): \n",This,len,ptr);
599
600   /* Copy the current mouse state */
601   fill_DataFormat(ptr, &(This->m_state), This->wine_df);
602
603   /* Initialize the buffer when in relative mode */
604   if (This->absolute == 0) {
605     This->m_state.lX = 0;
606     This->m_state.lY = 0;
607     This->m_state.lZ = 0;
608   }
609
610   /* Check if we need to do a mouse warping */
611   if (This->need_warp == WARP_NEEDED) {
612     dinput_window_check(This);
613     TRACE("Warping mouse to %ld - %ld\n", This->mapped_center.x, This->mapped_center.y);
614     SetCursorPos( This->mapped_center.x, This->mapped_center.y );
615
616 #ifdef MOUSE_HACK
617     This->need_warp = WARP_DONE;
618 #else
619     This->need_warp = WARP_STARTED;
620 #endif
621   }
622
623   LeaveCriticalSection(&(This->crit));
624
625   TRACE("(X: %ld - Y: %ld   L: %02x M: %02x R: %02x)\n",
626         This->m_state.lX, This->m_state.lY,
627         This->m_state.rgbButtons[0], This->m_state.rgbButtons[2], This->m_state.rgbButtons[1]);
628
629   return 0;
630 }
631
632 /******************************************************************************
633   *     GetDeviceState : gets buffered input data.
634   */
635 static HRESULT WINAPI SysMouseAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
636                                               DWORD dodsize,
637                                               LPDIDEVICEOBJECTDATA dod,
638                                               LPDWORD entries,
639                                               DWORD flags
640 ) {
641   ICOM_THIS(SysMouseAImpl,iface);
642   DWORD len, nqtail;
643
644   EnterCriticalSection(&(This->crit));
645   TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
646
647   len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
648       + (This->queue_head - This->queue_tail);
649   if (len > *entries) len = *entries;
650
651   if (dod == NULL) {
652     if (len)
653         TRACE("Application discarding %ld event(s).\n", len);
654
655     *entries = len;
656     nqtail = This->queue_tail + len;
657     while (nqtail >= This->queue_len) nqtail -= This->queue_len;
658   } else {
659     if (dodsize < sizeof(DIDEVICEOBJECTDATA)) {
660       ERR("Wrong structure size !\n");
661       LeaveCriticalSection(&(This->crit));
662       return DIERR_INVALIDPARAM;
663     }
664
665     if (len)
666         TRACE("Application retrieving %ld event(s).\n", len);
667
668     *entries = 0;
669     nqtail = This->queue_tail;
670     while (len) {
671       DWORD span = ((This->queue_head < nqtail) ? This->queue_len : This->queue_head)
672                  - nqtail;
673       if (span > len) span = len;
674       /* Copy the buffered data into the application queue */
675       memcpy(dod + *entries, This->data_queue + nqtail, span * dodsize);
676       /* Advance position */
677       nqtail += span;
678       if (nqtail >= This->queue_len) nqtail -= This->queue_len;
679       *entries += span;
680       len -= span;
681     }
682   }
683   if (!(flags & DIGDD_PEEK))
684     This->queue_tail = nqtail;
685
686   LeaveCriticalSection(&(This->crit));
687
688   /* Check if we need to do a mouse warping */
689   if (This->need_warp == WARP_NEEDED) {
690     dinput_window_check(This);
691     TRACE("Warping mouse to %ld - %ld\n", This->mapped_center.x, This->mapped_center.y);
692     SetCursorPos( This->mapped_center.x, This->mapped_center.y );
693
694 #ifdef MOUSE_HACK
695     This->need_warp = WARP_DONE;
696 #else
697     This->need_warp = WARP_STARTED;
698 #endif
699   }
700   return 0;
701 }
702
703 /******************************************************************************
704   *     SetProperty : change input device properties
705   */
706 static HRESULT WINAPI SysMouseAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
707                                             REFGUID rguid,
708                                             LPCDIPROPHEADER ph)
709 {
710   ICOM_THIS(SysMouseAImpl,iface);
711
712   TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
713
714   if (!HIWORD(rguid)) {
715     switch ((DWORD)rguid) {
716     case (DWORD) DIPROP_BUFFERSIZE: {
717       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
718
719       TRACE("buffersize = %ld\n",pd->dwData);
720
721       This->data_queue = (LPDIDEVICEOBJECTDATA)HeapAlloc(GetProcessHeap(),0,
722                                                           pd->dwData * sizeof(DIDEVICEOBJECTDATA));
723       This->queue_head = 0;
724       This->queue_tail = 0;
725       This->queue_len  = pd->dwData;
726       break;
727     }
728     case (DWORD) DIPROP_AXISMODE: {
729       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
730       This->absolute = !(pd->dwData);
731       TRACE("Using %s coordinates mode now\n", This->absolute ? "absolute" : "relative");
732       break;
733     }
734     default:
735       FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
736       break;
737     }
738   }
739
740   return 0;
741 }
742
743 /******************************************************************************
744   *     GetProperty : get input device properties
745   */
746 static HRESULT WINAPI SysMouseAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
747                                                 REFGUID rguid,
748                                                 LPDIPROPHEADER pdiph)
749 {
750   ICOM_THIS(SysMouseAImpl,iface);
751
752   TRACE("(this=%p,%s,%p): stub!\n",
753         iface, debugstr_guid(rguid), pdiph);
754
755   if (TRACE_ON(dinput))
756     _dump_DIPROPHEADER(pdiph);
757
758   if (!HIWORD(rguid)) {
759     switch ((DWORD)rguid) {
760     case (DWORD) DIPROP_BUFFERSIZE: {
761       LPDIPROPDWORD     pd = (LPDIPROPDWORD)pdiph;
762
763       TRACE(" return buffersize = %d\n",This->queue_len);
764       pd->dwData = This->queue_len;
765       break;
766     }
767
768     case (DWORD) DIPROP_GRANULARITY: {
769       LPDIPROPDWORD pr = (LPDIPROPDWORD) pdiph;
770
771       /* We'll just assume that the app asks about the Z axis */
772       pr->dwData = WHEEL_DELTA;
773
774       break;
775     }
776
777     case (DWORD) DIPROP_RANGE: {
778       LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
779
780       if ((pdiph->dwHow == DIPH_BYID) &&
781           ((pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS)) ||
782            (pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS)))) {
783         /* Querying the range of either the X or the Y axis.  As I do
784            not know the range, do as if the range were
785            unrestricted...*/
786         pr->lMin = DIPROPRANGE_NOMIN;
787         pr->lMax = DIPROPRANGE_NOMAX;
788       }
789
790       break;
791     }
792
793     default:
794       FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
795       break;
796     }
797   }
798
799
800   return DI_OK;
801 }
802
803
804
805 /******************************************************************************
806   *     SetEventNotification : specifies event to be sent on state change
807   */
808 static HRESULT WINAPI SysMouseAImpl_SetEventNotification(LPDIRECTINPUTDEVICE8A iface,
809                                                          HANDLE hnd) {
810   ICOM_THIS(SysMouseAImpl,iface);
811
812   TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
813
814   This->hEvent = hnd;
815
816   return DI_OK;
817 }
818
819 /******************************************************************************
820   *     GetCapabilities : get the device capablitites
821   */
822 static HRESULT WINAPI SysMouseAImpl_GetCapabilities(
823         LPDIRECTINPUTDEVICE8A iface,
824         LPDIDEVCAPS lpDIDevCaps)
825 {
826   ICOM_THIS(SysMouseAImpl,iface);
827
828   TRACE("(this=%p,%p)\n",This,lpDIDevCaps);
829
830   if (lpDIDevCaps->dwSize == sizeof(DIDEVCAPS)) {
831     lpDIDevCaps->dwFlags = DIDC_ATTACHED;
832     lpDIDevCaps->dwDevType = DIDEVTYPE_MOUSE;
833     lpDIDevCaps->dwAxes = 3;
834     lpDIDevCaps->dwButtons = 3;
835     lpDIDevCaps->dwPOVs = 0;
836     lpDIDevCaps->dwFFSamplePeriod = 0;
837     lpDIDevCaps->dwFFMinTimeResolution = 0;
838     lpDIDevCaps->dwFirmwareRevision = 100;
839     lpDIDevCaps->dwHardwareRevision = 100;
840     lpDIDevCaps->dwFFDriverVersion = 0;
841   } else {
842     /* DirectX 3.0 */
843     FIXME("DirectX 3.0 not supported....\n");
844   }
845
846   return DI_OK;
847 }
848
849
850 /******************************************************************************
851   *     EnumObjects : enumerate the different buttons and axis...
852   */
853 static HRESULT WINAPI SysMouseAImpl_EnumObjects(
854         LPDIRECTINPUTDEVICE8A iface,
855         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
856         LPVOID lpvRef,
857         DWORD dwFlags)
858 {
859   ICOM_THIS(SysMouseAImpl,iface);
860   DIDEVICEOBJECTINSTANCEA ddoi;
861
862   TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
863   if (TRACE_ON(dinput)) {
864     DPRINTF("  - flags = ");
865     _dump_EnumObjects_flags(dwFlags);
866     DPRINTF("\n");
867   }
868
869   /* Only the fields till dwFFMaxForce are relevant */
870   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
871
872   /* In a mouse, we have : two relative axis and three buttons */
873   if ((dwFlags == DIDFT_ALL) ||
874       (dwFlags & DIDFT_AXIS)) {
875     /* X axis */
876     ddoi.guidType = GUID_XAxis;
877     ddoi.dwOfs = This->offset_array[WINE_MOUSE_X_POSITION];
878     ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS;
879     strcpy(ddoi.tszName, "X-Axis");
880     _dump_OBJECTINSTANCEA(&ddoi);
881     if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
882
883     /* Y axis */
884     ddoi.guidType = GUID_YAxis;
885     ddoi.dwOfs = This->offset_array[WINE_MOUSE_Y_POSITION];
886     ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS;
887     strcpy(ddoi.tszName, "Y-Axis");
888     _dump_OBJECTINSTANCEA(&ddoi);
889     if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
890
891     /* Z axis */
892     ddoi.guidType = GUID_ZAxis;
893     ddoi.dwOfs = This->offset_array[WINE_MOUSE_Z_POSITION];
894     ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_Z_AXIS_INSTANCE) | DIDFT_RELAXIS;
895     strcpy(ddoi.tszName, "Z-Axis");
896     _dump_OBJECTINSTANCEA(&ddoi);
897     if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
898   }
899
900   if ((dwFlags == DIDFT_ALL) ||
901       (dwFlags & DIDFT_BUTTON)) {
902     ddoi.guidType = GUID_Button;
903
904     /* Left button */
905     ddoi.dwOfs = This->offset_array[WINE_MOUSE_L_POSITION];
906     ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_L_BUTTON_INSTANCE) | DIDFT_PSHBUTTON;
907     strcpy(ddoi.tszName, "Left-Button");
908     _dump_OBJECTINSTANCEA(&ddoi);
909     if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
910
911     /* Right button */
912     ddoi.dwOfs = This->offset_array[WINE_MOUSE_R_POSITION];
913     ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_R_BUTTON_INSTANCE) | DIDFT_PSHBUTTON;
914     strcpy(ddoi.tszName, "Right-Button");
915     _dump_OBJECTINSTANCEA(&ddoi);
916     if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
917
918     /* Middle button */
919     ddoi.dwOfs = This->offset_array[WINE_MOUSE_M_POSITION];
920     ddoi.dwType = DIDFT_MAKEINSTANCE(WINE_MOUSE_M_BUTTON_INSTANCE) | DIDFT_PSHBUTTON;
921     strcpy(ddoi.tszName, "Middle-Button");
922     _dump_OBJECTINSTANCEA(&ddoi);
923     if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
924   }
925
926   return DI_OK;
927 }
928
929
930 static ICOM_VTABLE(IDirectInputDevice8A) SysMouseAvt =
931 {
932         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
933         IDirectInputDevice2AImpl_QueryInterface,
934         IDirectInputDevice2AImpl_AddRef,
935         SysMouseAImpl_Release,
936         SysMouseAImpl_GetCapabilities,
937         SysMouseAImpl_EnumObjects,
938         SysMouseAImpl_GetProperty,
939         SysMouseAImpl_SetProperty,
940         SysMouseAImpl_Acquire,
941         SysMouseAImpl_Unacquire,
942         SysMouseAImpl_GetDeviceState,
943         SysMouseAImpl_GetDeviceData,
944         SysMouseAImpl_SetDataFormat,
945         SysMouseAImpl_SetEventNotification,
946         SysMouseAImpl_SetCooperativeLevel,
947         IDirectInputDevice2AImpl_GetObjectInfo,
948         IDirectInputDevice2AImpl_GetDeviceInfo,
949         IDirectInputDevice2AImpl_RunControlPanel,
950         IDirectInputDevice2AImpl_Initialize,
951         IDirectInputDevice2AImpl_CreateEffect,
952         IDirectInputDevice2AImpl_EnumEffects,
953         IDirectInputDevice2AImpl_GetEffectInfo,
954         IDirectInputDevice2AImpl_GetForceFeedbackState,
955         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
956         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
957         IDirectInputDevice2AImpl_Escape,
958         IDirectInputDevice2AImpl_Poll,
959         IDirectInputDevice2AImpl_SendDeviceData,
960         IDirectInputDevice7AImpl_EnumEffectsInFile,
961         IDirectInputDevice7AImpl_WriteEffectToFile,
962         IDirectInputDevice8AImpl_BuildActionMap,
963         IDirectInputDevice8AImpl_SetActionMap,
964         IDirectInputDevice8AImpl_GetImageInfo
965 };