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