Changed mouse button mapping and check size of return buffer.
[wine] / windows / dinput.c
1 /*              DirectInput
2  *
3  * Copyright 1998 Marcus Meissner
4  *
5  * Additions (mouse support) Copyright 1998 Lionel Ulmer
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 <X11/Xlib.h>
26 #include <sys/signal.h>
27
28 #include "windows.h"
29 #include "winerror.h"
30 #include "shell.h"
31 #include "ole.h"
32 #include "compobj.h"
33 #include "interfaces.h"
34 #include "gdi.h"
35 #include "heap.h"
36 #include "win.h"
37 #include "dinput.h"
38 #include "debug.h"
39
40 extern BYTE InputKeyStateTable[256];
41 extern int min_keycode, max_keycode;
42 extern WORD keyc2vkey[256];
43
44 static IDirectInputA_VTable ddiavt;
45 static IDirectInputDeviceA_VTable SysKeyboardAvt;
46 static IDirectInputDeviceA_VTable SysMouseAvt;
47
48 /******************************************************************************
49  *      DirectInputCreate32A
50  */
51 HRESULT WINAPI DirectInputCreate32A(HINSTANCE32 hinst, DWORD dwVersion, LPDIRECTINPUT32A *ppDI, LPUNKNOWN punkOuter) {
52         TRACE(dinput, "(0x%08lx,%04lx,%p,%p)\n",
53                 (DWORD)hinst,dwVersion,ppDI,punkOuter
54         );
55         (*ppDI) = (LPDIRECTINPUT32A)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectInput32A));
56         (*ppDI)->ref = 1;
57         (*ppDI)->lpvtbl = &ddiavt;
58         return 0;
59 }
60 /******************************************************************************
61  *      IDirectInputA_EnumDevices
62  */
63 static HRESULT WINAPI IDirectInputA_EnumDevices(
64         LPDIRECTINPUT32A this, DWORD dwDevType, LPDIENUMDEVICESCALLBACK32A lpCallback,
65         LPVOID pvRef, DWORD dwFlags
66 ) {
67   DIDEVICEINSTANCE32A devInstance;
68   int ret;
69
70   TRACE(dinput, "(this=%p,0x%04lx,%p,%p,%04lx)\n", this, dwDevType, lpCallback, pvRef, dwFlags);
71
72 #if 0 /* Not compiled for the moment as long as I do not find
73          the needed constants */
74   /* Ignore this field for the moment */
75   if (dwDevType != 0) {
76     FIXME(dinput, "device filtering not supported.\n");
77   }
78     
79   devInstance.dwSize = sizeof(DIDEVICEINSTANCE32A);
80   
81   /* Return keyboard */
82   devInstance.guidInstance = GUID_SysKeyboard;
83   devInstance.guidProduct = GUID_SysKeyboard;
84   devInstance.dwDevType = 1; /* Constant unknown :-( */
85   strcpy(devInstance.tszInstanceName, "Keyboard");
86   strcpy(devInstance.tszProductName, "Wine Keyboard");
87   
88   ret = lpCallback(&devInstance, pvRef);
89   TRACE(dinput, "Keyboard registered (%d)\n", ret);
90   
91   if (!ret)
92     return 0;
93   
94   /* Return mouse */
95   devInstance.guidInstance = GUID_SysMouse;
96   devInstance.guidProduct = GUID_SysMouse;
97   devInstance.dwDevType = 2; /* Constant unknown :-( */
98   strcpy(devInstance.tszInstanceName, "Mouse");
99   strcpy(devInstance.tszProductName, "Wine Mouse");
100   
101   ret = lpCallback(&devInstance, pvRef);
102   TRACE(dinput, "Mouse registered (%d)\n", ret);
103 #endif
104   
105         return 0;
106 }
107
108 static ULONG WINAPI IDirectInputA_AddRef(LPDIRECTINPUT32A this) {
109         return ++(this->ref);
110 }
111
112 static ULONG WINAPI IDirectInputA_Release(LPDIRECTINPUT32A this) {
113         if (!(--this->ref)) {
114                 HeapFree(GetProcessHeap(),0,this);
115                 return 0;
116         }
117         return this->ref;
118 }
119
120 static HRESULT WINAPI IDirectInputA_CreateDevice(
121         LPDIRECTINPUT32A this,REFGUID rguid,LPDIRECTINPUTDEVICE32A* pdev,
122         LPUNKNOWN punk
123 ) {
124         char    xbuf[50];
125         
126         WINE_StringFromCLSID(rguid,xbuf);
127         FIXME(dinput,"(this=%p,%s,%p,%p): stub\n",this,xbuf,pdev,punk);
128         if (!memcmp(&GUID_SysKeyboard,rguid,sizeof(GUID_SysKeyboard))) {
129                 *pdev = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysKeyboard32A));
130                 (*pdev)->ref = 1;
131                 (*pdev)->lpvtbl = &SysKeyboardAvt;
132                 memcpy(&((*pdev)->guid),rguid,sizeof(*rguid));
133                 memset(((LPSYSKEYBOARD32A)(*pdev))->keystate,0,256);
134                 return 0;
135         }
136         if (!memcmp(&GUID_SysMouse,rguid,sizeof(GUID_SysMouse))) {
137                 *pdev = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(SysMouse32A));
138                 (*pdev)->ref = 1;
139                 (*pdev)->lpvtbl = &SysMouseAvt;
140                 memcpy(&((*pdev)->guid),rguid,sizeof(*rguid));
141                 return 0;
142         }
143         return E_FAIL;
144 }
145
146 static HRESULT WINAPI IDirectInputA_QueryInterface(
147         LPDIRECTINPUT32A this,REFIID riid,LPVOID *ppobj
148 ) {
149         char    xbuf[50];
150
151         WINE_StringFromCLSID(riid,xbuf);
152         TRACE(dinput,"(this=%p,%s,%p)\n",this,xbuf,ppobj);
153         if (!memcmp(&IID_IUnknown,riid,sizeof(*riid))) {
154                 this->lpvtbl->fnAddRef(this);
155                 *ppobj = this;
156                 return 0;
157         }
158         if (!memcmp(&IID_IDirectInputA,riid,sizeof(*riid))) {
159                 this->lpvtbl->fnAddRef(this);
160                 *ppobj = this;
161                 return 0;
162         }
163         return E_FAIL;
164 }
165
166 static HRESULT WINAPI IDirectInputA_Initialize(
167         LPDIRECTINPUT32A this,HINSTANCE32 hinst,DWORD x
168 ) {
169         return DIERR_ALREADYINITIALIZED;
170 }
171
172 static IDirectInputA_VTable ddiavt= {
173         IDirectInputA_QueryInterface,
174         IDirectInputA_AddRef,
175         IDirectInputA_Release,
176         IDirectInputA_CreateDevice,
177         IDirectInputA_EnumDevices,
178         (void*)6,
179         (void*)7,
180         IDirectInputA_Initialize
181 };
182
183 /******************************************************************************
184  *      IDirectInputDeviceA
185  */
186 static HRESULT WINAPI IDirectInputDeviceA_SetDataFormat(
187         LPDIRECTINPUTDEVICE32A this,LPCDIDATAFORMAT df
188 ) {
189         /*
190         int i;
191         TRACE(dinput,"(this=%p,%p)\n",this,df);
192
193         TRACE(dinput,"df.dwSize=%ld\n",df->dwSize);
194         TRACE(dinput,"(df.dwObjsize=%ld)\n",df->dwObjSize);
195         TRACE(dinput,"(df.dwFlags=0x%08lx)\n",df->dwFlags);
196         TRACE(dinput,"(df.dwDataSize=%ld)\n",df->dwDataSize);
197         TRACE(dinput,"(df.dwNumObjs=%ld)\n",df->dwNumObjs);
198
199         for (i=0;i<df->dwNumObjs;i++) {
200                 char    xbuf[50];
201
202                 if (df->rgodf[i].pguid)
203                         WINE_StringFromCLSID(df->rgodf[i].pguid,xbuf);
204                 else
205                         strcpy(xbuf,"<no guid>");
206                 TRACE(dinput,"df.rgodf[%d].guid %s\n",i,xbuf);
207                 TRACE(dinput,"df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
208                 TRACE(dinput,"dwType 0x%02lx,dwInstance %ld\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
209                 TRACE(dinput,"df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
210         }
211         */
212         return 0;
213 }
214
215 static HRESULT WINAPI IDirectInputDeviceA_SetCooperativeLevel(
216         LPDIRECTINPUTDEVICE32A this,HWND32 hwnd,DWORD dwflags
217 ) {
218         FIXME(dinput,"(this=%p,0x%08lx,0x%08lx): stub\n",this,(DWORD)hwnd,dwflags);
219         return 0;
220 }
221
222 static HRESULT WINAPI IDirectInputDeviceA_SetProperty(
223         LPDIRECTINPUTDEVICE32A this,REFGUID rguid,LPCDIPROPHEADER ph
224 ) {
225         char    xbuf[50];
226
227         if (HIWORD(rguid))
228                 WINE_StringFromCLSID(rguid,xbuf);
229         else
230                 sprintf(xbuf,"<special guid %ld>",(DWORD)rguid);
231         TRACE(dinput,"(this=%p,%s,%p)\n",this,xbuf,ph);
232         if (!HIWORD(rguid)) {
233                 switch ((DWORD)rguid) {
234                 case DIPROP_BUFFERSIZE: {
235                         LPCDIPROPDWORD  pd = (LPCDIPROPDWORD)ph;
236
237                         TRACE(dinput,"buffersize = %ld\n",pd->dwData);
238                         break;
239                 }
240                 default:
241                         WARN(dinput,"Unknown type %ld\n",(DWORD)rguid);
242                         break;
243                 }
244         }
245         return 0;
246 }
247
248 static HRESULT WINAPI IDirectInputDeviceA_SetEventNotification(
249         LPDIRECTINPUTDEVICE32A this,HANDLE32 hnd
250 ) {
251         FIXME(dinput,"(this=%p,0x%08lx): stub\n",this,(DWORD)hnd);
252         return 0;
253 }
254
255 static HRESULT WINAPI IDirectInputDeviceA_GetDeviceData(
256         LPDIRECTINPUTDEVICE32A this,DWORD dodsize,LPDIDEVICEOBJECTDATA dod,
257         LPDWORD entries,DWORD flags
258 ) {
259   TRACE(dinput,"IDirectInputDeviceA(%p)->GetDeviceData(%ld,%p,%p(0x%08lx),0x%08lx)\n",
260         this,dodsize,dod,entries,*entries,flags);
261         return 0;
262 }
263
264
265 static HRESULT WINAPI IDirectInputDeviceA_Acquire(LPDIRECTINPUTDEVICE32A this) {
266         FIXME(dinput,"(this=%p): stub\n",this);
267         return 0;
268 }
269
270 static HRESULT WINAPI IDirectInputDeviceA_Unacquire(LPDIRECTINPUTDEVICE32A this) {
271         FIXME(dinput,"(this=%p): stub\n",this);
272         return 0;
273 }
274
275 static ULONG WINAPI IDirectInputDeviceA_Release(LPDIRECTINPUTDEVICE32A this) {
276         this->ref--;
277         if (this->ref)
278                 return this->ref;
279         HeapFree(GetProcessHeap(),0,this);
280         return 0;
281 }
282
283 static HRESULT WINAPI SysKeyboardA_SetProperty(
284         LPDIRECTINPUTDEVICE32A this,REFGUID rguid,LPCDIPROPHEADER ph
285 ) {
286         char                    xbuf[50];
287
288         if (HIWORD(rguid))
289                 WINE_StringFromCLSID(rguid,xbuf);
290         else
291                 sprintf(xbuf,"<special guid %ld>",(DWORD)rguid);
292         TRACE(dinput,"(this=%p,%s,%p)\n",this,xbuf,ph);
293         TRACE(dinput,"(size=%ld,headersize=%ld,obj=%ld,how=%ld\n",
294             ph->dwSize,ph->dwHeaderSize,ph->dwObj,ph->dwHow);
295         if (!HIWORD(rguid)) {
296                 switch ((DWORD)rguid) {
297                 case DIPROP_BUFFERSIZE: {
298                         LPCDIPROPDWORD  pd = (LPCDIPROPDWORD)ph;
299
300                         TRACE(dinput,"(buffersize=%ld)\n",pd->dwData);
301                         break;
302                 }
303                 default:
304                         WARN(dinput,"Unknown type %ld\n",(DWORD)rguid);
305                         break;
306                 }
307         }
308         return 0;
309 }
310
311 static HRESULT WINAPI SysKeyboardA_GetDeviceState(
312         LPDIRECTINPUTDEVICE32A this,DWORD len,LPVOID ptr
313 ) {
314         if (len==256) {
315                 int keyc,vkey;
316
317                 memset(ptr,0,256);
318                 for (keyc=min_keycode;keyc<max_keycode;keyc++)
319                 {
320                     /* X keycode to virtual key */
321                     vkey = keyc2vkey[keyc] & 0xFF;
322                     /* The windows scancode is keyc-min_keycode */
323                     if (InputKeyStateTable[vkey]&0x80) {
324                         ((LPBYTE)ptr)[keyc-min_keycode]=0x80;
325                         ((LPBYTE)ptr)[(keyc-min_keycode)|0x80]=0x80;
326                     }
327                 }
328                 return 0;
329         }
330         WARN(dinput,"whoops, SysKeyboardA_GetDeviceState got len %ld?\n",len);
331         return 0;
332 }
333
334 static HRESULT WINAPI SysKeyboardA_GetDeviceData(
335         LPDIRECTINPUTDEVICE32A this,DWORD dodsize,LPDIDEVICEOBJECTDATA dod,
336         LPDWORD entries,DWORD flags
337 ) {
338         int                     keyc,n,vkey,xentries;
339         LPSYSKEYBOARD32A        kthis = (LPSYSKEYBOARD32A)this;
340
341         TRACE(dinput,"(this=%p,%ld,%p,%p(%ld)),0x%08lx)\n",
342                 this,dodsize,dod,entries,entries?*entries:0,flags);
343         EVENT_WaitNetEvent(FALSE,TRUE);
344         if (entries)
345                 xentries = *entries; 
346         else
347                 xentries = 1;
348
349         n = 0;
350
351         for (keyc=min_keycode;(keyc<max_keycode) && (n<*entries);keyc++)
352         {
353                     /* X keycode to virtual key */
354                     vkey = keyc2vkey[keyc] & 0xFF;
355                     if (kthis->keystate[vkey] == (InputKeyStateTable[vkey]&0x80))
356                         continue;
357                 if (dod) {
358                         /* add an entry */
359                         dod[n].dwOfs            = keyc-min_keycode; /* scancode */
360                         dod[n].dwData           = InputKeyStateTable[vkey]&0x80;
361                         dod[n].dwTimeStamp      = 0; /* umm */
362                         dod[n].dwSequence       = 0; /* umm */
363                         n++;
364                 }
365                 if (!(flags & DIGDD_PEEK))
366                         kthis->keystate[vkey] = InputKeyStateTable[vkey]&0x80;
367                     
368         }
369         
370         if (n) fprintf(stderr,"%d entries\n",n);
371         *entries = n;
372         return 0;
373 }
374
375 static HRESULT WINAPI SysKeyboardA_Acquire(LPDIRECTINPUTDEVICE32A this) {
376         FIXME(dinput,"(this=%p): stub\n",this);
377         return 0;
378 }
379
380 static HRESULT WINAPI SysKeyboardA_Unacquire(LPDIRECTINPUTDEVICE32A this) {
381         FIXME(dinput,"(this=%p): stub\n",this);
382         return 0;
383 }
384
385 static HRESULT WINAPI IDirectInputDeviceA_QueryInterface(
386         LPDIRECTINPUTDEVICE32A this,REFIID riid,LPVOID *ppobj
387 ) {
388         char    xbuf[50];
389
390         WINE_StringFromCLSID(riid,xbuf);
391         TRACE(dinput,"(this=%p,%s,%p)\n",this,xbuf,ppobj);
392         if (!memcmp(&IID_IUnknown,riid,sizeof(*riid))) {
393                 this->lpvtbl->fnAddRef(this);
394                 *ppobj = this;
395                 return 0;
396         }
397         if (!memcmp(&IID_IDirectInputDeviceA,riid,sizeof(*riid))) {
398                 this->lpvtbl->fnAddRef(this);
399                 *ppobj = this;
400                 return 0;
401         }
402         return E_FAIL;
403 }
404
405 /******************************************************************************
406  *      SysMouseA (DInput Mouse support)
407  */
408
409 /******************************************************************************
410   *     SetDataFormat : the application can choose the format of the data
411   *   the device driver sends back with GetDeviceState.
412   *
413   *   For the moment, only the "standard" configuration (c_dfDIMouse) is supported
414   *   in absolute and relative mode.
415   */
416 static HRESULT WINAPI SysMouseA_SetDataFormat(
417         LPDIRECTINPUTDEVICE32A this,LPCDIDATAFORMAT df
418 ) {
419   int i;
420   LPSYSMOUSE32A mthis = (LPSYSMOUSE32A) this;
421   
422   TRACE(dinput,"(this=%p,%p)\n",this,df);
423
424   TRACE(dinput,"(df.dwSize=%ld)\n",df->dwSize);
425   TRACE(dinput,"(df.dwObjsize=%ld)\n",df->dwObjSize);
426   TRACE(dinput,"(df.dwFlags=0x%08lx)\n",df->dwFlags);
427   TRACE(dinput,"(df.dwDataSize=%ld)\n",df->dwDataSize);
428   TRACE(dinput,"(df.dwNumObjs=%ld)\n",df->dwNumObjs);
429
430   for (i=0;i<df->dwNumObjs;i++) {
431     char        xbuf[50];
432     
433     if (df->rgodf[i].pguid)
434       WINE_StringFromCLSID(df->rgodf[i].pguid,xbuf);
435     else
436       strcpy(xbuf,"<no guid>");
437     TRACE(dinput,"df.rgodf[%d].guid %s (%p)\n",i,xbuf, df->rgodf[i].pguid);
438     TRACE(dinput,"df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
439     TRACE(dinput,"dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
440     TRACE(dinput,"df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
441   }
442
443   /* Check size of data format to prevent crashes if the applications
444      sends a smaller buffer */
445   if (df->dwDataSize != sizeof(struct DIMOUSESTATE)) {
446     FIXME(dinput, "non-standard mouse configuration not supported yet.");
447     return DIERR_INVALIDPARAM;
448   }
449   
450   /* For the moment, ignore these fields and return always as if
451      c_dfDIMouse was passed as format... */
452   if (df->dwFlags == DIDF_ABSAXIS)
453     mthis->absolute = 1;
454   else {
455     Window rw, cr;
456     int rx, ry, cx, cy;
457     unsigned int mask;
458     
459     /* We need to get the initial "previous" position to be able
460        to return deltas */
461     mthis->absolute = 0;
462   
463     /* Get the mouse position */
464     TSXQueryPointer(display, rootWindow, &rw, &cr,
465                     &rx, &ry, &cx, &cy, &mask);
466     /* Fill the initial mouse state structure */
467     mthis->prevX = rx;
468     mthis->prevY = ry;
469   }
470   
471   return 0;
472 }
473
474 /******************************************************************************
475   *     GetDeviceState : returns the "state" of the mouse.
476   *
477   *   For the moment, only the "standard" return structure (DIMOUSESTATE) is
478   *   supported.
479   */
480 static HRESULT WINAPI SysMouseA_GetDeviceState(
481         LPDIRECTINPUTDEVICE32A this,DWORD len,LPVOID ptr
482 ) {
483   Window rw, cr;
484   int rx, ry, cx, cy;
485   unsigned int mask;
486   struct DIMOUSESTATE *mstate = (struct DIMOUSESTATE *) ptr;
487   LPSYSMOUSE32A mthis = (LPSYSMOUSE32A) this;
488   
489   TRACE(dinput,"(this=%p,0x%08lx,%p): \n",this,len,ptr);
490   
491   /* Check if the buffer is big enough */
492   if (len < sizeof(struct DIMOUSESTATE)) {
493     FIXME(dinput, "unsupported state structure.");
494     return DIERR_INVALIDPARAM;
495   }
496   
497   /* Get the mouse position */
498   TSXQueryPointer(display, rootWindow, &rw, &cr,
499                   &rx, &ry, &cx, &cy, &mask);
500   TRACE(dinput,"(X:%d - Y:%d)\n", rx, ry);
501
502   /* Fill the mouse state structure */
503   if (mthis->absolute) {
504     mstate->lX = rx;
505     mstate->lY = ry;
506   } else {
507     mstate->lX = rx - mthis->prevX;
508     mstate->lY = ry - mthis->prevY;
509     /* Fill the previous positions */
510     mthis->prevX = rx;
511     mthis->prevY = ry;
512   }
513   mstate->lZ = 0;
514   mstate->rgbButtons[0] = (mask & Button1Mask ? 0xFF : 0x00);
515   mstate->rgbButtons[1] = (mask & Button3Mask ? 0xFF : 0x00); /* Windows button two is X button 3 */
516   mstate->rgbButtons[2] = (mask & Button2Mask ? 0xFF : 0x00);
517   mstate->rgbButtons[3] = (mask & Button4Mask ? 0xFF : 0x00);
518   
519   return 0;
520 }
521
522
523
524 static IDirectInputDeviceA_VTable SysKeyboardAvt={
525         IDirectInputDeviceA_QueryInterface,
526         (void*)2,
527         IDirectInputDeviceA_Release,
528         (void*)4,
529         (void*)5,
530         (void*)6,
531         SysKeyboardA_SetProperty,
532         SysKeyboardA_Acquire,
533         SysKeyboardA_Unacquire,
534         SysKeyboardA_GetDeviceState,
535         SysKeyboardA_GetDeviceData,
536         IDirectInputDeviceA_SetDataFormat,
537         IDirectInputDeviceA_SetEventNotification,
538         IDirectInputDeviceA_SetCooperativeLevel,
539         (void*)15,
540         (void*)16,
541         (void*)17,
542         (void*)18,
543 };
544
545 static IDirectInputDeviceA_VTable SysMouseAvt={
546         IDirectInputDeviceA_QueryInterface,
547         (void*)2,
548         IDirectInputDeviceA_Release,
549         (void*)4,
550         (void*)5,
551         (void*)6,
552         IDirectInputDeviceA_SetProperty,
553         IDirectInputDeviceA_Acquire,
554         IDirectInputDeviceA_Unacquire,
555         SysMouseA_GetDeviceState,
556         IDirectInputDeviceA_GetDeviceData,
557         SysMouseA_SetDataFormat,
558         IDirectInputDeviceA_SetEventNotification,
559         IDirectInputDeviceA_SetCooperativeLevel,
560         (void*)15,
561         (void*)16,
562         (void*)17,
563         (void*)18,
564 };