xref: /reactos/dll/directx/wine/dinput/mouse.c (revision c2c66aff)
1 /*		DirectInput Mouse device
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "dinput_private.h"
23 
24 /* Wine mouse driver object instances */
25 #define WINE_MOUSE_X_AXIS_INSTANCE   0
26 #define WINE_MOUSE_Y_AXIS_INSTANCE   1
27 #define WINE_MOUSE_Z_AXIS_INSTANCE   2
28 #define WINE_MOUSE_BUTTONS_INSTANCE  3
29 
30 static const IDirectInputDevice8AVtbl SysMouseAvt;
31 static const IDirectInputDevice8WVtbl SysMouseWvt;
32 
33 typedef struct SysMouseImpl SysMouseImpl;
34 
35 typedef enum
36 {
37     WARP_DEFAULT,
38     WARP_DISABLE,
39     WARP_FORCE_ON
40 } WARP_MOUSE;
41 
42 struct SysMouseImpl
43 {
44     struct IDirectInputDeviceImpl   base;
45 
46     /* SysMouseAImpl */
47     /* These are used in case of relative -> absolute transitions */
48     POINT                           org_coords;
49     BOOL                            clipped;
50     /* warping: whether we need to move mouse back to middle once we
51      * reach window borders (for e.g. shooters, "surface movement" games) */
52     BOOL                            need_warp;
53     DWORD                           last_warped;
54 
55     /* This is for mouse reporting. */
56     DIMOUSESTATE2                   m_state;
57 
58     WARP_MOUSE                      warp_override;
59 };
60 
61 static inline SysMouseImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
62 {
63     return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface), SysMouseImpl, base);
64 }
65 static inline SysMouseImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
66 {
67     return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface), SysMouseImpl, base);
68 }
69 
70 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(SysMouseImpl *This)
71 {
72     return &This->base.IDirectInputDevice8W_iface;
73 }
74 
75 static int dinput_mouse_hook( LPDIRECTINPUTDEVICE8A iface, WPARAM wparam, LPARAM lparam );
76 
77 const GUID DInput_Wine_Mouse_GUID = { /* 9e573ed8-7734-11d2-8d4a-23903fb6bdf7 */
78     0x9e573ed8, 0x7734, 0x11d2, {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
79 };
80 
81 static void _dump_mouse_state(const DIMOUSESTATE2 *m_state)
82 {
83     int i;
84 
85     if (!TRACE_ON(dinput)) return;
86 
87     TRACE("(X: %d Y: %d Z: %d", m_state->lX, m_state->lY, m_state->lZ);
88     for (i = 0; i < 5; i++) TRACE(" B%d: %02x", i, m_state->rgbButtons[i]);
89     TRACE(")\n");
90 }
91 
92 static void fill_mouse_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version) {
93     DWORD dwSize;
94     DIDEVICEINSTANCEA ddi;
95 
96     dwSize = lpddi->dwSize;
97 
98     TRACE("%d %p\n", dwSize, lpddi);
99 
100     memset(lpddi, 0, dwSize);
101     memset(&ddi, 0, sizeof(ddi));
102 
103     ddi.dwSize = dwSize;
104     ddi.guidInstance = GUID_SysMouse;/* DInput's GUID */
105     ddi.guidProduct = DInput_Wine_Mouse_GUID; /* Vendor's GUID */
106     if (version >= 0x0800)
107         ddi.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
108     else
109         ddi.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
110     strcpy(ddi.tszInstanceName, "Mouse");
111     strcpy(ddi.tszProductName, "Wine Mouse");
112 
113     memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
114 }
115 
116 static void fill_mouse_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version) {
117     DWORD dwSize;
118     DIDEVICEINSTANCEW ddi;
119 
120     dwSize = lpddi->dwSize;
121 
122     TRACE("%d %p\n", dwSize, lpddi);
123 
124     memset(lpddi, 0, dwSize);
125     memset(&ddi, 0, sizeof(ddi));
126 
127     ddi.dwSize = dwSize;
128     ddi.guidInstance = GUID_SysMouse;/* DInput's GUID */
129     ddi.guidProduct = DInput_Wine_Mouse_GUID; /* Vendor's GUID */
130     if (version >= 0x0800)
131         ddi.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
132     else
133         ddi.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
134     MultiByteToWideChar(CP_ACP, 0, "Mouse", -1, ddi.tszInstanceName, MAX_PATH);
135     MultiByteToWideChar(CP_ACP, 0, "Wine Mouse", -1, ddi.tszProductName, MAX_PATH);
136 
137     memcpy(lpddi, &ddi, (dwSize < sizeof(ddi) ? dwSize : sizeof(ddi)));
138 }
139 
140 static HRESULT mousedev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
141 {
142     if (id != 0)
143         return E_FAIL;
144 
145     if ((dwDevType == 0) ||
146 	((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
147 	(((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
148 	TRACE("Enumerating the mouse device\n");
149 
150 	fill_mouse_dideviceinstanceA(lpddi, version);
151 
152 	return S_OK;
153     }
154 
155     return S_FALSE;
156 }
157 
158 static HRESULT mousedev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
159 {
160     if (id != 0)
161         return E_FAIL;
162 
163     if ((dwDevType == 0) ||
164 	((dwDevType == DIDEVTYPE_MOUSE) && (version < 0x0800)) ||
165 	(((dwDevType == DI8DEVCLASS_POINTER) || (dwDevType == DI8DEVTYPE_MOUSE)) && (version >= 0x0800))) {
166 	TRACE("Enumerating the mouse device\n");
167 
168 	fill_mouse_dideviceinstanceW(lpddi, version);
169 
170 	return S_OK;
171     }
172 
173     return S_FALSE;
174 }
175 
176 static SysMouseImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput)
177 {
178     SysMouseImpl* newDevice;
179     LPDIDATAFORMAT df = NULL;
180     unsigned i;
181     char buffer[20];
182     HKEY hkey, appkey;
183 
184     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysMouseImpl));
185     if (!newDevice) return NULL;
186     newDevice->base.IDirectInputDevice8A_iface.lpVtbl = &SysMouseAvt;
187     newDevice->base.IDirectInputDevice8W_iface.lpVtbl = &SysMouseWvt;
188     newDevice->base.ref = 1;
189     newDevice->base.dwCoopLevel = DISCL_NONEXCLUSIVE | DISCL_BACKGROUND;
190     newDevice->base.guid = *rguid;
191     InitializeCriticalSection(&newDevice->base.crit);
192     newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": SysMouseImpl*->base.crit");
193     newDevice->base.dinput = dinput;
194     newDevice->base.event_proc = dinput_mouse_hook;
195 
196     get_app_key(&hkey, &appkey);
197     if (!get_config_key(hkey, appkey, "MouseWarpOverride", buffer, sizeof(buffer)))
198     {
199         if (!strcasecmp(buffer, "disable"))
200             newDevice->warp_override = WARP_DISABLE;
201         else if (!strcasecmp(buffer, "force"))
202             newDevice->warp_override = WARP_FORCE_ON;
203     }
204     if (appkey) RegCloseKey(appkey);
205     if (hkey) RegCloseKey(hkey);
206 
207     /* Create copy of default data format */
208     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIMouse2.dwSize))) goto failed;
209     memcpy(df, &c_dfDIMouse2, c_dfDIMouse2.dwSize);
210     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
211     memcpy(df->rgodf, c_dfDIMouse2.rgodf, df->dwNumObjs * df->dwObjSize);
212 
213     /* Because we don't do any detection yet just modify instance and type */
214     for (i = 0; i < df->dwNumObjs; i++)
215         if (DIDFT_GETTYPE(df->rgodf[i].dwType) & DIDFT_AXIS)
216             df->rgodf[i].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_RELAXIS;
217         else
218             df->rgodf[i].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
219 
220     newDevice->base.data_format.wine_df = df;
221     IDirectInput_AddRef(&newDevice->base.dinput->IDirectInput7A_iface);
222 
223     EnterCriticalSection(&dinput->crit);
224     list_add_tail(&dinput->devices_list, &newDevice->base.entry);
225     LeaveCriticalSection(&dinput->crit);
226 
227     return newDevice;
228 
229 failed:
230     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
231     HeapFree(GetProcessHeap(), 0, df);
232     HeapFree(GetProcessHeap(), 0, newDevice);
233     return NULL;
234 }
235 
236 static HRESULT mousedev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
237 {
238     TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
239     *pdev = NULL;
240 
241     if (IsEqualGUID(&GUID_SysMouse, rguid) ||        /* Generic Mouse */
242         IsEqualGUID(&DInput_Wine_Mouse_GUID, rguid)) /* Wine Mouse */
243     {
244         SysMouseImpl *This;
245 
246         if (riid == NULL)
247             ;/* nothing */
248         else if (IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
249                  IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
250                  IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
251                  IsEqualGUID(&IID_IDirectInputDevice8A, riid))
252         {
253             unicode = 0;
254         }
255         else if (IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
256                  IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
257                  IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
258                  IsEqualGUID(&IID_IDirectInputDevice8W, riid))
259         {
260             unicode = 1;
261         }
262         else
263         {
264             WARN("no interface\n");
265             return DIERR_NOINTERFACE;
266         }
267 
268         This = alloc_device(rguid, dinput);
269         TRACE("Created a Mouse device (%p)\n", This);
270 
271         if (!This) return DIERR_OUTOFMEMORY;
272 
273         if (unicode)
274             *pdev = &This->base.IDirectInputDevice8W_iface;
275         else
276             *pdev = &This->base.IDirectInputDevice8A_iface;
277 
278         return DI_OK;
279     }
280 
281     return DIERR_DEVICENOTREG;
282 }
283 
284 const struct dinput_device mouse_device = {
285     "Wine mouse driver",
286     mousedev_enum_deviceA,
287     mousedev_enum_deviceW,
288     mousedev_create_device
289 };
290 
291 /******************************************************************************
292  *	SysMouseA (DInput Mouse support)
293  */
294 
295 /* low-level mouse hook */
296 static int dinput_mouse_hook( LPDIRECTINPUTDEVICE8A iface, WPARAM wparam, LPARAM lparam )
297 {
298     MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)lparam;
299     SysMouseImpl* This = impl_from_IDirectInputDevice8A(iface);
300     int wdata = 0, inst_id = -1, ret = 0;
301 
302     TRACE("msg %lx @ (%d %d)\n", wparam, hook->pt.x, hook->pt.y);
303 
304     EnterCriticalSection(&This->base.crit);
305 
306     switch(wparam) {
307         case WM_MOUSEMOVE:
308         {
309             POINT pt, pt1;
310 
311             GetCursorPos(&pt);
312             This->m_state.lX += pt.x = hook->pt.x - pt.x;
313             This->m_state.lY += pt.y = hook->pt.y - pt.y;
314 
315             if (This->base.data_format.user_df->dwFlags & DIDF_ABSAXIS)
316             {
317                 pt1.x = This->m_state.lX;
318                 pt1.y = This->m_state.lY;
319             } else
320                 pt1 = pt;
321 
322             if (pt.x)
323             {
324                 inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS;
325                 wdata = pt1.x;
326             }
327             if (pt.y)
328             {
329                 /* Already have X, need to queue it */
330                 if (inst_id != -1)
331                     queue_event(iface, inst_id,
332                                 wdata, GetCurrentTime(), This->base.dinput->evsequence);
333                 inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS;
334                 wdata = pt1.y;
335             }
336 
337             if (pt.x || pt.y)
338             {
339                 if ((This->warp_override == WARP_FORCE_ON) ||
340                     (This->warp_override != WARP_DISABLE && (This->base.dwCoopLevel & DISCL_EXCLUSIVE)))
341                     This->need_warp = TRUE;
342             }
343             break;
344         }
345         case WM_MOUSEWHEEL:
346             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_Z_AXIS_INSTANCE) | DIDFT_RELAXIS;
347             This->m_state.lZ += wdata = (short)HIWORD(hook->mouseData);
348             /* FarCry crashes if it gets a mouse wheel message */
349             /* FIXME: should probably filter out other messages too */
350             ret = This->clipped;
351             break;
352         case WM_LBUTTONDOWN:
353             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 0) | DIDFT_PSHBUTTON;
354             This->m_state.rgbButtons[0] = wdata = 0x80;
355 	    break;
356 	case WM_LBUTTONUP:
357             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 0) | DIDFT_PSHBUTTON;
358             This->m_state.rgbButtons[0] = wdata = 0x00;
359 	    break;
360 	case WM_RBUTTONDOWN:
361             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 1) | DIDFT_PSHBUTTON;
362             This->m_state.rgbButtons[1] = wdata = 0x80;
363 	    break;
364 	case WM_RBUTTONUP:
365             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 1) | DIDFT_PSHBUTTON;
366             This->m_state.rgbButtons[1] = wdata = 0x00;
367 	    break;
368 	case WM_MBUTTONDOWN:
369             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 2) | DIDFT_PSHBUTTON;
370             This->m_state.rgbButtons[2] = wdata = 0x80;
371 	    break;
372 	case WM_MBUTTONUP:
373             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 2) | DIDFT_PSHBUTTON;
374             This->m_state.rgbButtons[2] = wdata = 0x00;
375 	    break;
376         case WM_XBUTTONDOWN:
377             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 2 + HIWORD(hook->mouseData)) | DIDFT_PSHBUTTON;
378             This->m_state.rgbButtons[2 + HIWORD(hook->mouseData)] = wdata = 0x80;
379             break;
380         case WM_XBUTTONUP:
381             inst_id = DIDFT_MAKEINSTANCE(WINE_MOUSE_BUTTONS_INSTANCE + 2 + HIWORD(hook->mouseData)) | DIDFT_PSHBUTTON;
382             This->m_state.rgbButtons[2 + HIWORD(hook->mouseData)] = wdata = 0x00;
383             break;
384     }
385 
386 
387     if (inst_id != -1)
388     {
389         _dump_mouse_state(&This->m_state);
390         queue_event(iface, inst_id,
391                     wdata, GetCurrentTime(), This->base.dinput->evsequence++);
392     }
393 
394     LeaveCriticalSection(&This->base.crit);
395     return ret;
396 }
397 
398 static void warp_check( SysMouseImpl* This, BOOL force )
399 {
400     DWORD now = GetCurrentTime();
401     const DWORD interval = This->clipped ? 500 : 10;
402 
403     if (force || (This->need_warp && (now - This->last_warped > interval)))
404     {
405         RECT rect, new_rect;
406         POINT mapped_center;
407 
408         This->last_warped = now;
409         This->need_warp = FALSE;
410         if (!GetClientRect(This->base.win, &rect)) return;
411         MapWindowPoints( This->base.win, 0, (POINT *)&rect, 2 );
412         if (!This->clipped)
413         {
414             mapped_center.x = (rect.left + rect.right) / 2;
415             mapped_center.y = (rect.top + rect.bottom) / 2;
416             TRACE("Warping mouse to %d - %d\n", mapped_center.x, mapped_center.y);
417             SetCursorPos( mapped_center.x, mapped_center.y );
418         }
419         if (This->base.dwCoopLevel & DISCL_EXCLUSIVE)
420         {
421             /* make sure we clip even if the window covers the whole screen */
422             rect.left = max( rect.left, GetSystemMetrics( SM_XVIRTUALSCREEN ) + 1 );
423             rect.top = max( rect.top, GetSystemMetrics( SM_YVIRTUALSCREEN ) + 1 );
424             rect.right = min( rect.right, rect.left + GetSystemMetrics( SM_CXVIRTUALSCREEN ) - 2 );
425             rect.bottom = min( rect.bottom, rect.top + GetSystemMetrics( SM_CYVIRTUALSCREEN ) - 2 );
426             TRACE("Clipping mouse to %s\n", wine_dbgstr_rect( &rect ));
427             ClipCursor( &rect );
428             This->clipped = GetClipCursor( &new_rect ) && EqualRect( &rect, &new_rect );
429         }
430     }
431 }
432 
433 
434 /******************************************************************************
435   *     Acquire : gets exclusive control of the mouse
436   */
437 static HRESULT WINAPI SysMouseWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
438 {
439     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
440     POINT point;
441     HRESULT res;
442 
443     TRACE("(this=%p)\n",This);
444 
445     if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK) return res;
446 
447     /* Init the mouse state */
448     GetCursorPos( &point );
449     if (This->base.data_format.user_df->dwFlags & DIDF_ABSAXIS)
450     {
451       This->m_state.lX = point.x;
452       This->m_state.lY = point.y;
453     } else {
454       This->m_state.lX = 0;
455       This->m_state.lY = 0;
456       This->org_coords = point;
457     }
458     This->m_state.lZ = 0;
459     This->m_state.rgbButtons[0] = GetKeyState(VK_LBUTTON) & 0x80;
460     This->m_state.rgbButtons[1] = GetKeyState(VK_RBUTTON) & 0x80;
461     This->m_state.rgbButtons[2] = GetKeyState(VK_MBUTTON) & 0x80;
462 
463     if (This->base.dwCoopLevel & DISCL_EXCLUSIVE)
464     {
465         ShowCursor(FALSE); /* hide cursor */
466         warp_check( This, TRUE );
467     }
468     else if (This->warp_override == WARP_FORCE_ON)
469     {
470         /* Need a window to warp mouse in. */
471         if (!This->base.win) This->base.win = GetDesktopWindow();
472         warp_check( This, TRUE );
473     }
474     else if (This->clipped)
475     {
476         ClipCursor( NULL );
477         This->clipped = FALSE;
478     }
479 
480     return DI_OK;
481 }
482 
483 static HRESULT WINAPI SysMouseAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
484 {
485     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
486     return SysMouseWImpl_Acquire(IDirectInputDevice8W_from_impl(This));
487 }
488 
489 /******************************************************************************
490   *     Unacquire : frees the mouse
491   */
492 static HRESULT WINAPI SysMouseWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
493 {
494     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
495     HRESULT res;
496 
497     TRACE("(this=%p)\n",This);
498 
499     if ((res = IDirectInputDevice2WImpl_Unacquire(iface)) != DI_OK) return res;
500 
501     if (This->base.dwCoopLevel & DISCL_EXCLUSIVE)
502     {
503         ClipCursor(NULL);
504         ShowCursor(TRUE); /* show cursor */
505         This->clipped = FALSE;
506     }
507 
508     /* And put the mouse cursor back where it was at acquire time */
509     if (This->base.dwCoopLevel & DISCL_EXCLUSIVE || This->warp_override == WARP_FORCE_ON)
510     {
511         TRACE("warping mouse back to %s\n", wine_dbgstr_point(&This->org_coords));
512         SetCursorPos(This->org_coords.x, This->org_coords.y);
513     }
514 
515     return DI_OK;
516 }
517 
518 static HRESULT WINAPI SysMouseAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
519 {
520     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
521     return SysMouseWImpl_Unacquire(IDirectInputDevice8W_from_impl(This));
522 }
523 
524 /******************************************************************************
525   *     GetDeviceState : returns the "state" of the mouse.
526   *
527   *   For the moment, only the "standard" return structure (DIMOUSESTATE) is
528   *   supported.
529   */
530 static HRESULT WINAPI SysMouseWImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface, DWORD len, LPVOID ptr)
531 {
532     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
533     TRACE("(%p)->(%u,%p)\n", This, len, ptr);
534 
535     if(This->base.acquired == 0) return DIERR_NOTACQUIRED;
536 
537     check_dinput_events();
538 
539     EnterCriticalSection(&This->base.crit);
540     _dump_mouse_state(&This->m_state);
541 
542     /* Copy the current mouse state */
543     fill_DataFormat(ptr, len, &This->m_state, &This->base.data_format);
544 
545     /* Initialize the buffer when in relative mode */
546     if (!(This->base.data_format.user_df->dwFlags & DIDF_ABSAXIS))
547     {
548 	This->m_state.lX = 0;
549 	This->m_state.lY = 0;
550 	This->m_state.lZ = 0;
551     }
552     LeaveCriticalSection(&This->base.crit);
553 
554     warp_check( This, FALSE );
555     return DI_OK;
556 }
557 
558 static HRESULT WINAPI SysMouseAImpl_GetDeviceState(LPDIRECTINPUTDEVICE8A iface, DWORD len, LPVOID ptr)
559 {
560     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
561     return SysMouseWImpl_GetDeviceState(IDirectInputDevice8W_from_impl(This), len, ptr);
562 }
563 
564 /******************************************************************************
565   *     GetDeviceData : gets buffered input data.
566   */
567 static HRESULT WINAPI SysMouseWImpl_GetDeviceData(LPDIRECTINPUTDEVICE8W iface,
568         DWORD dodsize, LPDIDEVICEOBJECTDATA dod, LPDWORD entries, DWORD flags)
569 {
570     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
571     HRESULT res;
572 
573     res = IDirectInputDevice2WImpl_GetDeviceData(iface, dodsize, dod, entries, flags);
574     if (SUCCEEDED(res)) warp_check( This, FALSE );
575     return res;
576 }
577 
578 static HRESULT WINAPI SysMouseAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
579         DWORD dodsize, LPDIDEVICEOBJECTDATA dod, LPDWORD entries, DWORD flags)
580 {
581     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
582     return SysMouseWImpl_GetDeviceData(IDirectInputDevice8W_from_impl(This), dodsize, dod, entries, flags);
583 }
584 
585 /******************************************************************************
586   *     GetProperty : get input device properties
587   */
588 static HRESULT WINAPI SysMouseWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
589 {
590     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
591 
592     TRACE("(%p) %s,%p\n", This, debugstr_guid(rguid), pdiph);
593     _dump_DIPROPHEADER(pdiph);
594 
595     if (IS_DIPROP(rguid)) {
596 	switch (LOWORD(rguid)) {
597 	    case (DWORD_PTR) DIPROP_GRANULARITY: {
598 		LPDIPROPDWORD pr = (LPDIPROPDWORD) pdiph;
599 
600 		/* We'll just assume that the app asks about the Z axis */
601 		pr->dwData = WHEEL_DELTA;
602 
603 		break;
604 	    }
605 
606 	    case (DWORD_PTR) DIPROP_RANGE: {
607 		LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
608 
609 		if ((pdiph->dwHow == DIPH_BYID) &&
610 		    ((pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_X_AXIS_INSTANCE) | DIDFT_RELAXIS)) ||
611 		     (pdiph->dwObj == (DIDFT_MAKEINSTANCE(WINE_MOUSE_Y_AXIS_INSTANCE) | DIDFT_RELAXIS)))) {
612 		    /* Querying the range of either the X or the Y axis.  As I do
613 		       not know the range, do as if the range were
614 		       unrestricted...*/
615 		    pr->lMin = DIPROPRANGE_NOMIN;
616 		    pr->lMax = DIPROPRANGE_NOMAX;
617 		}
618 
619 		break;
620 	    }
621 
622 	    default:
623                 return IDirectInputDevice2WImpl_GetProperty(iface, rguid, pdiph);
624         }
625     }
626 
627     return DI_OK;
628 }
629 
630 static HRESULT WINAPI SysMouseAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
631 {
632     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
633     return SysMouseWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
634 }
635 
636 /******************************************************************************
637   *     GetCapabilities : get the device capabilities
638   */
639 static HRESULT WINAPI SysMouseWImpl_GetCapabilities(LPDIRECTINPUTDEVICE8W iface, LPDIDEVCAPS lpDIDevCaps)
640 {
641     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
642     DIDEVCAPS devcaps;
643 
644     TRACE("(this=%p,%p)\n",This,lpDIDevCaps);
645 
646     if ((lpDIDevCaps->dwSize != sizeof(DIDEVCAPS)) && (lpDIDevCaps->dwSize != sizeof(DIDEVCAPS_DX3))) {
647         WARN("invalid parameter\n");
648         return DIERR_INVALIDPARAM;
649     }
650 
651     devcaps.dwSize = lpDIDevCaps->dwSize;
652     devcaps.dwFlags = DIDC_ATTACHED | DIDC_EMULATED;
653     if (This->base.dinput->dwVersion >= 0x0800)
654 	devcaps.dwDevType = DI8DEVTYPE_MOUSE | (DI8DEVTYPEMOUSE_TRADITIONAL << 8);
655     else
656 	devcaps.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_TRADITIONAL << 8);
657     devcaps.dwAxes = 3;
658     devcaps.dwButtons = 8;
659     devcaps.dwPOVs = 0;
660     devcaps.dwFFSamplePeriod = 0;
661     devcaps.dwFFMinTimeResolution = 0;
662     devcaps.dwFirmwareRevision = 100;
663     devcaps.dwHardwareRevision = 100;
664     devcaps.dwFFDriverVersion = 0;
665 
666     memcpy(lpDIDevCaps, &devcaps, lpDIDevCaps->dwSize);
667 
668     return DI_OK;
669 }
670 
671 static HRESULT WINAPI SysMouseAImpl_GetCapabilities(LPDIRECTINPUTDEVICE8A iface, LPDIDEVCAPS lpDIDevCaps)
672 {
673     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
674     return SysMouseWImpl_GetCapabilities(IDirectInputDevice8W_from_impl(This), lpDIDevCaps);
675 }
676 
677 /******************************************************************************
678   *     GetObjectInfo : get information about a device object such as a button
679   *                     or axis
680   */
681 static HRESULT WINAPI SysMouseWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
682         LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
683 {
684     static const WCHAR x_axisW[] = {'X','-','A','x','i','s',0};
685     static const WCHAR y_axisW[] = {'Y','-','A','x','i','s',0};
686     static const WCHAR wheelW[] = {'W','h','e','e','l',0};
687     static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
688     HRESULT res;
689 
690     res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
691     if (res != DI_OK) return res;
692 
693     if      (IsEqualGUID(&pdidoi->guidType, &GUID_XAxis)) strcpyW(pdidoi->tszName, x_axisW);
694     else if (IsEqualGUID(&pdidoi->guidType, &GUID_YAxis)) strcpyW(pdidoi->tszName, y_axisW);
695     else if (IsEqualGUID(&pdidoi->guidType, &GUID_ZAxis)) strcpyW(pdidoi->tszName, wheelW);
696     else if (pdidoi->dwType & DIDFT_BUTTON)
697         wsprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType) - 3);
698 
699     _dump_OBJECTINSTANCEW(pdidoi);
700     return res;
701 }
702 
703 static HRESULT WINAPI SysMouseAImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
704         LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
705 {
706     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
707     HRESULT res;
708     DIDEVICEOBJECTINSTANCEW didoiW;
709     DWORD dwSize = pdidoi->dwSize;
710 
711     didoiW.dwSize = sizeof(didoiW);
712     res = SysMouseWImpl_GetObjectInfo(IDirectInputDevice8W_from_impl(This), &didoiW, dwObj, dwHow);
713     if (res != DI_OK) return res;
714 
715     memset(pdidoi, 0, pdidoi->dwSize);
716     memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
717     pdidoi->dwSize = dwSize;
718     WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
719                         sizeof(pdidoi->tszName), NULL, NULL);
720 
721     return res;
722 }
723 
724 /******************************************************************************
725   *     GetDeviceInfo : get information about a device's identity
726   */
727 static HRESULT WINAPI SysMouseAImpl_GetDeviceInfo(
728 	LPDIRECTINPUTDEVICE8A iface,
729 	LPDIDEVICEINSTANCEA pdidi)
730 {
731     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
732     TRACE("(this=%p,%p)\n", This, pdidi);
733 
734     if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)) {
735         WARN(" dinput3 not supported yet...\n");
736 	return DI_OK;
737     }
738 
739     fill_mouse_dideviceinstanceA(pdidi, This->base.dinput->dwVersion);
740 
741     return DI_OK;
742 }
743 
744 static HRESULT WINAPI SysMouseWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, LPDIDEVICEINSTANCEW pdidi)
745 {
746     SysMouseImpl *This = impl_from_IDirectInputDevice8W(iface);
747     TRACE("(this=%p,%p)\n", This, pdidi);
748 
749     if (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW)) {
750         WARN(" dinput3 not supported yet...\n");
751 	return DI_OK;
752     }
753 
754     fill_mouse_dideviceinstanceW(pdidi, This->base.dinput->dwVersion);
755 
756     return DI_OK;
757 }
758 
759 static HRESULT WINAPI SysMouseWImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface,
760                                                    LPDIACTIONFORMATW lpdiaf,
761                                                    LPCWSTR lpszUserName,
762                                                    DWORD dwFlags)
763 {
764     FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
765 
766     return _build_action_map(iface, lpdiaf, lpszUserName, dwFlags, DIMOUSE_MASK, &c_dfDIMouse2);
767 }
768 
769 static HRESULT WINAPI SysMouseAImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface,
770                                                    LPDIACTIONFORMATA lpdiaf,
771                                                    LPCSTR lpszUserName,
772                                                    DWORD dwFlags)
773 {
774     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
775     DIACTIONFORMATW diafW;
776     HRESULT hr;
777     WCHAR *lpszUserNameW = NULL;
778     int username_size;
779 
780     diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiaf->dwNumActions);
781     _copy_diactionformatAtoW(&diafW, lpdiaf);
782 
783     if (lpszUserName != NULL)
784     {
785         username_size = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0);
786         lpszUserNameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*username_size);
787         MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, lpszUserNameW, username_size);
788     }
789 
790     hr = SysMouseWImpl_BuildActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
791 
792     _copy_diactionformatWtoA(lpdiaf, &diafW);
793     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
794     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
795 
796     return hr;
797 }
798 
799 static HRESULT WINAPI SysMouseWImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface,
800                                                  LPDIACTIONFORMATW lpdiaf,
801                                                  LPCWSTR lpszUserName,
802                                                  DWORD dwFlags)
803 {
804     FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", iface, lpdiaf, debugstr_w(lpszUserName), dwFlags);
805 
806     return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, &c_dfDIMouse2);
807 }
808 
809 static HRESULT WINAPI SysMouseAImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface,
810                                                  LPDIACTIONFORMATA lpdiaf,
811                                                  LPCSTR lpszUserName,
812                                                  DWORD dwFlags)
813 {
814     SysMouseImpl *This = impl_from_IDirectInputDevice8A(iface);
815     DIACTIONFORMATW diafW;
816     HRESULT hr;
817     WCHAR *lpszUserNameW = NULL;
818     int username_size;
819 
820     diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiaf->dwNumActions);
821     _copy_diactionformatAtoW(&diafW, lpdiaf);
822 
823     if (lpszUserName != NULL)
824     {
825         username_size = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0);
826         lpszUserNameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*username_size);
827         MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, lpszUserNameW, username_size);
828     }
829 
830     hr = SysMouseWImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags);
831 
832     HeapFree(GetProcessHeap(), 0, diafW.rgoAction);
833     HeapFree(GetProcessHeap(), 0, lpszUserNameW);
834 
835     return hr;
836 }
837 
838 static const IDirectInputDevice8AVtbl SysMouseAvt =
839 {
840     IDirectInputDevice2AImpl_QueryInterface,
841     IDirectInputDevice2AImpl_AddRef,
842     IDirectInputDevice2AImpl_Release,
843     SysMouseAImpl_GetCapabilities,
844     IDirectInputDevice2AImpl_EnumObjects,
845     SysMouseAImpl_GetProperty,
846     IDirectInputDevice2AImpl_SetProperty,
847     SysMouseAImpl_Acquire,
848     SysMouseAImpl_Unacquire,
849     SysMouseAImpl_GetDeviceState,
850     SysMouseAImpl_GetDeviceData,
851     IDirectInputDevice2AImpl_SetDataFormat,
852     IDirectInputDevice2AImpl_SetEventNotification,
853     IDirectInputDevice2AImpl_SetCooperativeLevel,
854     SysMouseAImpl_GetObjectInfo,
855     SysMouseAImpl_GetDeviceInfo,
856     IDirectInputDevice2AImpl_RunControlPanel,
857     IDirectInputDevice2AImpl_Initialize,
858     IDirectInputDevice2AImpl_CreateEffect,
859     IDirectInputDevice2AImpl_EnumEffects,
860     IDirectInputDevice2AImpl_GetEffectInfo,
861     IDirectInputDevice2AImpl_GetForceFeedbackState,
862     IDirectInputDevice2AImpl_SendForceFeedbackCommand,
863     IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
864     IDirectInputDevice2AImpl_Escape,
865     IDirectInputDevice2AImpl_Poll,
866     IDirectInputDevice2AImpl_SendDeviceData,
867     IDirectInputDevice7AImpl_EnumEffectsInFile,
868     IDirectInputDevice7AImpl_WriteEffectToFile,
869     SysMouseAImpl_BuildActionMap,
870     SysMouseAImpl_SetActionMap,
871     IDirectInputDevice8AImpl_GetImageInfo
872 };
873 
874 static const IDirectInputDevice8WVtbl SysMouseWvt =
875 {
876     IDirectInputDevice2WImpl_QueryInterface,
877     IDirectInputDevice2WImpl_AddRef,
878     IDirectInputDevice2WImpl_Release,
879     SysMouseWImpl_GetCapabilities,
880     IDirectInputDevice2WImpl_EnumObjects,
881     SysMouseWImpl_GetProperty,
882     IDirectInputDevice2WImpl_SetProperty,
883     SysMouseWImpl_Acquire,
884     SysMouseWImpl_Unacquire,
885     SysMouseWImpl_GetDeviceState,
886     SysMouseWImpl_GetDeviceData,
887     IDirectInputDevice2WImpl_SetDataFormat,
888     IDirectInputDevice2WImpl_SetEventNotification,
889     IDirectInputDevice2WImpl_SetCooperativeLevel,
890     SysMouseWImpl_GetObjectInfo,
891     SysMouseWImpl_GetDeviceInfo,
892     IDirectInputDevice2WImpl_RunControlPanel,
893     IDirectInputDevice2WImpl_Initialize,
894     IDirectInputDevice2WImpl_CreateEffect,
895     IDirectInputDevice2WImpl_EnumEffects,
896     IDirectInputDevice2WImpl_GetEffectInfo,
897     IDirectInputDevice2WImpl_GetForceFeedbackState,
898     IDirectInputDevice2WImpl_SendForceFeedbackCommand,
899     IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
900     IDirectInputDevice2WImpl_Escape,
901     IDirectInputDevice2WImpl_Poll,
902     IDirectInputDevice2WImpl_SendDeviceData,
903     IDirectInputDevice7WImpl_EnumEffectsInFile,
904     IDirectInputDevice7WImpl_WriteEffectToFile,
905     SysMouseWImpl_BuildActionMap,
906     SysMouseWImpl_SetActionMap,
907     IDirectInputDevice8WImpl_GetImageInfo
908 };
909