Fixed some issues found by winapi_check.
[wine] / dlls / dinput / joystick / linuxinput.c
1 /*              DirectInput Joystick device
2  *
3  * Copyright 1998,2000 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  *
6  */
7
8 #include "config.h"
9
10 #ifdef HAVE_LINUX_INPUT_H
11
12 #include <assert.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include <time.h>
16 #include <unistd.h>
17 #include <sys/time.h>
18 #include <sys/fcntl.h>
19 #include <sys/ioctl.h>
20 #include <errno.h>
21 #ifdef HAVE_SYS_ERRNO_H
22 # include <sys/errno.h>
23 #endif
24 #ifdef HAVE_LINUX_INPUT_H
25 # include <linux/input.h>
26 #endif
27
28 #define EVDEVPREFIX     "/dev/input/event"
29
30 #include "debugtools.h"
31 #include "winbase.h"
32 #include "winerror.h"
33 #include "windef.h"
34 #include "dinput.h"
35
36 #include "dinput_private.h"
37 #include "device_private.h"
38
39 DEFAULT_DEBUG_CHANNEL(dinput);
40
41 /* Wine joystick driver object instances */
42 #define WINE_JOYSTICK_AXIS_BASE   0
43 #define WINE_JOYSTICK_BUTTON_BASE 8
44
45 typedef struct JoystickAImpl JoystickAImpl;
46 static ICOM_VTABLE(IDirectInputDevice2A) JoystickAvt;
47 static ICOM_VTABLE(IDirectInputDevice7A) Joystick7Avt;
48 struct JoystickAImpl
49 {
50         /* IDirectInputDevice2AImpl */
51         ICOM_VFIELD(IDirectInputDevice2A);
52         DWORD                           ref;
53         GUID                            guid;
54         
55
56         /* The 'parent' DInput */
57         IDirectInputAImpl *dinput;
58         
59         /* joystick private */
60         /* what range and deadzone the game wants */
61         LONG                            wantmin[ABS_MAX];
62         LONG                            wantmax[ABS_MAX];
63         LONG                            deadz[ABS_MAX];
64
65         /* autodetecting ranges per axe by following movement */
66         LONG                            havemax[ABS_MAX];
67         LONG                            havemin[ABS_MAX];
68
69         int                             joyfd;
70
71         LPDIDATAFORMAT                  df;
72         HANDLE                          hEvent;
73         LPDIDEVICEOBJECTDATA            data_queue;
74         int                             queue_pos, queue_len;
75         DIJOYSTATE                      js;
76
77         /* data returned by the EVIOCGABS() ioctl */
78         int                             axes[ABS_MAX+1][5];
79
80 #define AXE_ABS         0
81 #define AXE_ABSMIN      1
82 #define AXE_ABSMAX      2
83 #define AXE_ABSFUZZ     3
84 #define AXE_ABSFLAT     4
85
86         
87         /* data returned by EVIOCGBIT for EV_ABS and EV_KEY */
88         BYTE                            absbits[(ABS_MAX+7)/8];
89         BYTE                            keybits[(KEY_MAX+7)/8];
90 };
91
92 /* This GUID is slightly different from the linux joystick one. Take note. */
93 static GUID DInput_Wine_Joystick_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
94   0x9e573eda,
95   0x7734,
96   0x11d2,
97   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
98 };
99
100 #define test_bit(arr,bit) (((BYTE*)arr)[bit>>3]&(1<<(bit&7)))
101
102 static BOOL joydev_enum_device(DWORD dwDevType, DWORD dwFlags, LPCDIDEVICEINSTANCEA lpddi)
103 {
104   int i, fd, havejoy = 0;
105
106   if ((dwDevType != 0) && (GET_DIDEVICE_TYPE(dwDevType) != DIDEVTYPE_JOYSTICK))
107       return FALSE;
108
109   for (i=0;i<64;i++) {
110       char      buf[200];
111       BYTE      absbits[(ABS_MAX+7)/8],keybits[(KEY_MAX+7)/8];
112
113       sprintf(buf,EVDEVPREFIX"%d",i);
114       if (-1!=(fd=open(buf,O_RDONLY))) {
115           if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits)) {
116               perror("EVIOCGBIT EV_ABS");
117               close(fd);
118               continue;
119           }
120           if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits)) {
121               perror("EVIOCGBIT EV_KEY");
122               close(fd);
123               continue;
124           }
125           /* A true joystick has at least axis X and Y, and at least 1
126            * button. copied from linux/drivers/input/joydev.c */
127           if (test_bit(absbits,ABS_X) && test_bit(absbits,ABS_Y) &&
128               (   test_bit(keybits,BTN_TRIGGER) ||
129                   test_bit(keybits,BTN_A)       ||
130                   test_bit(keybits,BTN_1)
131               )
132           ) {
133               FIXME("found a joystick at %s!\n",buf);
134               havejoy = 1;
135           }
136           close(fd);
137       }
138       if (havejoy || (errno==ENODEV))
139           break;
140   }
141
142   if (!havejoy)
143       return FALSE;
144
145   TRACE("Enumerating the Joystick device\n");
146     
147   /* Return joystick */
148   lpddi->guidInstance   = GUID_Joystick;
149   lpddi->guidProduct    = DInput_Wine_Joystick_GUID;
150
151   lpddi->dwDevType      = DIDEVTYPE_JOYSTICK |
152                        (DIDEVTYPEJOYSTICK_TRADITIONAL<<8);
153
154   strcpy(lpddi->tszInstanceName,        "Joystick");
155   /* ioctl JSIOCGNAME(len) */
156   strcpy(lpddi->tszProductName, "Wine Joystick");
157   return TRUE;
158 }
159
160 static JoystickAImpl *alloc_device(REFGUID rguid, ICOM_VTABLE(IDirectInputDevice2A) *jvt, IDirectInputAImpl *dinput)
161 {
162   JoystickAImpl* newDevice;
163   int i;
164  
165   newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickAImpl));
166   newDevice->ref        = 1;
167   ICOM_VTBL(newDevice)  = jvt;
168
169   newDevice->joyfd      = -1;
170   newDevice->dinput     = dinput;
171   memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
172   for (i=0;i<ABS_MAX;i++) {
173     newDevice->wantmin[i] = -32768;
174     newDevice->wantmax[i] =  32767;
175     newDevice->deadz[i]   =  1024; /* guessing */
176   }
177   return newDevice;
178 }
179
180 static HRESULT joydev_create_device(IDirectInputAImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
181 {
182   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
183       (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
184     if ((riid == NULL) || (IsEqualGUID(&IID_IDirectInputDevice2A,riid)) || (IsEqualGUID(&IID_IDirectInputDevice2A,riid))) {
185       *pdev=(IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput);
186     
187       TRACE("Creating a Joystick device (%p)\n", *pdev);
188       return DI_OK;
189     } else if (IsEqualGUID(&IID_IDirectInputDevice7A,riid)) {
190       *pdev=(IDirectInputDeviceA*) alloc_device(rguid, (ICOM_VTABLE(IDirectInputDevice2A) *) &Joystick7Avt, dinput);
191     
192       TRACE("Creating a Joystick DInput7A device (%p)\n", *pdev);
193       return DI_OK;
194     } else
195       return DIERR_NOINTERFACE;
196   }
197
198   return DIERR_DEVICENOTREG;
199 }
200
201 static dinput_device joydev = {
202   20,
203   joydev_enum_device,
204   joydev_create_device
205 };
206
207 DECL_GLOBAL_CONSTRUCTOR(joydev_register) { dinput_register_device(&joydev); }
208
209 /******************************************************************************
210  *      Joystick
211  */
212 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE2A iface)
213 {
214         ICOM_THIS(JoystickAImpl,iface);
215
216         This->ref--;
217         if (This->ref)
218                 return This->ref;
219
220         /* Free the data queue */
221         if (This->data_queue != NULL)
222           HeapFree(GetProcessHeap(),0,This->data_queue);
223         
224         /* Free the DataFormat */
225         HeapFree(GetProcessHeap(), 0, This->df);
226         
227         HeapFree(GetProcessHeap(),0,This);
228         return 0;
229 }
230
231 /******************************************************************************
232   *   SetDataFormat : the application can choose the format of the data
233   *   the device driver sends back with GetDeviceState.
234   */
235 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
236         LPDIRECTINPUTDEVICE2A iface,LPCDIDATAFORMAT df
237 )
238 {
239   ICOM_THIS(JoystickAImpl,iface);
240   int i;
241   
242   TRACE("(this=%p,%p)\n",This,df);
243
244   TRACE("(df.dwSize=%ld)\n",df->dwSize);
245   TRACE("(df.dwObjsize=%ld)\n",df->dwObjSize);
246   TRACE("(df.dwFlags=0x%08lx)\n",df->dwFlags);
247   TRACE("(df.dwDataSize=%ld)\n",df->dwDataSize);
248   TRACE("(df.dwNumObjs=%ld)\n",df->dwNumObjs);
249
250   for (i=0;i<df->dwNumObjs;i++) {
251     TRACE("df.rgodf[%d].guid %s (%p)\n",i,debugstr_guid(df->rgodf[i].pguid), df->rgodf[i].pguid);
252     TRACE("df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
253     TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
254     TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
255   }
256   
257   /* Store the new data format */
258   This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
259   memcpy(This->df, df, df->dwSize);
260   This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
261   memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
262   
263   return 0;
264 }
265
266 /******************************************************************************
267   *     Acquire : gets exclusive control of the joystick
268   */
269 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE2A iface)
270 {
271     int         i;
272     ICOM_THIS(JoystickAImpl,iface);
273     char        buf[200];
274   
275     TRACE("(this=%p)\n",This);
276     if (This->joyfd!=-1)
277         return 0;
278     for (i=0;i<64;i++) {
279       sprintf(buf,EVDEVPREFIX"%d",i);
280       if (-1==(This->joyfd=open(buf,O_RDONLY))) {
281         if (errno==ENODEV)
282           return DIERR_NOTFOUND;
283         perror(buf);
284         continue;
285       }
286       if ((-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
287           (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_KEY,sizeof(This->keybits)),This->keybits)) &&
288           (test_bit(This->absbits,ABS_X) && test_bit(This->absbits,ABS_Y) &&
289            (test_bit(This->keybits,BTN_TRIGGER)||
290             test_bit(This->keybits,BTN_A)        ||
291             test_bit(This->keybits,BTN_1)
292           )
293          )
294       )
295         break;
296       close(This->joyfd);
297       This->joyfd = -1;
298     }
299     if (This->joyfd==-1)
300         return DIERR_NOTFOUND;
301
302     for (i=0;i<ABS_MAX;i++) {
303         if (test_bit(This->absbits,i)) {
304           if (-1==ioctl(This->joyfd,EVIOCGABS(i),&(This->axes[i])))
305             continue;
306           FIXME("axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
307               i,
308               This->axes[i][AXE_ABS],
309               This->axes[i][AXE_ABSMIN],
310               This->axes[i][AXE_ABSMAX],
311               This->axes[i][AXE_ABSFUZZ],
312               This->axes[i][AXE_ABSFLAT]
313           );
314           This->havemax[i] = This->axes[i][AXE_ABSMIN];
315           This->havemin[i] = This->axes[i][AXE_ABSMAX];
316         }
317     }
318     MESSAGE("\n");
319
320     return 0;
321 }
322
323 /******************************************************************************
324   *     Unacquire : frees the joystick
325   */
326 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE2A iface)
327 {
328     ICOM_THIS(JoystickAImpl,iface);
329
330     TRACE("(this=%p)\n",This);
331     if (This->joyfd!=-1) {
332         close(This->joyfd);
333         This->joyfd = -1;
334     }
335     return DI_OK;
336 }
337
338 /* 
339  * This maps the read value (from the input event) to a value in the
340  * 'wanted' range. It also autodetects the possible range of the axe and
341  * adapts values accordingly.
342  */
343 static int
344 map_axis(JoystickAImpl* This, int axis, int val) {
345     int xmid = This->axes[axis][AXE_ABS];
346     int xmin = This->axes[axis][AXE_ABSMIN];
347     int xmax = This->axes[axis][AXE_ABSMAX];
348     int hmax = This->havemax[axis];
349     int hmin = This->havemin[axis];
350     int wmin = This->wantmin[axis];
351     int wmax = This->wantmax[axis];
352     int ret;
353
354     if (val > hmax) This->havemax[axis] = hmax = val;
355     if (val < hmin) This->havemin[axis] = hmin = val;
356
357     if (xmin == xmax) return val;
358     if ((hmin == hmax) || (hmax==xmid) || (hmin==xmid)) return val;
359
360     /* dont assume total linearity, but linearity starting from a zeropoint */
361     if (val > xmid) {
362         ret = (val-xmid)*((wmax-wmin)/2)/(hmax-xmid)+0;
363     } else {
364         ret = (xmid-val)*((wmax-wmin)/2)/(hmin-xmid)+0;
365     }
366
367 #if 0
368     /* deadzone doesn't work comfortably enough right now. needs more testing*/
369     if ((ret > -deadz/2 ) && (ret < deadz/2)) {
370         FIXME("%d in deadzone, return mid.\n",val);
371         return (wmax-wmin)/2+wmin;
372     }
373 #endif
374     return ret;
375 }
376
377 static void joy_polldev(JoystickAImpl *This) {
378     struct timeval tv;
379     fd_set      readfds;
380     struct      input_event ie;
381
382     if (This->joyfd==-1)
383         return;
384
385     while (1) {
386         memset(&tv,0,sizeof(tv));
387         FD_ZERO(&readfds);
388         FD_SET(This->joyfd,&readfds);
389
390         if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
391             return;
392
393         /* we have one event, so we can read */
394         if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
395             return;
396
397         TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
398         switch (ie.type) {
399         case EV_KEY:    /* button */
400             switch (ie.code) {
401             case BTN_TRIGGER:   /* normal flight stick */
402             case BTN_A:         /* gamepad */
403             case BTN_1:         /* generic */
404                 GEN_EVENT(DIJOFS_BUTTON(0),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
405                 This->js.rgbButtons[0] = ie.value?0x80:0x00;
406                 break;
407             case BTN_THUMB:
408             case BTN_B:
409             case BTN_2:
410                 GEN_EVENT(DIJOFS_BUTTON(1),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
411                 This->js.rgbButtons[1] = ie.value?0x80:0x00;
412                 break;
413             case BTN_THUMB2:
414             case BTN_C:
415             case BTN_3:
416                 GEN_EVENT(DIJOFS_BUTTON(2),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
417                 This->js.rgbButtons[2] = ie.value?0x80:0x00;
418                 break;
419             default:
420                 FIXME("unhandled joystick axe %x, value %d\n",ie.code,ie.value);
421                 break;
422             }
423             break;
424         case EV_ABS:
425             switch (ie.code) {
426             case ABS_X:
427                 GEN_EVENT(DIJOFS_X,ie.value,ie.time.tv_usec,(This->dinput->evsequence)++);
428                 This->js.lX = map_axis(This,0,ie.value);
429                 break;
430             case ABS_Y:
431                 GEN_EVENT(DIJOFS_Y,ie.value,ie.time.tv_usec,(This->dinput->evsequence)++);
432                 This->js.lY = map_axis(This,1,ie.value);
433             case ABS_Z:
434                 GEN_EVENT(DIJOFS_Z,ie.value,ie.time.tv_usec,(This->dinput->evsequence)++);
435                 This->js.lZ = map_axis(This,2,ie.value);
436                 break;
437             default:
438                 FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
439                 break;
440             }
441             break;
442         default:
443             FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
444             break;
445         }
446     }
447 }
448
449 /******************************************************************************
450   *     GetDeviceState : returns the "state" of the joystick.
451   *
452   */
453 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
454         LPDIRECTINPUTDEVICE2A iface,DWORD len,LPVOID ptr
455 ) {
456     ICOM_THIS(JoystickAImpl,iface);
457   
458     joy_polldev(This);
459
460     TRACE("(this=%p,0x%08lx,%p)\n",This,len,ptr);
461     if (len != sizeof(DIJOYSTATE)) {
462         FIXME("len %ld is not sizeof(DIJOYSTATE), unsupported format.\n",len);
463     }
464     memcpy(ptr,&(This->js),len);
465     This->queue_pos = 0;
466     return 0;
467 }
468
469 /******************************************************************************
470   *     GetDeviceData : gets buffered input data.
471   */
472 static HRESULT WINAPI JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE2A iface,
473                                               DWORD dodsize,
474                                               LPDIDEVICEOBJECTDATA dod,
475                                               LPDWORD entries,
476                                               DWORD flags
477 ) {
478   ICOM_THIS(JoystickAImpl,iface);
479   
480   FIXME("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx),STUB!\n",This,dodsize,*entries,flags);
481
482   joy_polldev(This);
483   if (flags & DIGDD_PEEK)
484     FIXME("DIGDD_PEEK\n");
485
486   if (dod == NULL) {
487   } else {
488   }
489   return 0;
490 }
491
492 /******************************************************************************
493   *     SetProperty : change input device properties
494   */
495 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE2A iface,
496                                             REFGUID rguid,
497                                             LPCDIPROPHEADER ph)
498 {
499   ICOM_THIS(JoystickAImpl,iface);
500
501   FIXME("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
502   FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph->dwSize, ph->dwHeaderSize,ph->dwObj,ph->dwHow);
503   
504   if (!HIWORD(rguid)) {
505     switch ((DWORD)rguid) {
506     case (DWORD) DIPROP_BUFFERSIZE: {
507       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
508
509       FIXME("buffersize = %ld\n",pd->dwData);
510       break;
511     }
512     case (DWORD)DIPROP_RANGE: {
513       LPCDIPROPRANGE    pr = (LPCDIPROPRANGE)ph;
514
515       FIXME("proprange(%ld,%ld)\n",pr->lMin,pr->lMax);
516       switch (ph->dwObj) {
517       case 0:   /* X */
518       case 4:   /* Y */
519       case 8:   /* Z */
520       case 12:  /* Rx */
521       case 16:  /* Ry */
522       case 20:  /* Rz */
523           This->wantmin[ph->dwObj/4] = pr->lMin;
524           This->wantmax[ph->dwObj/4] = pr->lMax;
525           break;
526       default:
527           FIXME("setting proprange %ld - %ld for dwObj %ld\n",pr->lMin,pr->lMax,ph->dwObj);
528       }
529       break;
530     }
531     case (DWORD)DIPROP_DEADZONE: {
532       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
533
534       FIXME("setting deadzone(%ld)\n",pd->dwData);
535       This->deadz[ph->dwObj/4] = pd->dwData;
536       break;
537     }
538     default:
539       FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
540       break;
541     }
542   }
543   return 0;
544 }
545
546 /******************************************************************************
547   *     SetEventNotification : specifies event to be sent on state change
548   */
549 static HRESULT WINAPI JoystickAImpl_SetEventNotification(
550         LPDIRECTINPUTDEVICE2A iface, HANDLE hnd
551 ) {
552     ICOM_THIS(JoystickAImpl,iface);
553
554     TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
555     This->hEvent = hnd;
556     return DI_OK;
557 }
558
559 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
560         LPDIRECTINPUTDEVICE2A iface,
561         LPDIDEVCAPS lpDIDevCaps)
562 {
563     ICOM_THIS(JoystickAImpl,iface);
564     int         xfd = This->joyfd;
565     int         i,axes,buttons;
566     int         wasacquired = 1;
567
568     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
569     if (xfd==-1) {
570         /* yes, games assume we return something, even if unacquired */
571         JoystickAImpl_Acquire(iface);
572         xfd = This->joyfd;
573         wasacquired = 0;
574     }
575     lpDIDevCaps->dwFlags        = DIDC_ATTACHED;
576     lpDIDevCaps->dwDevType      = DIDEVTYPE_JOYSTICK;
577
578     axes=0;
579     for (i=0;i<ABS_MAX;i++) if (test_bit(This->absbits,i)) axes++;
580     buttons=0;
581     for (i=0;i<KEY_MAX;i++) if (test_bit(This->keybits,i)) buttons++;
582
583     lpDIDevCaps->dwAxes = axes;
584     lpDIDevCaps->dwButtons = buttons;
585
586     if (!wasacquired)
587         JoystickAImpl_Unacquire(iface);
588
589     return DI_OK;
590 }
591
592 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE2A iface) {
593     ICOM_THIS(JoystickAImpl,iface);
594     TRACE("(),stub!\n");
595
596     joy_polldev(This);
597     return DI_OK;
598 }
599
600 /******************************************************************************
601   *     EnumObjects : enumerate the different buttons and axis...
602   */
603 static HRESULT WINAPI JoystickAImpl_EnumObjects(
604         LPDIRECTINPUTDEVICE2A iface,
605         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
606         LPVOID lpvRef,
607         DWORD dwFlags)
608 {
609   ICOM_THIS(JoystickAImpl,iface);
610   DIDEVICEOBJECTINSTANCEA ddoi;
611   int xfd = This->joyfd;
612
613
614   TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
615   if (TRACE_ON(dinput)) {
616     DPRINTF("  - flags = ");
617     _dump_EnumObjects_flags(dwFlags);
618     DPRINTF("\n");
619   }
620
621   if (xfd == -1) return DIERR_NOTACQUIRED;
622
623   /* Only the fields till dwFFMaxForce are relevant */
624   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
625     
626   /* For the joystick, do as is done in the GetCapabilities function */
627   /* FIXME: needs more items */
628   if ((dwFlags == DIDFT_ALL) ||
629       (dwFlags & DIDFT_AXIS)) {
630     BYTE i;
631     
632     for (i = 0; i < ABS_MAX; i++) {
633       if (!test_bit(This->absbits,i)) continue;
634
635       switch (i) {
636       case ABS_X:
637         ddoi.guidType = GUID_XAxis;
638         ddoi.dwOfs = DIJOFS_X;
639         break;
640       case ABS_Y:
641         ddoi.guidType = GUID_YAxis;
642         ddoi.dwOfs = DIJOFS_Y;
643         break;
644       case ABS_Z:
645         ddoi.guidType = GUID_ZAxis;
646         ddoi.dwOfs = DIJOFS_Z;
647         break;
648       case ABS_RX:
649         ddoi.guidType = GUID_RxAxis;
650         ddoi.dwOfs = DIJOFS_RX;
651         break;
652       case ABS_RY:
653         ddoi.guidType = GUID_RyAxis;
654         ddoi.dwOfs = DIJOFS_RY;
655         break;
656       case ABS_RZ:
657         ddoi.guidType = GUID_RzAxis;
658         ddoi.dwOfs = DIJOFS_RZ;
659         break;
660       case ABS_THROTTLE:
661         ddoi.guidType = GUID_Slider;
662         ddoi.dwOfs = DIJOFS_SLIDER(0);
663         break;
664       default:
665         FIXME("unhandled abs axis %d, ignoring!\n",i);
666       }
667       ddoi.dwType = DIDFT_MAKEINSTANCE((1<<i) << WINE_JOYSTICK_AXIS_BASE) | DIDFT_ABSAXIS;
668       sprintf(ddoi.tszName, "%d-Axis", i);
669       _dump_OBJECTINSTANCEA(&ddoi);
670       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
671           return DI_OK;
672     }
673   }
674
675   if ((dwFlags == DIDFT_ALL) ||
676       (dwFlags & DIDFT_BUTTON)) {
677     BYTE i;
678     
679     /*The DInput SDK says that GUID_Button is only for mouse buttons but well*/
680
681     ddoi.guidType = GUID_Button;
682     
683     for (i = 0; i < KEY_MAX; i++) {
684       if (!test_bit(This->keybits,i)) continue;
685
686       switch (i) {
687       case BTN_TRIGGER:
688       case BTN_A:
689       case BTN_1:
690           ddoi.dwOfs = DIJOFS_BUTTON(0);
691           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 0) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
692           break;
693         case BTN_THUMB:
694         case BTN_B:
695         case BTN_2:
696           ddoi.dwOfs = DIJOFS_BUTTON(1);
697           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 1) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
698           break;
699         case BTN_THUMB2:
700         case BTN_C:
701         case BTN_3:
702           ddoi.dwOfs = DIJOFS_BUTTON(2);
703           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 2) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
704           break;
705         case BTN_TOP:
706         case BTN_X:
707         case BTN_4:
708           ddoi.dwOfs = DIJOFS_BUTTON(3);
709           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 3) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
710           break;
711         case BTN_TOP2:
712         case BTN_Y:
713         case BTN_5:
714           ddoi.dwOfs = DIJOFS_BUTTON(4);
715           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 4) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
716           break;
717         case BTN_PINKIE:
718         case BTN_Z:
719         case BTN_6:
720           ddoi.dwOfs = DIJOFS_BUTTON(5);
721           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 5) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
722           break;
723         case BTN_BASE:
724         case BTN_TL:
725         case BTN_7:
726           ddoi.dwOfs = DIJOFS_BUTTON(6);
727           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 6) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
728           break;
729         case BTN_BASE2:
730         case BTN_TR:
731         case BTN_8:
732           ddoi.dwOfs = DIJOFS_BUTTON(7);
733           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 7) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
734           break;
735         case BTN_BASE3:
736         case BTN_TL2:
737         case BTN_9:
738           ddoi.dwOfs = DIJOFS_BUTTON(8);
739           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 8) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
740           break;
741         case BTN_BASE4:
742         case BTN_TR2:
743           ddoi.dwOfs = DIJOFS_BUTTON(9);
744           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 9) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
745           break;
746         case BTN_BASE5:
747         case BTN_SELECT:
748           ddoi.dwOfs = DIJOFS_BUTTON(10);
749           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 10) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
750           break;
751       }
752       sprintf(ddoi.tszName, "%d-Button", i);
753       _dump_OBJECTINSTANCEA(&ddoi);
754       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
755           return DI_OK;
756     }
757   }
758
759   if (xfd!=This->joyfd)
760     close(xfd);
761
762   return DI_OK;
763 }
764
765 /******************************************************************************
766   *     GetProperty : get input device properties
767   */
768 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE2A iface,
769                                                 REFGUID rguid,
770                                                 LPDIPROPHEADER pdiph)
771 {
772   ICOM_THIS(JoystickAImpl,iface);
773
774   TRACE("(this=%p,%s,%p): stub!\n",
775         iface, debugstr_guid(rguid), pdiph);
776
777   if (TRACE_ON(dinput))
778     _dump_DIPROPHEADER(pdiph);
779   
780   if (!HIWORD(rguid)) {
781     switch ((DWORD)rguid) {
782     case (DWORD) DIPROP_BUFFERSIZE: {
783       LPDIPROPDWORD     pd = (LPDIPROPDWORD)pdiph;
784       
785       TRACE(" return buffersize = %d\n",This->queue_len);
786       pd->dwData = This->queue_len;
787       break;
788     }
789
790     case (DWORD) DIPROP_RANGE: {
791       LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
792       if ((pdiph->dwHow == DIPH_BYID) &&
793           (pdiph->dwObj & DIDFT_ABSAXIS)) {
794         /* The app is querying the current range of the axis : return the lMin and lMax values */
795         FIXME("unimplemented axis range query.\n");
796       }
797       
798       break;
799     }
800       
801     default:
802       FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
803       break;
804     }
805   }
806   
807   
808   return DI_OK;
809 }
810
811 static ICOM_VTABLE(IDirectInputDevice2A) JoystickAvt = 
812 {
813         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
814         IDirectInputDevice2AImpl_QueryInterface,
815         IDirectInputDevice2AImpl_AddRef,
816         JoystickAImpl_Release,
817         JoystickAImpl_GetCapabilities,
818         JoystickAImpl_EnumObjects,
819         JoystickAImpl_GetProperty,
820         JoystickAImpl_SetProperty,
821         JoystickAImpl_Acquire,
822         JoystickAImpl_Unacquire,
823         JoystickAImpl_GetDeviceState,
824         JoystickAImpl_GetDeviceData,
825         JoystickAImpl_SetDataFormat,
826         JoystickAImpl_SetEventNotification,
827         IDirectInputDevice2AImpl_SetCooperativeLevel,
828         IDirectInputDevice2AImpl_GetObjectInfo,
829         IDirectInputDevice2AImpl_GetDeviceInfo,
830         IDirectInputDevice2AImpl_RunControlPanel,
831         IDirectInputDevice2AImpl_Initialize,
832         IDirectInputDevice2AImpl_CreateEffect,
833         IDirectInputDevice2AImpl_EnumEffects,
834         IDirectInputDevice2AImpl_GetEffectInfo,
835         IDirectInputDevice2AImpl_GetForceFeedbackState,
836         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
837         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
838         IDirectInputDevice2AImpl_Escape,
839         JoystickAImpl_Poll,
840         IDirectInputDevice2AImpl_SendDeviceData,
841 };
842
843 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
844 # define XCAST(fun)     (typeof(Joystick7Avt.fn##fun))
845 #else
846 # define XCAST(fun)     (void*)
847 #endif
848
849 static ICOM_VTABLE(IDirectInputDevice7A) Joystick7Avt = 
850 {
851         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
852         XCAST(QueryInterface)IDirectInputDevice2AImpl_QueryInterface,
853         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
854         XCAST(Release)JoystickAImpl_Release,
855         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
856         XCAST(EnumObjects)JoystickAImpl_EnumObjects,
857         XCAST(GetProperty)JoystickAImpl_GetProperty,
858         XCAST(SetProperty)JoystickAImpl_SetProperty,
859         XCAST(Acquire)JoystickAImpl_Acquire,
860         XCAST(Unacquire)JoystickAImpl_Unacquire,
861         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
862         XCAST(GetDeviceData)JoystickAImpl_GetDeviceData,
863         XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
864         XCAST(SetEventNotification)JoystickAImpl_SetEventNotification,
865         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
866         XCAST(GetObjectInfo)IDirectInputDevice2AImpl_GetObjectInfo,
867         XCAST(GetDeviceInfo)IDirectInputDevice2AImpl_GetDeviceInfo,
868         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
869         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
870         XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
871         XCAST(EnumEffects)IDirectInputDevice2AImpl_EnumEffects,
872         XCAST(GetEffectInfo)IDirectInputDevice2AImpl_GetEffectInfo,
873         XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
874         XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
875         XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
876         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
877         XCAST(Poll)JoystickAImpl_Poll,
878         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
879         IDirectInputDevice7AImpl_EnumEffectsInFile,
880         IDirectInputDevice7AImpl_WriteEffectToFile
881 };
882
883 #undef XCAST
884
885 #endif  /* HAVE_LINUX_INPUT_H */