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