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