Fixed possible endless loop.
[wine] / windows / dinput.c
1 /*              DirectInput
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  *
6  */
7 /* Status:
8  *
9  * - Tomb Raider 2 Demo:
10  *   Playable using keyboard only.
11  * - WingCommander Prophecy Demo:
12  *   Doesn't get Input Focus.
13  * 
14  * - Fallout : works great in X and DGA mode
15  *
16  * FIXME: The keyboard handling needs to (and will) be merged into keyboard.c
17  *        (The current implementation is currently only a proof of concept and
18  *         an utter mess.)
19  */
20
21 #include "config.h"
22 #include <string.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <sys/signal.h>
26
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "wine/obj_base.h"
30 #include "dinput.h"
31 #include "debug.h"
32 #include "message.h"
33 #include "display.h"
34 #include "mouse.h"
35 #include "sysmetrics.h"
36
37 extern BYTE InputKeyStateTable[256];
38 extern int min_keycode, max_keycode;
39 extern WORD keyc2vkey[256];
40
41 static ICOM_VTABLE(IDirectInputA) ddiavt;
42 static ICOM_VTABLE(IDirectInputDevice2A) SysKeyboardAvt;
43 static ICOM_VTABLE(IDirectInputDevice2A) SysMouseAvt;
44
45 typedef struct IDirectInputAImpl IDirectInputAImpl;
46 typedef struct IDirectInputDevice2AImpl IDirectInputDevice2AImpl;
47 typedef struct SysKeyboardAImpl SysKeyboardAImpl;
48 typedef struct SysMouseAImpl SysMouseAImpl;
49
50 struct IDirectInputDevice2AImpl
51 {
52         ICOM_VTABLE(IDirectInputDevice2A)* lpvtbl;
53         DWORD                           ref;
54         GUID                            guid;
55 };
56
57 struct SysKeyboardAImpl
58 {
59         /* IDirectInputDevice2AImpl */
60         ICOM_VTABLE(IDirectInputDevice2A)* lpvtbl;
61         DWORD                           ref;
62         GUID                            guid;
63         /* SysKeyboardAImpl */
64         BYTE                            keystate[256];
65 };
66
67 struct SysMouseAImpl
68 {
69         /* IDirectInputDevice2AImpl */
70         ICOM_VTABLE(IDirectInputDevice2A)* lpvtbl;
71         DWORD                           ref;
72         GUID                            guid;
73         /* SysMouseAImpl */
74         BYTE                            absolute;
75         /* Previous position for relative moves */
76         LONG prevX, prevY;
77         LPMOUSE_EVENT_PROC prev_handler;
78         HWND win;
79         DWORD win_centerX, win_centerY;
80         LPDIDEVICEOBJECTDATA data_queue;
81         int queue_pos, queue_len;
82         int need_warp;
83         int acquired;
84 };
85
86
87 /* UIDs for Wine "drivers".
88    When enumerating each device supporting DInput, they have two UIDs :
89     - the 'windows' UID
90     - a vendor UID */
91 static GUID DInput_Wine_Mouse_GUID = { /* 9e573ed8-7734-11d2-8d4a-23903fb6bdf7 */
92   0x9e573ed8,
93   0x7734,
94   0x11d2,
95   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
96 };
97 static GUID DInput_Wine_Keyboard_GUID = { /* 0ab8648a-7735-11d2-8c73-71df54a96441 */
98   0x0ab8648a,
99   0x7735,
100   0x11d2,
101   {0x8c, 0x73, 0x71, 0xdf, 0x54, 0xa9, 0x64, 0x41}
102 };
103
104 /* FIXME: This is ugly and not thread safe :/ */
105 static IDirectInputDevice2A* current_lock = NULL;
106
107 /******************************************************************************
108  *      Various debugging tools
109  */
110 static void _dump_cooperativelevel(DWORD dwFlags) {
111   int   i;
112   const struct {
113     DWORD       mask;
114     char        *name;
115   } flags[] = {
116 #define FE(x) { x, #x},
117     FE(DISCL_BACKGROUND)
118     FE(DISCL_EXCLUSIVE)
119     FE(DISCL_FOREGROUND)
120     FE(DISCL_NONEXCLUSIVE)
121   };
122   for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
123     if (flags[i].mask & dwFlags)
124       DUMP("%s ",flags[i].name);
125   DUMP("\n");
126 }
127
128 struct IDirectInputAImpl
129 {
130         ICOM_VTABLE(IDirectInputA)* lpvtbl;
131         DWORD                   ref;
132 };
133
134 /******************************************************************************
135  *      DirectInputCreate32A
136  */
137 HRESULT WINAPI DirectInputCreateA(HINSTANCE hinst, DWORD dwVersion, LPDIRECTINPUTA *ppDI, LPUNKNOWN punkOuter)
138 {
139         IDirectInputAImpl* This;
140         TRACE(dinput, "(0x%08lx,%04lx,%p,%p)\n",
141                 (DWORD)hinst,dwVersion,ppDI,punkOuter
142         );
143         This = (IDirectInputAImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInputAImpl));
144         This->ref = 1;
145         This->lpvtbl = &ddiavt;
146         *ppDI=(IDirectInputA*)This;
147         return 0;
148 }
149 /******************************************************************************
150  *      IDirectInputA_EnumDevices
151  */
152 static HRESULT WINAPI IDirectInputAImpl_EnumDevices(
153         LPDIRECTINPUTA iface, DWORD dwDevType, LPDIENUMDEVICESCALLBACKA lpCallback,
154         LPVOID pvRef, DWORD dwFlags
155 )
156 {
157   ICOM_THIS(IDirectInputAImpl,iface);
158   DIDEVICEINSTANCEA devInstance;
159   int ret;
160
161   TRACE(dinput, "(this=%p,0x%04lx,%p,%p,%04lx)\n", This, dwDevType, lpCallback, pvRef, dwFlags);
162
163   devInstance.dwSize = sizeof(DIDEVICEINSTANCEA);
164   
165   if ((dwDevType == 0) || (dwDevType == DIDEVTYPE_KEYBOARD)) {
166   /* Return keyboard */
167     devInstance.guidInstance = GUID_SysKeyboard;         /* DInput's GUID */
168     devInstance.guidProduct = DInput_Wine_Keyboard_GUID; /* Vendor's GUID */
169     devInstance.dwDevType = DIDEVTYPE_KEYBOARD | (DIDEVTYPEKEYBOARD_UNKNOWN << 8);
170   strcpy(devInstance.tszInstanceName, "Keyboard");
171   strcpy(devInstance.tszProductName, "Wine Keyboard");
172   
173   ret = lpCallback(&devInstance, pvRef);
174     TRACE(dinput, "Keyboard registered\n");
175   
176     if (ret == DIENUM_STOP)
177     return 0;
178   }
179   
180   if ((dwDevType == 0) || (dwDevType == DIDEVTYPE_KEYBOARD)) {
181   /* Return mouse */
182     devInstance.guidInstance = GUID_SysMouse;         /* DInput's GUID */
183     devInstance.guidProduct = DInput_Wine_Mouse_GUID; /* Vendor's GUID */
184     devInstance.dwDevType = DIDEVTYPE_MOUSE | (DIDEVTYPEMOUSE_UNKNOWN << 8);
185   strcpy(devInstance.tszInstanceName, "Mouse");
186   strcpy(devInstance.tszProductName, "Wine Mouse");
187   
188   ret = lpCallback(&devInstance, pvRef);
189     TRACE(dinput, "Mouse registered\n");
190   }
191
192   /* Should also do joystick enumerations.... */
193   
194         return 0;
195 }
196
197 static ULONG WINAPI IDirectInputAImpl_AddRef(LPDIRECTINPUTA iface)
198 {
199         ICOM_THIS(IDirectInputAImpl,iface);
200         return ++(This->ref);
201 }
202
203 static ULONG WINAPI IDirectInputAImpl_Release(LPDIRECTINPUTA iface)
204 {
205         ICOM_THIS(IDirectInputAImpl,iface);
206         if (!(--This->ref)) {
207                 HeapFree(GetProcessHeap(),0,This);
208                 return 0;
209         }
210         return This->ref;
211 }
212
213 static HRESULT WINAPI IDirectInputAImpl_CreateDevice(
214         LPDIRECTINPUTA iface,REFGUID rguid,LPDIRECTINPUTDEVICEA* pdev,
215         LPUNKNOWN punk
216 ) {
217         ICOM_THIS(IDirectInputAImpl,iface);
218         char    xbuf[50];
219         
220         WINE_StringFromCLSID(rguid,xbuf);
221         FIXME(dinput,"(this=%p,%s,%p,%p): stub\n",This,xbuf,pdev,punk);
222         if ((!memcmp(&GUID_SysKeyboard,rguid,sizeof(GUID_SysKeyboard))) ||          /* Generic Keyboard */
223             (!memcmp(&DInput_Wine_Keyboard_GUID,rguid,sizeof(GUID_SysKeyboard)))) { /* Wine Keyboard */
224                 SysKeyboardAImpl* newDevice;
225                 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysKeyboardAImpl));
226                 newDevice->ref = 1;
227                 newDevice->lpvtbl = &SysKeyboardAvt;
228                 memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
229                 memset(newDevice->keystate,0,256);
230                 *pdev=(IDirectInputDeviceA*)newDevice;
231                 return DI_OK;
232         }
233         if ((!memcmp(&GUID_SysMouse,rguid,sizeof(GUID_SysMouse))) ||             /* Generic Mouse */
234             (!memcmp(&DInput_Wine_Mouse_GUID,rguid,sizeof(GUID_SysMouse)))) { /* Wine Mouse */
235                 SysKeyboardAImpl* newDevice;
236                 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysMouseAImpl));
237                 newDevice->ref = 1;
238                 newDevice->lpvtbl = &SysMouseAvt;
239                 memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
240                 *pdev=(IDirectInputDeviceA*)newDevice;
241                 return DI_OK;
242         }
243         return E_FAIL;
244 }
245
246 static HRESULT WINAPI IDirectInputAImpl_QueryInterface(
247         LPDIRECTINPUTA iface,REFIID riid,LPVOID *ppobj
248 ) {
249         ICOM_THIS(IDirectInputAImpl,iface);
250         char    xbuf[50];
251
252         WINE_StringFromCLSID(riid,xbuf);
253         TRACE(dinput,"(this=%p,%s,%p)\n",This,xbuf,ppobj);
254         if (!memcmp(&IID_IUnknown,riid,sizeof(*riid))) {
255                 IDirectInputA_AddRef(iface);
256                 *ppobj = This;
257                 return 0;
258         }
259         if (!memcmp(&IID_IDirectInputA,riid,sizeof(*riid))) {
260                 IDirectInputA_AddRef(iface);
261                 *ppobj = This;
262                 return 0;
263         }
264         return E_FAIL;
265 }
266
267 static HRESULT WINAPI IDirectInputAImpl_Initialize(
268         LPDIRECTINPUTA iface,HINSTANCE hinst,DWORD x
269 ) {
270         return DIERR_ALREADYINITIALIZED;
271 }
272
273 static HRESULT WINAPI IDirectInputAImpl_GetDeviceStatus(LPDIRECTINPUTA iface,
274                                                         REFGUID rguid) {
275   ICOM_THIS(IDirectInputAImpl,iface);
276   char xbuf[50];
277   
278   WINE_StringFromCLSID(rguid,xbuf);
279   FIXME(dinput,"(%p)->(%s): stub\n",This,xbuf);
280   
281   return DI_OK;
282 }
283
284 static HRESULT WINAPI IDirectInputAImpl_RunControlPanel(LPDIRECTINPUTA iface,
285                                                         HWND hwndOwner,
286                                                         DWORD dwFlags) {
287   ICOM_THIS(IDirectInputAImpl,iface);
288   FIXME(dinput,"(%p)->(%08lx,%08lx): stub\n",This, (DWORD) hwndOwner, dwFlags);
289   
290   return DI_OK;
291 }
292
293 static ICOM_VTABLE(IDirectInputA) ddiavt= {
294         IDirectInputAImpl_QueryInterface,
295         IDirectInputAImpl_AddRef,
296         IDirectInputAImpl_Release,
297         IDirectInputAImpl_CreateDevice,
298         IDirectInputAImpl_EnumDevices,
299         IDirectInputAImpl_GetDeviceStatus,
300         IDirectInputAImpl_RunControlPanel,
301         IDirectInputAImpl_Initialize
302 };
303
304 /******************************************************************************
305  *      IDirectInputDeviceA
306  */
307
308 static HRESULT WINAPI IDirectInputDevice2AImpl_SetDataFormat(
309         LPDIRECTINPUTDEVICE2A iface,LPCDIDATAFORMAT df
310 ) {
311         /*
312         int i;
313         TRACE(dinput,"(this=%p,%p)\n",This,df);
314
315         TRACE(dinput,"df.dwSize=%ld\n",df->dwSize);
316         TRACE(dinput,"(df.dwObjsize=%ld)\n",df->dwObjSize);
317         TRACE(dinput,"(df.dwFlags=0x%08lx)\n",df->dwFlags);
318         TRACE(dinput,"(df.dwDataSize=%ld)\n",df->dwDataSize);
319         TRACE(dinput,"(df.dwNumObjs=%ld)\n",df->dwNumObjs);
320
321         for (i=0;i<df->dwNumObjs;i++) {
322                 char    xbuf[50];
323
324                 if (df->rgodf[i].pguid)
325                         WINE_StringFromCLSID(df->rgodf[i].pguid,xbuf);
326                 else
327                         strcpy(xbuf,"<no guid>");
328                 TRACE(dinput,"df.rgodf[%d].guid %s\n",i,xbuf);
329                 TRACE(dinput,"df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
330                 TRACE(dinput,"dwType 0x%02lx,dwInstance %ld\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
331                 TRACE(dinput,"df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
332         }
333         */
334         return 0;
335 }
336
337 static HRESULT WINAPI IDirectInputDevice2AImpl_SetCooperativeLevel(
338         LPDIRECTINPUTDEVICE2A iface,HWND hwnd,DWORD dwflags
339 ) {
340         ICOM_THIS(IDirectInputDevice2AImpl,iface);
341         FIXME(dinput,"(this=%p,0x%08lx,0x%08lx): stub\n",This,(DWORD)hwnd,dwflags);
342         if (TRACE_ON(dinput))
343           _dump_cooperativelevel(dwflags);
344         return 0;
345 }
346
347 static HRESULT WINAPI IDirectInputDevice2AImpl_SetEventNotification(
348         LPDIRECTINPUTDEVICE2A iface,HANDLE hnd
349 ) {
350         ICOM_THIS(IDirectInputDevice2AImpl,iface);
351         FIXME(dinput,"(this=%p,0x%08lx): stub\n",This,(DWORD)hnd);
352         return 0;
353 }
354
355 static ULONG WINAPI IDirectInputDevice2AImpl_Release(LPDIRECTINPUTDEVICE2A iface)
356 {
357         ICOM_THIS(IDirectInputDevice2AImpl,iface);
358         This->ref--;
359         if (This->ref)
360                 return This->ref;
361         HeapFree(GetProcessHeap(),0,This);
362         return 0;
363 }
364
365 static HRESULT WINAPI SysKeyboardAImpl_SetProperty(
366         LPDIRECTINPUTDEVICE2A iface,REFGUID rguid,LPCDIPROPHEADER ph
367 )
368 {
369         ICOM_THIS(SysKeyboardAImpl,iface);
370         char                    xbuf[50];
371
372         if (HIWORD(rguid))
373                 WINE_StringFromCLSID(rguid,xbuf);
374         else
375                 sprintf(xbuf,"<special guid %ld>",(DWORD)rguid);
376         TRACE(dinput,"(this=%p,%s,%p)\n",This,xbuf,ph);
377         TRACE(dinput,"(size=%ld,headersize=%ld,obj=%ld,how=%ld\n",
378             ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow);
379         if (!HIWORD(rguid)) {
380                 switch ((DWORD)rguid) {
381                 case DIPROP_BUFFERSIZE: {
382                         LPCDIPROPDWORD  pd = (LPCDIPROPDWORD)ph;
383
384                         TRACE(dinput,"(buffersize=%ld)\n",pd->dwData);
385                         break;
386                 }
387                 default:
388                         WARN(dinput,"Unknown type %ld\n",(DWORD)rguid);
389                         break;
390                 }
391         }
392         return 0;
393 }
394
395 static HRESULT WINAPI SysKeyboardAImpl_GetDeviceState(
396         LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr
397 )
398 {
399         if (len==256) {
400                 int keyc,vkey;
401
402                 memset(ptr,0,256);
403                 for (keyc=min_keycode;keyc<max_keycode;keyc++)
404                 {
405                     /* X keycode to virtual key */
406                     vkey = keyc2vkey[keyc] & 0xFF;
407                     /* The windows scancode is keyc-min_keycode */
408                     if (InputKeyStateTable[vkey]&0x80) {
409                         ((LPBYTE)ptr)[keyc-min_keycode]=0x80;
410                         ((LPBYTE)ptr)[(keyc-min_keycode)|0x80]=0x80;
411                     }
412                 }
413                 return 0;
414         }
415         WARN(dinput,"whoops, SysKeyboardAImpl_GetDeviceState got len %ld?\n",len);
416         return 0;
417 }
418
419 static HRESULT WINAPI SysKeyboardAImpl_GetDeviceData(
420         LPDIRECTINPUTDEVICE2A iface,DWORD dodsize,LPDIDEVICEOBJECTDATA dod,
421         LPDWORD entries,DWORD flags
422 )
423 {
424         ICOM_THIS(SysKeyboardAImpl,iface);
425         int                     keyc,n,vkey,xentries;
426
427         TRACE(dinput,"(this=%p,%ld,%p,%p(%ld)),0x%08lx)\n",
428                 This,dodsize,dod,entries,entries?*entries:0,flags);
429         EVENT_WaitNetEvent(FALSE,TRUE);
430         if (entries)
431                 xentries = *entries; 
432         else
433                 xentries = 1;
434
435         n = 0;
436
437         for (keyc=min_keycode;(keyc<max_keycode) && (n<*entries);keyc++)
438         {
439                     /* X keycode to virtual key */
440                     vkey = keyc2vkey[keyc] & 0xFF;
441                     if (This->keystate[vkey] == (InputKeyStateTable[vkey]&0x80))
442                         continue;
443                 if (dod) {
444                         /* add an entry */
445                         dod[n].dwOfs            = keyc-min_keycode; /* scancode */
446                         dod[n].dwData           = InputKeyStateTable[vkey]&0x80;
447                         dod[n].dwTimeStamp      = 0; /* umm */
448                         dod[n].dwSequence       = 0; /* umm */
449                         n++;
450                 }
451                 if (!(flags & DIGDD_PEEK))
452                         This->keystate[vkey] = InputKeyStateTable[vkey]&0x80;
453                     
454         }
455         
456         if (n) fprintf(stderr,"%d entries\n",n);
457         *entries = n;
458         return 0;
459 }
460
461 static HRESULT WINAPI SysKeyboardAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface)
462 {
463         ICOM_THIS(SysKeyboardAImpl,iface);
464         TRACE(dinput,"(this=%p): stub\n",This);
465         return 0;
466 }
467
468 static HRESULT WINAPI SysKeyboardAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface)
469 {
470         ICOM_THIS(SysKeyboardAImpl,iface);
471         TRACE(dinput,"(this=%p): stub\n",This);
472         return 0;
473 }
474
475 static HRESULT WINAPI IDirectInputDevice2AImpl_QueryInterface(
476         LPDIRECTINPUTDEVICE2A iface,REFIID riid,LPVOID *ppobj
477 )
478 {
479         ICOM_THIS(IDirectInputDevice2AImpl,iface);
480         char    xbuf[50];
481
482         WINE_StringFromCLSID(riid,xbuf);
483         TRACE(dinput,"(this=%p,%s,%p)\n",This,xbuf,ppobj);
484         if (!memcmp(&IID_IUnknown,riid,sizeof(*riid))) {
485                 IDirectInputDevice2_AddRef(iface);
486                 *ppobj = This;
487                 return 0;
488         }
489         if (!memcmp(&IID_IDirectInputDeviceA,riid,sizeof(*riid))) {
490                 IDirectInputDevice2_AddRef(iface);
491                 *ppobj = This;
492                 return 0;
493         }
494         if (!memcmp(&IID_IDirectInputDevice2A,riid,sizeof(*riid))) {
495                 IDirectInputDevice2_AddRef(iface);
496                 *ppobj = This;
497                 return 0;
498         }
499         return E_FAIL;
500 }
501
502 static ULONG WINAPI IDirectInputDevice2AImpl_AddRef(
503         LPDIRECTINPUTDEVICE2A iface)
504 {
505         ICOM_THIS(IDirectInputDevice2AImpl,iface);
506         return ++This->ref;
507 }
508
509 static HRESULT WINAPI IDirectInputDevice2AImpl_GetCapabilities(
510         LPDIRECTINPUTDEVICE2A iface,
511         LPDIDEVCAPS lpDIDevCaps)
512 {
513         FIXME(dinput, "stub!\n");
514         return DI_OK;
515 }
516
517 static HRESULT WINAPI IDirectInputDevice2AImpl_EnumObjects(
518         LPDIRECTINPUTDEVICE2A iface,
519         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
520         LPVOID lpvRef,
521         DWORD dwFlags)
522 {
523         FIXME(dinput, "stub!\n");
524 #if 0
525         if (lpCallback)
526                 lpCallback(NULL, lpvRef);
527 #endif
528         return DI_OK;
529 }
530         
531 static HRESULT WINAPI IDirectInputDevice2AImpl_GetProperty(
532         LPDIRECTINPUTDEVICE2A iface,
533         REFGUID rguid,
534         LPDIPROPHEADER pdiph)
535 {
536         FIXME(dinput, "stub!\n");
537         return DI_OK;
538 }
539
540 static HRESULT WINAPI IDirectInputDevice2AImpl_GetObjectInfo(
541         LPDIRECTINPUTDEVICE2A iface,
542         LPDIDEVICEOBJECTINSTANCEA pdidoi,
543         DWORD dwObj,
544         DWORD dwHow)
545 {
546         FIXME(dinput, "stub!\n");
547         return DI_OK;
548 }
549         
550 static HRESULT WINAPI IDirectInputDevice2AImpl_GetDeviceInfo(
551         LPDIRECTINPUTDEVICE2A iface,
552         LPDIDEVICEINSTANCEA pdidi)
553 {
554         FIXME(dinput, "stub!\n");
555         return DI_OK;
556 }
557         
558 static HRESULT WINAPI IDirectInputDevice2AImpl_RunControlPanel(
559         LPDIRECTINPUTDEVICE2A iface,
560         HWND hwndOwner,
561         DWORD dwFlags)
562 {
563         FIXME(dinput, "stub!\n");
564         return DI_OK;
565 }
566         
567 static HRESULT WINAPI IDirectInputDevice2AImpl_Initialize(
568         LPDIRECTINPUTDEVICE2A iface,
569         HINSTANCE hinst,
570         DWORD dwVersion,
571         REFGUID rguid)
572 {
573         FIXME(dinput, "stub!\n");
574         return DI_OK;
575 }
576         
577 /******************************************************************************
578  *      IDirectInputDevice2A
579  */
580
581 static HRESULT WINAPI IDirectInputDevice2AImpl_CreateEffect(
582         LPDIRECTINPUTDEVICE2A iface,
583         REFGUID rguid,
584         LPCDIEFFECT lpeff,
585         LPDIRECTINPUTEFFECT *ppdef,
586         LPUNKNOWN pUnkOuter)
587 {
588         FIXME(dinput, "stub!\n");
589         return DI_OK;
590 }
591
592 static HRESULT WINAPI IDirectInputDevice2AImpl_EnumEffects(
593         LPDIRECTINPUTDEVICE2A iface,
594         LPDIENUMEFFECTSCALLBACKA lpCallback,
595         LPVOID lpvRef,
596         DWORD dwFlags)
597 {
598         FIXME(dinput, "stub!\n");
599         if (lpCallback)
600                 lpCallback(NULL, lpvRef);
601         return DI_OK;
602 }
603
604 static HRESULT WINAPI IDirectInputDevice2AImpl_GetEffectInfo(
605         LPDIRECTINPUTDEVICE2A iface,
606         LPDIEFFECTINFOA lpdei,
607         REFGUID rguid)
608 {
609         FIXME(dinput, "stub!\n");
610         return DI_OK;
611 }
612
613 static HRESULT WINAPI IDirectInputDevice2AImpl_GetForceFeedbackState(
614         LPDIRECTINPUTDEVICE2A iface,
615         LPDWORD pdwOut)
616 {
617         FIXME(dinput, "stub!\n");
618         return DI_OK;
619 }
620
621 static HRESULT WINAPI IDirectInputDevice2AImpl_SendForceFeedbackCommand(
622         LPDIRECTINPUTDEVICE2A iface,
623         DWORD dwFlags)
624 {
625         FIXME(dinput, "stub!\n");
626         return DI_OK;
627 }
628
629 static HRESULT WINAPI IDirectInputDevice2AImpl_EnumCreatedEffectObjects(
630         LPDIRECTINPUTDEVICE2A iface,
631         LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
632         LPVOID lpvRef,
633         DWORD dwFlags)
634 {
635         FIXME(dinput, "stub!\n");
636         if (lpCallback)
637                 lpCallback(NULL, lpvRef);
638         return DI_OK;
639 }
640
641 static HRESULT WINAPI IDirectInputDevice2AImpl_Escape(
642         LPDIRECTINPUTDEVICE2A iface,
643         LPDIEFFESCAPE lpDIEEsc)
644 {
645         FIXME(dinput, "stub!\n");
646         return DI_OK;
647 }
648
649 static HRESULT WINAPI IDirectInputDevice2AImpl_Poll(
650         LPDIRECTINPUTDEVICE2A iface)
651 {
652         FIXME(dinput, "stub!\n");
653         return DI_OK;
654 }
655
656 static HRESULT WINAPI IDirectInputDevice2AImpl_SendDeviceData(
657         LPDIRECTINPUTDEVICE2A iface,
658         DWORD cbObjectData,
659         LPDIDEVICEOBJECTDATA rgdod,
660         LPDWORD pdwInOut,
661         DWORD dwFlags)
662 {
663         FIXME(dinput, "stub!\n");
664         return DI_OK;
665 }
666
667 /******************************************************************************
668  *      SysMouseA (DInput Mouse support)
669  */
670
671 /******************************************************************************
672   *     Release : release the mouse buffer.
673   */
674 static ULONG WINAPI SysMouseAImpl_Release(LPDIRECTINPUTDEVICE2A iface)
675 {
676         ICOM_THIS(SysMouseAImpl,iface);
677
678         This->ref--;
679         if (This->ref)
680                 return This->ref;
681
682         /* Free the data queue */
683         if (This->data_queue != NULL)
684           HeapFree(GetProcessHeap(),0,This->data_queue);
685
686         /* Install the previous event handler (in case of releasing an aquired
687            mouse device) */
688         if (This->prev_handler != NULL)
689           MOUSE_Enable(This->prev_handler);
690         
691         HeapFree(GetProcessHeap(),0,This);
692         return 0;
693 }
694
695
696 /******************************************************************************
697   *     SetCooperativeLevel : store the window in which we will do our
698   *   grabbing.
699   */
700 static HRESULT WINAPI SysMouseAImpl_SetCooperativeLevel(
701         LPDIRECTINPUTDEVICE2A iface,HWND hwnd,DWORD dwflags
702 )
703 {
704   ICOM_THIS(SysMouseAImpl,iface);
705
706   TRACE(dinput,"(this=%p,0x%08lx,0x%08lx): stub\n",This,(DWORD)hwnd,dwflags);
707
708   if (TRACE_ON(dinput))
709     _dump_cooperativelevel(dwflags);
710
711   /* Store the window which asks for the mouse */
712   This->win = hwnd;
713   
714   return 0;
715 }
716
717
718 /******************************************************************************
719   *     SetDataFormat : the application can choose the format of the data
720   *   the device driver sends back with GetDeviceState.
721   *
722   *   For the moment, only the "standard" configuration (c_dfDIMouse) is supported
723   *   in absolute and relative mode.
724   */
725 static HRESULT WINAPI SysMouseAImpl_SetDataFormat(
726         LPDIRECTINPUTDEVICE2A iface,LPCDIDATAFORMAT df
727 )
728 {
729   ICOM_THIS(SysMouseAImpl,iface);
730   int i;
731   
732   TRACE(dinput,"(this=%p,%p)\n",This,df);
733
734   TRACE(dinput,"(df.dwSize=%ld)\n",df->dwSize);
735   TRACE(dinput,"(df.dwObjsize=%ld)\n",df->dwObjSize);
736   TRACE(dinput,"(df.dwFlags=0x%08lx)\n",df->dwFlags);
737   TRACE(dinput,"(df.dwDataSize=%ld)\n",df->dwDataSize);
738   TRACE(dinput,"(df.dwNumObjs=%ld)\n",df->dwNumObjs);
739
740   for (i=0;i<df->dwNumObjs;i++) {
741     char        xbuf[50];
742     
743     if (df->rgodf[i].pguid)
744       WINE_StringFromCLSID(df->rgodf[i].pguid,xbuf);
745     else
746       strcpy(xbuf,"<no guid>");
747     TRACE(dinput,"df.rgodf[%d].guid %s (%p)\n",i,xbuf, df->rgodf[i].pguid);
748     TRACE(dinput,"df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
749     TRACE(dinput,"dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
750     TRACE(dinput,"df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
751   }
752
753   /* Check size of data format to prevent crashes if the applications
754      sends a smaller buffer */
755   if (df->dwDataSize != sizeof(struct DIMOUSESTATE)) {
756     FIXME(dinput, "non-standard mouse configuration not supported yet.");
757     return DIERR_INVALIDPARAM;
758   }
759   
760   /* For the moment, ignore these fields and return always as if
761      c_dfDIMouse was passed as format... */
762
763   /* Check if the mouse is in absolute or relative mode */
764   if (df->dwFlags == DIDF_ABSAXIS)
765     This->absolute = 1;
766   else 
767     This->absolute = 0;
768   
769   return 0;
770 }
771
772 #define GEN_EVENT(offset,data,time,seq)                                         \
773 {                                                                               \
774   if (This->queue_pos < This->queue_len) {                                      \
775     This->data_queue[This->queue_pos].dwOfs = offset;                           \
776     This->data_queue[This->queue_pos].dwData = data;                            \
777     This->data_queue[This->queue_pos].dwTimeStamp = time;                       \
778     This->data_queue[This->queue_pos].dwSequence = seq;                 \
779     This->queue_pos++;                                                          \
780   }                                                                             \
781   }
782   
783 /* Our private mouse event handler */
784 static void WINAPI dinput_mouse_event( DWORD dwFlags, DWORD dx, DWORD dy,
785                                       DWORD cButtons, DWORD dwExtraInfo )
786 {
787   DWORD posX, posY, keyState, time, extra;
788   SysMouseAImpl* This = (SysMouseAImpl*) current_lock;
789   
790   if (   !IsBadReadPtr( (LPVOID)dwExtraInfo, sizeof(WINE_MOUSEEVENT) )
791       && ((WINE_MOUSEEVENT *)dwExtraInfo)->magic == WINE_MOUSEEVENT_MAGIC ) {
792     WINE_MOUSEEVENT *wme = (WINE_MOUSEEVENT *)dwExtraInfo;
793     keyState = wme->keyState;
794     time = wme->time;
795     extra = (DWORD)wme->hWnd;
796     
797     assert( dwFlags & MOUSEEVENTF_ABSOLUTE );
798     posX = (dx * SYSMETRICS_CXSCREEN) >> 16;
799     posY = (dy * SYSMETRICS_CYSCREEN) >> 16;
800   } else {
801     ERR(dinput, "Mouse event not supported...\n");
802     return ;
803   }
804
805   TRACE(dinput, " %ld %ld ", posX, posY);
806
807   if ( dwFlags & MOUSEEVENTF_MOVE ) {
808     if (This->absolute) {
809       if (posX != This->prevX)
810         GEN_EVENT(DIMOFS_X, posX, time, 0);
811       if (posY != This->prevY)
812         GEN_EVENT(DIMOFS_Y, posY, time, 0);
813     } else {
814       /* Relative mouse input : the real fun starts here... */
815       if (This->need_warp) {
816         if (posX != This->prevX)
817           GEN_EVENT(DIMOFS_X, posX - This->prevX, time, 0);
818         if (posY != This->prevY)
819           GEN_EVENT(DIMOFS_Y, posY - This->prevY, time, 0);
820       } else {
821         /* This is the first time the event handler has been called after a
822            GetData of GetState. */
823         if (posX != This->win_centerX) {
824           GEN_EVENT(DIMOFS_X, posX - This->win_centerX, time, 0);
825           This->need_warp = 1;
826         }
827           
828         if (posY != This->win_centerY) {
829           GEN_EVENT(DIMOFS_Y, posY - This->win_centerY, time, 0);
830           This->need_warp = 1;
831         }
832       }
833     }
834   }
835   if ( dwFlags & MOUSEEVENTF_LEFTDOWN ) {
836     if (TRACE_ON(dinput))
837       DUMP(" LD ");
838
839     GEN_EVENT(DIMOFS_BUTTON0, 0xFF, time, 0);
840   }
841   if ( dwFlags & MOUSEEVENTF_LEFTUP ) {
842     if (TRACE_ON(dinput))
843       DUMP(" LU ");
844
845     GEN_EVENT(DIMOFS_BUTTON0, 0x00, time, 0);
846   }
847   if ( dwFlags & MOUSEEVENTF_RIGHTDOWN ) {
848     if (TRACE_ON(dinput))
849       DUMP(" RD ");
850
851     GEN_EVENT(DIMOFS_BUTTON1, 0xFF, time, 0);
852   }
853   if ( dwFlags & MOUSEEVENTF_RIGHTUP ) {
854     if (TRACE_ON(dinput))
855       DUMP(" RU ");
856
857     GEN_EVENT(DIMOFS_BUTTON1, 0x00, time, 0);
858   }
859   if ( dwFlags & MOUSEEVENTF_MIDDLEDOWN ) {
860     if (TRACE_ON(dinput))
861       DUMP(" MD ");
862
863     GEN_EVENT(DIMOFS_BUTTON2, 0xFF, time, 0);
864   }
865   if ( dwFlags & MOUSEEVENTF_MIDDLEUP ) {
866     if (TRACE_ON(dinput))
867       DUMP(" MU ");
868
869     GEN_EVENT(DIMOFS_BUTTON2, 0x00, time, 0);
870   }
871   if (TRACE_ON(dinput))
872     DUMP("\n");
873
874   This->prevX = posX;
875   This->prevY = posY;
876 }
877
878
879 /******************************************************************************
880   *     Acquire : gets exclusive control of the mouse
881   */
882 static HRESULT WINAPI SysMouseAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface)
883 {
884   ICOM_THIS(SysMouseAImpl,iface);
885   RECT  rect;
886   
887   TRACE(dinput,"(this=%p)\n",This);
888
889   if (This->acquired == 0) {
890     POINT       point;
891
892     /* This stores the current mouse handler. */
893     This->prev_handler = mouse_event;
894     
895     /* Store (in a global variable) the current lock */
896     current_lock = (IDirectInputDevice2A*)This;
897     
898     /* Install our own mouse event handler */
899     MOUSE_Enable(dinput_mouse_event);
900     
901     /* Get the window dimension and find the center */
902     GetWindowRect(This->win, &rect);
903     This->win_centerX = (rect.right  - rect.left) / 2;
904     This->win_centerY = (rect.bottom - rect.top ) / 2;
905
906     /* Warp the mouse to the center of the window */
907     TRACE(dinput, "Warping mouse to %ld - %ld\n", This->win_centerX, This->win_centerY);
908     point.x = This->win_centerX;
909     point.y = This->win_centerY;
910     MapWindowPoints(This->win, HWND_DESKTOP, &point, 1);
911     DISPLAY_MoveCursor(point.x, point.y);
912     
913     This->acquired = 1;
914   }
915   return 0;
916 }
917
918 /******************************************************************************
919   *     Unacquire : frees the mouse
920   */
921 static HRESULT WINAPI SysMouseAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface)
922 {
923   ICOM_THIS(SysMouseAImpl,iface);
924
925   TRACE(dinput,"(this=%p)\n",This);
926
927   /* Reinstall previous mouse event handler */
928   MOUSE_Enable(This->prev_handler);
929   This->prev_handler = NULL;
930   
931   /* No more locks */
932   current_lock = NULL;
933
934   /* Unacquire device */
935   This->acquired = 0;
936   
937   return 0;
938 }
939
940 /******************************************************************************
941   *     GetDeviceState : returns the "state" of the mouse.
942   *
943   *   For the moment, only the "standard" return structure (DIMOUSESTATE) is
944   *   supported.
945   */
946 static HRESULT WINAPI SysMouseAImpl_GetDeviceState(
947         LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr
948 ) {
949   ICOM_THIS(SysMouseAImpl,iface);
950   DWORD rx, ry, state;
951   struct DIMOUSESTATE *mstate = (struct DIMOUSESTATE *) ptr;
952   
953   TRACE(dinput,"(this=%p,0x%08lx,%p): \n",This,len,ptr);
954   
955   /* Check if the buffer is big enough */
956   if (len < sizeof(struct DIMOUSESTATE)) {
957     FIXME(dinput, "unsupported state structure.");
958     return DIERR_INVALIDPARAM;
959   }
960   
961   /* Get the mouse position */
962   EVENT_QueryPointer(&rx, &ry, &state);
963   TRACE(dinput,"(X:%ld - Y:%ld)\n", rx, ry);
964
965   /* Fill the mouse state structure */
966   if (This->absolute) {
967     mstate->lX = rx;
968     mstate->lY = ry;
969   } else {
970     mstate->lX = rx - This->win_centerX;
971     mstate->lY = ry - This->win_centerY;
972
973     if ((mstate->lX != 0) || (mstate->lY != 0))
974       This->need_warp = 1;
975   }
976   mstate->lZ = 0;
977   mstate->rgbButtons[0] = (state & MK_LBUTTON ? 0xFF : 0x00);
978   mstate->rgbButtons[1] = (state & MK_RBUTTON ? 0xFF : 0x00);
979   mstate->rgbButtons[2] = (state & MK_MBUTTON ? 0xFF : 0x00);
980   mstate->rgbButtons[3] = 0x00;
981   
982   /* Check if we need to do a mouse warping */
983   if (This->need_warp) {
984     POINT point;
985
986     TRACE(dinput, "Warping mouse to %ld - %ld\n", This->win_centerX, This->win_centerY);
987     point.x = This->win_centerX;
988     point.y = This->win_centerY;
989     MapWindowPoints(This->win, HWND_DESKTOP, &point, 1);
990     DISPLAY_MoveCursor(point.x, point.y);
991
992     This->need_warp = 0;
993   }
994   
995   TRACE(dinput, "(X: %ld - Y: %ld   L: %02x M: %02x R: %02x)\n",
996         mstate->lX, mstate->lY,
997         mstate->rgbButtons[0], mstate->rgbButtons[2], mstate->rgbButtons[1]);
998   
999   return 0;
1000 }
1001
1002 /******************************************************************************
1003   *     GetDeviceState : gets buffered input data.
1004   */
1005 static HRESULT WINAPI SysMouseAImpl_GetDeviceData(LPDIRECTINPUTDEVICE2A iface,
1006                                               DWORD dodsize,
1007                                               LPDIDEVICEOBJECTDATA dod,
1008                                               LPDWORD entries,
1009                                               DWORD flags
1010 ) {
1011   ICOM_THIS(SysMouseAImpl,iface);
1012   
1013   TRACE(dinput,"(%p)->(%ld,%p,%p(0x%08lx),0x%08lx)\n",
1014         This,dodsize,dod,entries,*entries,flags);
1015
1016   if (flags & DIGDD_PEEK)
1017     TRACE(dinput, "DIGDD_PEEK\n");
1018
1019   if (dod == NULL) {
1020     *entries = This->queue_pos;
1021     This->queue_pos = 0;
1022   } else {
1023     /* Check for buffer overflow */
1024     if (This->queue_pos > *entries) {
1025       WARN(dinput, "Buffer overflow not handled properly yet...\n");
1026       This->queue_pos = *entries;
1027     }
1028     if (dodsize != sizeof(DIDEVICEOBJECTDATA)) {
1029       ERR(dinput, "Wrong structure size !\n");
1030       return DIERR_INVALIDPARAM;
1031     }
1032
1033     TRACE(dinput, "Application retrieving %d event(s).\n", This->queue_pos); 
1034     
1035     /* Copy the buffered data into the application queue */
1036     memcpy(dod, This->data_queue, This->queue_pos * dodsize);
1037     
1038     /* Reset the event queue */
1039     This->queue_pos = 0;
1040   }
1041   
1042   /* Check if we need to do a mouse warping */
1043   if (This->need_warp) {
1044     POINT point;
1045
1046     TRACE(dinput, "Warping mouse to %ld - %ld\n", This->win_centerX, This->win_centerY);
1047     point.x = This->win_centerX;
1048     point.y = This->win_centerY;
1049     MapWindowPoints(This->win, HWND_DESKTOP, &point, 1);
1050     DISPLAY_MoveCursor(point.x, point.y);
1051
1052     This->need_warp = 0;
1053   }
1054   
1055   return 0;
1056 }
1057
1058 /******************************************************************************
1059   *     SetProperty : change input device properties
1060   */
1061 static HRESULT WINAPI SysMouseAImpl_SetProperty(LPDIRECTINPUTDEVICE2A iface,
1062                                             REFGUID rguid,
1063                                             LPCDIPROPHEADER ph)
1064 {
1065   ICOM_THIS(SysMouseAImpl,iface);
1066   char  xbuf[50];
1067
1068   if (HIWORD(rguid))
1069     WINE_StringFromCLSID(rguid,xbuf);
1070   else
1071     sprintf(xbuf,"<special guid %ld>",(DWORD)rguid);
1072
1073   TRACE(dinput,"(this=%p,%s,%p)\n",This,xbuf,ph);
1074   
1075   if (!HIWORD(rguid)) {
1076     switch ((DWORD)rguid) {
1077     case DIPROP_BUFFERSIZE: {
1078       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
1079       
1080       TRACE(dinput,"buffersize = %ld\n",pd->dwData);
1081
1082       This->data_queue = (LPDIDEVICEOBJECTDATA)HeapAlloc(GetProcessHeap(),0,
1083                                                           pd->dwData * sizeof(DIDEVICEOBJECTDATA));
1084       This->queue_pos  = 0;
1085       This->queue_len  = pd->dwData;
1086       break;
1087     }
1088     default:
1089       WARN(dinput,"Unknown type %ld\n",(DWORD)rguid);
1090       break;
1091     }
1092   }
1093   
1094   return 0;
1095 }
1096
1097
1098 static ICOM_VTABLE(IDirectInputDevice2A) SysKeyboardAvt={
1099         IDirectInputDevice2AImpl_QueryInterface,
1100         IDirectInputDevice2AImpl_AddRef,
1101         IDirectInputDevice2AImpl_Release,
1102         IDirectInputDevice2AImpl_GetCapabilities,
1103         IDirectInputDevice2AImpl_EnumObjects,
1104         IDirectInputDevice2AImpl_GetProperty,
1105         SysKeyboardAImpl_SetProperty,
1106         SysKeyboardAImpl_Acquire,
1107         SysKeyboardAImpl_Unacquire,
1108         SysKeyboardAImpl_GetDeviceState,
1109         SysKeyboardAImpl_GetDeviceData,
1110         IDirectInputDevice2AImpl_SetDataFormat,
1111         IDirectInputDevice2AImpl_SetEventNotification,
1112         IDirectInputDevice2AImpl_SetCooperativeLevel,
1113         IDirectInputDevice2AImpl_GetObjectInfo,
1114         IDirectInputDevice2AImpl_GetDeviceInfo,
1115         IDirectInputDevice2AImpl_RunControlPanel,
1116         IDirectInputDevice2AImpl_Initialize,
1117         IDirectInputDevice2AImpl_CreateEffect,
1118         IDirectInputDevice2AImpl_EnumEffects,
1119         IDirectInputDevice2AImpl_GetEffectInfo,
1120         IDirectInputDevice2AImpl_GetForceFeedbackState,
1121         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1122         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1123         IDirectInputDevice2AImpl_Escape,
1124         IDirectInputDevice2AImpl_Poll,
1125         IDirectInputDevice2AImpl_SendDeviceData,
1126 };
1127
1128 static ICOM_VTABLE(IDirectInputDevice2A) SysMouseAvt={
1129         IDirectInputDevice2AImpl_QueryInterface,
1130         IDirectInputDevice2AImpl_AddRef,
1131         SysMouseAImpl_Release,
1132         IDirectInputDevice2AImpl_GetCapabilities,
1133         IDirectInputDevice2AImpl_EnumObjects,
1134         IDirectInputDevice2AImpl_GetProperty,
1135         SysMouseAImpl_SetProperty,
1136         SysMouseAImpl_Acquire,
1137         SysMouseAImpl_Unacquire,
1138         SysMouseAImpl_GetDeviceState,
1139         SysMouseAImpl_GetDeviceData,
1140         SysMouseAImpl_SetDataFormat,
1141         IDirectInputDevice2AImpl_SetEventNotification,
1142         SysMouseAImpl_SetCooperativeLevel,
1143         IDirectInputDevice2AImpl_GetObjectInfo,
1144         IDirectInputDevice2AImpl_GetDeviceInfo,
1145         IDirectInputDevice2AImpl_RunControlPanel,
1146         IDirectInputDevice2AImpl_Initialize,
1147         IDirectInputDevice2AImpl_CreateEffect,
1148         IDirectInputDevice2AImpl_EnumEffects,
1149         IDirectInputDevice2AImpl_GetEffectInfo,
1150         IDirectInputDevice2AImpl_GetForceFeedbackState,
1151         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1152         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1153         IDirectInputDevice2AImpl_Escape,
1154         IDirectInputDevice2AImpl_Poll,
1155         IDirectInputDevice2AImpl_SendDeviceData,
1156 };