Fixed DLLMODE handling (--mode is now --subsystem and uses -Wb).
[wine] / dlls / dinput / joystick_linuxinput.c
1 /*              DirectInput Joystick device
2  *
3  * Copyright 1998,2000 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #ifdef HAVE_LINUX_INPUT_H
26
27 #include <assert.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <time.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #ifdef HAVE_SYS_TIME_H
36 # include <sys/time.h>
37 #endif
38 #include <sys/fcntl.h>
39 #ifdef HAVE_SYS_IOCTL_H
40 # include <sys/ioctl.h>
41 #endif
42 #include <errno.h>
43 #ifdef HAVE_SYS_ERRNO_H
44 # include <sys/errno.h>
45 #endif
46
47 #ifdef HAVE_CORRECT_LINUXINPUT_H
48
49 #ifdef HAVE_LINUX_INPUT_H
50 # include <linux/input.h>
51 #endif
52
53
54 #define EVDEVPREFIX     "/dev/input/event"
55
56 #include "wine/debug.h"
57 #include "wine/unicode.h"
58 #include "windef.h"
59 #include "winbase.h"
60 #include "winerror.h"
61 #include "dinput.h"
62
63 #include "dinput_private.h"
64 #include "device_private.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
67
68 /* Wine joystick driver object instances */
69 #define WINE_JOYSTICK_AXIS_BASE   0
70 #define WINE_JOYSTICK_BUTTON_BASE 8
71
72 typedef struct JoystickImpl JoystickImpl;
73 static ICOM_VTABLE(IDirectInputDevice8A) JoystickAvt;
74 static ICOM_VTABLE(IDirectInputDevice8W) JoystickWvt;
75 struct JoystickImpl
76 {
77         LPVOID                          lpVtbl;
78         DWORD                           ref;
79         GUID                            guid;
80
81
82         /* The 'parent' DInput */
83         IDirectInputImpl               *dinput;
84
85         /* joystick private */
86         /* what range and deadzone the game wants */
87         LONG                            wantmin[ABS_MAX];
88         LONG                            wantmax[ABS_MAX];
89         LONG                            deadz[ABS_MAX];
90
91         /* autodetecting ranges per axe by following movement */
92         LONG                            havemax[ABS_MAX];
93         LONG                            havemin[ABS_MAX];
94
95         int                             joyfd;
96
97         LPDIDATAFORMAT                  df;
98         HANDLE                          hEvent;
99         LPDIDEVICEOBJECTDATA            data_queue;
100         int                             queue_head, queue_tail, queue_len;
101         DIJOYSTATE2                     js;
102
103         /* data returned by the EVIOCGABS() ioctl */
104         int                             axes[ABS_MAX+1][5];
105
106 #define AXE_ABS         0
107 #define AXE_ABSMIN      1
108 #define AXE_ABSMAX      2
109 #define AXE_ABSFUZZ     3
110 #define AXE_ABSFLAT     4
111
112
113         /* data returned by EVIOCGBIT for EV_ABS and EV_KEY */
114         BYTE                            absbits[(ABS_MAX+7)/8];
115         BYTE                            keybits[(KEY_MAX+7)/8];
116 };
117
118 /* This GUID is slightly different from the linux joystick one. Take note. */
119 static GUID DInput_Wine_Joystick_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
120   0x9e573eda,
121   0x7734,
122   0x11d2,
123   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
124 };
125
126 static void fake_current_js_state(JoystickImpl *ji);
127
128 #define test_bit(arr,bit) (((BYTE*)arr)[bit>>3]&(1<<(bit&7)))
129
130 static int joydev_have(void)
131 {
132   int i, fd;
133   int havejoy = 0;
134
135   for (i=0;i<64;i++) {
136       char      buf[200];
137       BYTE      absbits[(ABS_MAX+7)/8],keybits[(KEY_MAX+7)/8];
138
139       sprintf(buf,EVDEVPREFIX"%d",i);
140       if (-1!=(fd=open(buf,O_RDONLY))) {
141           if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(absbits)),absbits)) {
142               perror("EVIOCGBIT EV_ABS");
143               close(fd);
144               continue;
145           }
146           if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(keybits)),keybits)) {
147               perror("EVIOCGBIT EV_KEY");
148               close(fd);
149               continue;
150           }
151           /* A true joystick has at least axis X and Y, and at least 1
152            * button. copied from linux/drivers/input/joydev.c */
153           if (test_bit(absbits,ABS_X) && test_bit(absbits,ABS_Y) &&
154               (   test_bit(keybits,BTN_TRIGGER) ||
155                   test_bit(keybits,BTN_A)       ||
156                   test_bit(keybits,BTN_1)
157               )
158           ) {
159               FIXME("found a joystick at %s!\n",buf);
160               havejoy = 1;
161           }
162           close(fd);
163       }
164       if (havejoy || (errno==ENODEV))
165           break;
166   }
167   return havejoy;
168 }
169
170 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, int version)
171 {
172   int havejoy = 0;
173
174   if ((dwDevType != 0) && (GET_DIDEVICE_TYPE(dwDevType) != DIDEVTYPE_JOYSTICK))
175       return FALSE;
176
177   if (dwFlags & DIEDFL_FORCEFEEDBACK)
178     return FALSE;
179
180   havejoy = joydev_have();
181
182   if (!havejoy)
183       return FALSE;
184
185   TRACE("Enumerating the linuxinput Joystick device\n");
186
187   /* Return joystick */
188   lpddi->guidInstance   = GUID_Joystick;
189   lpddi->guidProduct    = DInput_Wine_Joystick_GUID;
190
191   lpddi->guidFFDriver = GUID_NULL;
192   lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL<<8);
193
194   strcpy(lpddi->tszInstanceName, "Joystick");
195   /* ioctl JSIOCGNAME(len) */
196   strcpy(lpddi->tszProductName, "Wine Joystick");
197   return TRUE;
198 }
199
200 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, int version)
201 {
202   int havejoy = 0;
203
204   if ((dwDevType != 0) && (GET_DIDEVICE_TYPE(dwDevType) != DIDEVTYPE_JOYSTICK))
205       return FALSE;
206
207   if (dwFlags & DIEDFL_FORCEFEEDBACK)
208     return FALSE;
209
210   havejoy = joydev_have();
211
212   if (!havejoy)
213       return FALSE;
214
215   TRACE("Enumerating the linuxinput Joystick device\n");
216
217   /* Return joystick */
218   lpddi->guidInstance   = GUID_Joystick;
219   lpddi->guidProduct    = DInput_Wine_Joystick_GUID;
220
221   lpddi->guidFFDriver = GUID_NULL;
222   lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL<<8);
223
224   MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, lpddi->tszInstanceName, MAX_PATH);
225   /* ioctl JSIOCGNAME(len) */
226   MultiByteToWideChar(CP_ACP, 0, "Wine Joystick", -1, lpddi->tszProductName, MAX_PATH);
227   return TRUE;
228 }
229
230 static JoystickImpl *alloc_device(REFGUID rguid, LPVOID jvt, IDirectInputImpl *dinput)
231 {
232   JoystickImpl* newDevice;
233   int i;
234
235   newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
236   newDevice->lpVtbl = jvt;
237   newDevice->ref = 1;
238   newDevice->joyfd = -1;
239   newDevice->dinput = dinput;
240   memcpy(&(newDevice->guid),rguid,sizeof(*rguid));
241   for (i=0;i<ABS_MAX;i++) {
242     newDevice->wantmin[i] = -32768;
243     newDevice->wantmax[i] =  32767;
244     /* TODO: 
245      * direct input defines a default for the deadzone somewhere; but as long
246      * as in map_axis the code for the dead zone is commented out its no
247      * problem
248      */
249     newDevice->deadz[i]   =  0;
250   }
251   fake_current_js_state(newDevice);
252   return newDevice;
253 }
254
255 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
256 {
257   int havejoy = 0;
258
259   havejoy = joydev_have();
260
261   if (!havejoy)
262       return DIERR_DEVICENOTREG;
263
264   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
265       (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
266     if ((riid == NULL) ||
267         IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
268         IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
269         IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
270         IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
271       *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput);
272       TRACE("Creating a Joystick device (%p)\n", *pdev);
273       return DI_OK;
274     } else
275       return DIERR_NOINTERFACE;
276   }
277
278   return DIERR_DEVICENOTREG;
279 }
280
281
282 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
283 {
284   int havejoy = 0;
285
286   havejoy = joydev_have();
287
288   if (!havejoy)
289       return DIERR_DEVICENOTREG;
290
291   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
292       (IsEqualGUID(&DInput_Wine_Joystick_GUID,rguid))) {
293     if ((riid == NULL) ||
294         IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
295         IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
296         IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
297         IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
298       *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput);
299       TRACE("Creating a Joystick device (%p)\n", *pdev);
300       return DI_OK;
301     } else
302       return DIERR_NOINTERFACE;
303   }
304
305   return DIERR_DEVICENOTREG;
306 }
307
308 static dinput_device joydev = {
309   20,
310   joydev_enum_deviceA,
311   joydev_enum_deviceW,
312   joydev_create_deviceA,
313   joydev_create_deviceW
314 };
315
316 DECL_GLOBAL_CONSTRUCTOR(joydev_register) { dinput_register_device(&joydev); }
317
318 /******************************************************************************
319  *      Joystick
320  */
321 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
322 {
323         ICOM_THIS(JoystickImpl,iface);
324
325         This->ref--;
326         if (This->ref)
327                 return This->ref;
328
329         /* Free the data queue */
330         if (This->data_queue != NULL)
331           HeapFree(GetProcessHeap(),0,This->data_queue);
332
333         /* Free the DataFormat */
334         HeapFree(GetProcessHeap(), 0, This->df);
335
336         HeapFree(GetProcessHeap(),0,This);
337         return 0;
338 }
339
340 /******************************************************************************
341   *   SetDataFormat : the application can choose the format of the data
342   *   the device driver sends back with GetDeviceState.
343   */
344 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
345         LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
346 )
347 {
348   ICOM_THIS(JoystickImpl,iface);
349   int i;
350
351   TRACE("(this=%p,%p)\n",This,df);
352
353   TRACE("(df.dwSize=%ld)\n",df->dwSize);
354   TRACE("(df.dwObjsize=%ld)\n",df->dwObjSize);
355   TRACE("(df.dwFlags=0x%08lx)\n",df->dwFlags);
356   TRACE("(df.dwDataSize=%ld)\n",df->dwDataSize);
357   TRACE("(df.dwNumObjs=%ld)\n",df->dwNumObjs);
358
359   for (i=0;i<df->dwNumObjs;i++) {
360     TRACE("df.rgodf[%d].guid %s (%p)\n",i,debugstr_guid(df->rgodf[i].pguid), df->rgodf[i].pguid);
361     TRACE("df.rgodf[%d].dwOfs %ld\n",i,df->rgodf[i].dwOfs);
362     TRACE("dwType 0x%02x,dwInstance %d\n",DIDFT_GETTYPE(df->rgodf[i].dwType),DIDFT_GETINSTANCE(df->rgodf[i].dwType));
363     TRACE("df.rgodf[%d].dwFlags 0x%08lx\n",i,df->rgodf[i].dwFlags);
364   }
365
366   /* Store the new data format */
367   This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
368   memcpy(This->df, df, df->dwSize);
369   This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
370   memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
371
372   return 0;
373 }
374
375 /******************************************************************************
376   *     Acquire : gets exclusive control of the joystick
377   */
378 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
379 {
380     int         i;
381     ICOM_THIS(JoystickImpl,iface);
382     char        buf[200];
383
384     TRACE("(this=%p)\n",This);
385     if (This->joyfd!=-1)
386         return 0;
387     for (i=0;i<64;i++) {
388       sprintf(buf,EVDEVPREFIX"%d",i);
389       if (-1==(This->joyfd=open(buf,O_RDONLY))) {
390         if (errno==ENODEV)
391           return DIERR_NOTFOUND;
392         perror(buf);
393         continue;
394       }
395       if ((-1!=ioctl(This->joyfd,EVIOCGBIT(EV_ABS,sizeof(This->absbits)),This->absbits)) &&
396           (-1!=ioctl(This->joyfd,EVIOCGBIT(EV_KEY,sizeof(This->keybits)),This->keybits)) &&
397           (test_bit(This->absbits,ABS_X) && test_bit(This->absbits,ABS_Y) &&
398            (test_bit(This->keybits,BTN_TRIGGER)||
399             test_bit(This->keybits,BTN_A)        ||
400             test_bit(This->keybits,BTN_1)
401           )
402          )
403       )
404         break;
405       close(This->joyfd);
406       This->joyfd = -1;
407     }
408     if (This->joyfd==-1)
409         return DIERR_NOTFOUND;
410
411     for (i=0;i<ABS_MAX;i++) {
412         if (test_bit(This->absbits,i)) {
413           if (-1==ioctl(This->joyfd,EVIOCGABS(i),&(This->axes[i])))
414             continue;
415           FIXME("axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
416               i,
417               This->axes[i][AXE_ABS],
418               This->axes[i][AXE_ABSMIN],
419               This->axes[i][AXE_ABSMAX],
420               This->axes[i][AXE_ABSFUZZ],
421               This->axes[i][AXE_ABSFLAT]
422           );
423           This->havemin[i] = This->axes[i][AXE_ABSMIN];
424           This->havemax[i] = This->axes[i][AXE_ABSMAX];
425         }
426     }
427     MESSAGE("\n");
428
429         fake_current_js_state(This);
430
431     return 0;
432 }
433
434 /******************************************************************************
435   *     Unacquire : frees the joystick
436   */
437 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
438 {
439     ICOM_THIS(JoystickImpl,iface);
440
441     TRACE("(this=%p)\n",This);
442     if (This->joyfd!=-1) {
443         close(This->joyfd);
444         This->joyfd = -1;
445         return DI_OK;
446     }
447     else 
448         return DI_NOEFFECT;
449 }
450
451 /*
452  * This maps the read value (from the input event) to a value in the
453  * 'wanted' range. It also autodetects the possible range of the axe and
454  * adapts values accordingly.
455  */
456 static int
457 map_axis(JoystickImpl* This, int axis, int val) {
458     int xmin = This->axes[axis][AXE_ABSMIN];
459     int xmax = This->axes[axis][AXE_ABSMAX];
460     int hmax = This->havemax[axis];
461     int hmin = This->havemin[axis];
462     int wmin = This->wantmin[axis];
463     int wmax = This->wantmax[axis];
464     int ret;
465
466     if (val > hmax) This->havemax[axis] = hmax = val;
467     if (val < hmin) This->havemin[axis] = hmin = val;
468
469     if (xmin == xmax) return val;
470
471     /* map the value from the hmin-hmax range into the wmin-wmax range */
472     ret = (val * (wmax-wmin)) / (hmax-hmin) + wmin;
473
474 #if 0
475     /* deadzone doesn't work comfortably enough right now. needs more testing*/
476     if ((ret > -deadz/2 ) && (ret < deadz/2)) {
477         FIXME("%d in deadzone, return mid.\n",val);
478         return (wmax-wmin)/2+wmin;
479     }
480 #endif
481     return ret;
482 }
483
484 /* 
485  * set the current state of the js device as it would be with the middle
486  * values on the axes
487  */
488 static void fake_current_js_state(JoystickImpl *ji)
489 {
490         ji->js.lX  = map_axis(ji, ABS_X,  ji->axes[ABS_X ][AXE_ABS]);
491         ji->js.lY  = map_axis(ji, ABS_Y,  ji->axes[ABS_Y ][AXE_ABS]);
492         ji->js.lZ  = map_axis(ji, ABS_Z,  ji->axes[ABS_Z ][AXE_ABS]);
493         ji->js.lRx = map_axis(ji, ABS_RX, ji->axes[ABS_RX][AXE_ABS]);
494         ji->js.lRy = map_axis(ji, ABS_RY, ji->axes[ABS_RY][AXE_ABS]);
495         ji->js.lRz = map_axis(ji, ABS_RZ, ji->axes[ABS_RZ][AXE_ABS]);
496 }
497
498 static void joy_polldev(JoystickImpl *This) {
499     struct timeval tv;
500     fd_set      readfds;
501     struct      input_event ie;
502
503     if (This->joyfd==-1)
504         return;
505
506     while (1) {
507         memset(&tv,0,sizeof(tv));
508         FD_ZERO(&readfds);
509         FD_SET(This->joyfd,&readfds);
510
511         if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
512             return;
513
514         /* we have one event, so we can read */
515         if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
516             return;
517
518         TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
519         switch (ie.type) {
520         case EV_KEY:    /* button */
521             switch (ie.code) {
522             case BTN_TRIGGER:   /* normal flight stick */
523             case BTN_A:         /* gamepad */
524             case BTN_1:         /* generic */
525                 This->js.rgbButtons[0] = ie.value?0x80:0x00;
526                 GEN_EVENT(DIJOFS_BUTTON(0),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
527                 break;
528             case BTN_THUMB:
529             case BTN_B:
530             case BTN_2:
531                 This->js.rgbButtons[1] = ie.value?0x80:0x00;
532                 GEN_EVENT(DIJOFS_BUTTON(1),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
533                 break;
534             case BTN_THUMB2:
535             case BTN_C:
536             case BTN_3:
537                 This->js.rgbButtons[2] = ie.value?0x80:0x00;
538                 GEN_EVENT(DIJOFS_BUTTON(2),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
539                 break;
540             case BTN_TOP:
541             case BTN_X:
542             case BTN_4:
543                 This->js.rgbButtons[3] = ie.value?0x80:0x00;
544                 GEN_EVENT(DIJOFS_BUTTON(3),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
545                 break;
546             case BTN_TOP2:
547             case BTN_Y:
548             case BTN_5:
549                 This->js.rgbButtons[4] = ie.value?0x80:0x00;
550                 GEN_EVENT(DIJOFS_BUTTON(4),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
551                 break;
552             case BTN_PINKIE:
553             case BTN_Z:
554             case BTN_6:
555                 This->js.rgbButtons[5] = ie.value?0x80:0x00;
556                 GEN_EVENT(DIJOFS_BUTTON(5),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
557                 break;
558             case BTN_BASE:
559             case BTN_TL:
560             case BTN_7:
561                 This->js.rgbButtons[6] = ie.value?0x80:0x00;
562                 GEN_EVENT(DIJOFS_BUTTON(6),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
563                 break;
564             case BTN_BASE2:
565             case BTN_TR:
566             case BTN_8:
567                 This->js.rgbButtons[7] = ie.value?0x80:0x00;
568                 GEN_EVENT(DIJOFS_BUTTON(7),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
569                 break;
570             case BTN_BASE3:
571             case BTN_TL2:
572             case BTN_9:
573                 This->js.rgbButtons[8] = ie.value?0x80:0x00;
574                 GEN_EVENT(DIJOFS_BUTTON(8),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
575                 break;
576             case BTN_BASE4:
577             case BTN_TR2:
578                 This->js.rgbButtons[9] = ie.value?0x80:0x00;
579                 GEN_EVENT(DIJOFS_BUTTON(9),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
580                 break;
581             case BTN_BASE5:
582             case BTN_SELECT:
583                 This->js.rgbButtons[10] = ie.value?0x80:0x00;
584                 GEN_EVENT(DIJOFS_BUTTON(10),ie.value?0x80:0x0,ie.time.tv_usec,(This->dinput->evsequence)++);
585                 break;
586             default:
587                 FIXME("unhandled joystick button %x, value %d\n",ie.code,ie.value);
588                 break;
589             }
590             break;
591         case EV_ABS:
592             switch (ie.code) {
593             case ABS_X:
594                 This->js.lX = map_axis(This,ABS_X,ie.value);
595                 GEN_EVENT(DIJOFS_X,This->js.lX,ie.time.tv_usec,(This->dinput->evsequence)++);
596                 break;
597             case ABS_Y:
598                 This->js.lY = map_axis(This,ABS_Y,ie.value);
599                 GEN_EVENT(DIJOFS_Y,This->js.lY,ie.time.tv_usec,(This->dinput->evsequence)++);
600                 break;
601             case ABS_Z:
602                 This->js.lZ = map_axis(This,ABS_Z,ie.value);
603                 GEN_EVENT(DIJOFS_Z,This->js.lZ,ie.time.tv_usec,(This->dinput->evsequence)++);
604                 break;
605             case ABS_RX:
606                 This->js.lRx = map_axis(This,ABS_RX,ie.value);
607                 GEN_EVENT(DIJOFS_RX,This->js.lRx,ie.time.tv_usec,(This->dinput->evsequence)++);
608                 break;
609             case ABS_RY:
610                 This->js.lRy = map_axis(This,ABS_RY,ie.value);
611                 GEN_EVENT(DIJOFS_RY,This->js.lRy,ie.time.tv_usec,(This->dinput->evsequence)++);
612                 break;
613             case ABS_RZ:
614                 This->js.lRz = map_axis(This,ABS_RZ,ie.value);
615                 GEN_EVENT(DIJOFS_RZ,This->js.lRz,ie.time.tv_usec,(This->dinput->evsequence)++);
616                 break;
617             default:
618                 FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
619                 break;
620             }
621             break;
622         default:
623             FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
624             break;
625         }
626     }
627 }
628
629 /******************************************************************************
630   *     GetDeviceState : returns the "state" of the joystick.
631   *
632   */
633 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
634         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
635 ) {
636     ICOM_THIS(JoystickImpl,iface);
637
638     joy_polldev(This);
639
640     TRACE("(this=%p,0x%08lx,%p)\n",This,len,ptr);
641     if ((len != sizeof(DIJOYSTATE)) && (len != sizeof(DIJOYSTATE2))) {
642         FIXME("len %ld is not sizeof(DIJOYSTATE) or DIJOYSTATE2, unsupported format.\n",len);
643         return E_FAIL;
644     }
645     memcpy(ptr,&(This->js),len);
646     This->queue_head = 0;
647     This->queue_tail = 0;
648     return 0;
649 }
650
651 /******************************************************************************
652   *     GetDeviceData : gets buffered input data.
653   */
654 static HRESULT WINAPI JoystickAImpl_GetDeviceData(LPDIRECTINPUTDEVICE8A iface,
655                                               DWORD dodsize,
656                                               LPDIDEVICEOBJECTDATA dod,
657                                               LPDWORD entries,
658                                               DWORD flags
659 ) {
660   ICOM_THIS(JoystickImpl,iface);
661
662   FIXME("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx),STUB!\n",This,dodsize,*entries,flags);
663
664   joy_polldev(This);
665   if (flags & DIGDD_PEEK)
666     FIXME("DIGDD_PEEK\n");
667
668   if (dod == NULL) {
669   } else {
670   }
671   return 0;
672 }
673
674 /******************************************************************************
675   *     SetProperty : change input device properties
676   */
677 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
678                                             REFGUID rguid,
679                                             LPCDIPROPHEADER ph)
680 {
681   ICOM_THIS(JoystickImpl,iface);
682
683   FIXME("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
684   FIXME("ph.dwSize = %ld, ph.dwHeaderSize =%ld, ph.dwObj = %ld, ph.dwHow= %ld\n",ph->dwSize, ph->dwHeaderSize,ph->dwObj,ph->dwHow);
685
686   if (!HIWORD(rguid)) {
687     switch ((DWORD)rguid) {
688     case (DWORD) DIPROP_BUFFERSIZE: {
689       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
690
691       FIXME("buffersize = %ld\n",pd->dwData);
692       break;
693     }
694     case (DWORD)DIPROP_RANGE: {
695       LPCDIPROPRANGE    pr = (LPCDIPROPRANGE)ph;
696
697       FIXME("proprange(%ld,%ld)\n",pr->lMin,pr->lMax);
698       switch (ph->dwObj) {
699       case 0:   /* X */
700       case 4:   /* Y */
701       case 8:   /* Z */
702       case 12:  /* Rx */
703       case 16:  /* Ry */
704       case 20:  /* Rz */
705           This->wantmin[ph->dwObj/4] = pr->lMin;
706           This->wantmax[ph->dwObj/4] = pr->lMax;
707           break;
708       default:
709           FIXME("setting proprange %ld - %ld for dwObj %ld\n",pr->lMin,pr->lMax,ph->dwObj);
710       }
711       break;
712     }
713     case (DWORD)DIPROP_DEADZONE: {
714       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
715
716       FIXME("setting deadzone(%ld)\n",pd->dwData);
717       This->deadz[ph->dwObj/4] = pd->dwData;
718       break;
719     }
720     default:
721       FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
722       break;
723     }
724   }
725   fake_current_js_state(This);
726   return 0;
727 }
728
729 /******************************************************************************
730   *     SetEventNotification : specifies event to be sent on state change
731   */
732 static HRESULT WINAPI JoystickAImpl_SetEventNotification(
733         LPDIRECTINPUTDEVICE8A iface, HANDLE hnd
734 ) {
735     ICOM_THIS(JoystickImpl,iface);
736
737     TRACE("(this=%p,0x%08lx)\n",This,(DWORD)hnd);
738     This->hEvent = hnd;
739     return DI_OK;
740 }
741
742 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
743         LPDIRECTINPUTDEVICE8A iface,
744         LPDIDEVCAPS lpDIDevCaps)
745 {
746     ICOM_THIS(JoystickImpl,iface);
747     int         xfd = This->joyfd;
748     int         i,axes,buttons;
749     int         wasacquired = 1;
750
751     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
752     if (xfd==-1) {
753         /* yes, games assume we return something, even if unacquired */
754         JoystickAImpl_Acquire(iface);
755         xfd = This->joyfd;
756         wasacquired = 0;
757     }
758     lpDIDevCaps->dwFlags        = DIDC_ATTACHED;
759     lpDIDevCaps->dwDevType      = DIDEVTYPE_JOYSTICK;
760
761     axes=0;
762     for (i=0;i<ABS_MAX;i++) if (test_bit(This->absbits,i)) axes++;
763     buttons=0;
764     for (i=0;i<KEY_MAX;i++) if (test_bit(This->keybits,i)) buttons++;
765
766     lpDIDevCaps->dwAxes = axes;
767     lpDIDevCaps->dwButtons = buttons;
768
769     if (!wasacquired)
770         JoystickAImpl_Unacquire(iface);
771
772     return DI_OK;
773 }
774
775 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface) {
776     ICOM_THIS(JoystickImpl,iface);
777     TRACE("(),stub!\n");
778
779     joy_polldev(This);
780     return DI_OK;
781 }
782
783 /******************************************************************************
784   *     EnumObjects : enumerate the different buttons and axis...
785   */
786 static HRESULT WINAPI JoystickAImpl_EnumObjects(
787         LPDIRECTINPUTDEVICE8A iface,
788         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
789         LPVOID lpvRef,
790         DWORD dwFlags)
791 {
792   ICOM_THIS(JoystickImpl,iface);
793   DIDEVICEOBJECTINSTANCEA ddoi;
794   int xfd = This->joyfd;
795
796
797   TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
798   if (TRACE_ON(dinput)) {
799     TRACE("  - flags = ");
800     _dump_EnumObjects_flags(dwFlags);
801     TRACE("\n");
802   }
803
804   if (xfd == -1) return DIERR_NOTACQUIRED;
805
806   /* Only the fields till dwFFMaxForce are relevant */
807   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
808
809   /* For the joystick, do as is done in the GetCapabilities function */
810   /* FIXME: needs more items */
811   if ((dwFlags == DIDFT_ALL) ||
812       (dwFlags & DIDFT_AXIS)) {
813     BYTE i;
814
815     for (i = 0; i < ABS_MAX; i++) {
816       if (!test_bit(This->absbits,i)) continue;
817
818       switch (i) {
819       case ABS_X:
820         ddoi.guidType = GUID_XAxis;
821         ddoi.dwOfs = DIJOFS_X;
822         break;
823       case ABS_Y:
824         ddoi.guidType = GUID_YAxis;
825         ddoi.dwOfs = DIJOFS_Y;
826         break;
827       case ABS_Z:
828         ddoi.guidType = GUID_ZAxis;
829         ddoi.dwOfs = DIJOFS_Z;
830         break;
831       case ABS_RX:
832         ddoi.guidType = GUID_RxAxis;
833         ddoi.dwOfs = DIJOFS_RX;
834         break;
835       case ABS_RY:
836         ddoi.guidType = GUID_RyAxis;
837         ddoi.dwOfs = DIJOFS_RY;
838         break;
839       case ABS_RZ:
840         ddoi.guidType = GUID_RzAxis;
841         ddoi.dwOfs = DIJOFS_RZ;
842         break;
843       case ABS_THROTTLE:
844         ddoi.guidType = GUID_Slider;
845         ddoi.dwOfs = DIJOFS_SLIDER(0);
846         break;
847       default:
848         FIXME("unhandled abs axis %d, ignoring!\n",i);
849       }
850       ddoi.dwType = DIDFT_MAKEINSTANCE((1<<i) << WINE_JOYSTICK_AXIS_BASE) | DIDFT_ABSAXIS;
851       sprintf(ddoi.tszName, "%d-Axis", i);
852       _dump_OBJECTINSTANCEA(&ddoi);
853       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
854           return DI_OK;
855     }
856   }
857
858   if ((dwFlags == DIDFT_ALL) ||
859       (dwFlags & DIDFT_BUTTON)) {
860     int i;
861
862     /*The DInput SDK says that GUID_Button is only for mouse buttons but well*/
863
864     ddoi.guidType = GUID_Button;
865
866     for (i = 0; i < KEY_MAX; i++) {
867       if (!test_bit(This->keybits,i)) continue;
868
869       switch (i) {
870       case BTN_TRIGGER:
871       case BTN_A:
872       case BTN_1:
873           ddoi.dwOfs = DIJOFS_BUTTON(0);
874           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 0) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
875           break;
876         case BTN_THUMB:
877         case BTN_B:
878         case BTN_2:
879           ddoi.dwOfs = DIJOFS_BUTTON(1);
880           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 1) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
881           break;
882         case BTN_THUMB2:
883         case BTN_C:
884         case BTN_3:
885           ddoi.dwOfs = DIJOFS_BUTTON(2);
886           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 2) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
887           break;
888         case BTN_TOP:
889         case BTN_X:
890         case BTN_4:
891           ddoi.dwOfs = DIJOFS_BUTTON(3);
892           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 3) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
893           break;
894         case BTN_TOP2:
895         case BTN_Y:
896         case BTN_5:
897           ddoi.dwOfs = DIJOFS_BUTTON(4);
898           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 4) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
899           break;
900         case BTN_PINKIE:
901         case BTN_Z:
902         case BTN_6:
903           ddoi.dwOfs = DIJOFS_BUTTON(5);
904           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 5) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
905           break;
906         case BTN_BASE:
907         case BTN_TL:
908         case BTN_7:
909           ddoi.dwOfs = DIJOFS_BUTTON(6);
910           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 6) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
911           break;
912         case BTN_BASE2:
913         case BTN_TR:
914         case BTN_8:
915           ddoi.dwOfs = DIJOFS_BUTTON(7);
916           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 7) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
917           break;
918         case BTN_BASE3:
919         case BTN_TL2:
920         case BTN_9:
921           ddoi.dwOfs = DIJOFS_BUTTON(8);
922           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 8) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
923           break;
924         case BTN_BASE4:
925         case BTN_TR2:
926           ddoi.dwOfs = DIJOFS_BUTTON(9);
927           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 9) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
928           break;
929         case BTN_BASE5:
930         case BTN_SELECT:
931           ddoi.dwOfs = DIJOFS_BUTTON(10);
932           ddoi.dwType = DIDFT_MAKEINSTANCE((0x0001 << 10) << WINE_JOYSTICK_BUTTON_BASE) | DIDFT_PSHBUTTON;
933           break;
934       }
935       sprintf(ddoi.tszName, "%d-Button", i);
936       _dump_OBJECTINSTANCEA(&ddoi);
937       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
938           return DI_OK;
939     }
940   }
941
942   if (xfd!=This->joyfd)
943     close(xfd);
944
945   return DI_OK;
946 }
947
948 static HRESULT WINAPI JoystickWImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
949                                                 LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
950                                                 LPVOID lpvRef,
951                                                 DWORD dwFlags)
952 {
953   ICOM_THIS(JoystickImpl,iface);
954
955   device_enumobjects_AtoWcb_data data;
956
957   data.lpCallBack = lpCallback;
958   data.lpvRef = lpvRef;
959
960   return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
961 }
962
963 /******************************************************************************
964   *     GetProperty : get input device properties
965   */
966 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
967                                                 REFGUID rguid,
968                                                 LPDIPROPHEADER pdiph)
969 {
970   ICOM_THIS(JoystickImpl,iface);
971
972   TRACE("(this=%p,%s,%p): stub!\n",
973         iface, debugstr_guid(rguid), pdiph);
974
975   if (TRACE_ON(dinput))
976     _dump_DIPROPHEADER(pdiph);
977
978   if (!HIWORD(rguid)) {
979     switch ((DWORD)rguid) {
980     case (DWORD) DIPROP_BUFFERSIZE: {
981       LPDIPROPDWORD     pd = (LPDIPROPDWORD)pdiph;
982
983       TRACE(" return buffersize = %d\n",This->queue_len);
984       pd->dwData = This->queue_len;
985       break;
986     }
987
988     case (DWORD) DIPROP_RANGE: {
989       /* LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph; */
990       if ((pdiph->dwHow == DIPH_BYID) &&
991           (pdiph->dwObj & DIDFT_ABSAXIS)) {
992         /* The app is querying the current range of the axis : return the lMin and lMax values */
993         FIXME("unimplemented axis range query.\n");
994       }
995
996       break;
997     }
998
999     default:
1000       FIXME("Unknown type %ld (%s)\n",(DWORD)rguid,debugstr_guid(rguid));
1001       break;
1002     }
1003   }
1004
1005
1006   return DI_OK;
1007 }
1008
1009 static ICOM_VTABLE(IDirectInputDevice8A) JoystickAvt =
1010 {
1011         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1012         IDirectInputDevice2AImpl_QueryInterface,
1013         IDirectInputDevice2AImpl_AddRef,
1014         JoystickAImpl_Release,
1015         JoystickAImpl_GetCapabilities,
1016         JoystickAImpl_EnumObjects,
1017         JoystickAImpl_GetProperty,
1018         JoystickAImpl_SetProperty,
1019         JoystickAImpl_Acquire,
1020         JoystickAImpl_Unacquire,
1021         JoystickAImpl_GetDeviceState,
1022         JoystickAImpl_GetDeviceData,
1023         JoystickAImpl_SetDataFormat,
1024         JoystickAImpl_SetEventNotification,
1025         IDirectInputDevice2AImpl_SetCooperativeLevel,
1026         IDirectInputDevice2AImpl_GetObjectInfo,
1027         IDirectInputDevice2AImpl_GetDeviceInfo,
1028         IDirectInputDevice2AImpl_RunControlPanel,
1029         IDirectInputDevice2AImpl_Initialize,
1030         IDirectInputDevice2AImpl_CreateEffect,
1031         IDirectInputDevice2AImpl_EnumEffects,
1032         IDirectInputDevice2AImpl_GetEffectInfo,
1033         IDirectInputDevice2AImpl_GetForceFeedbackState,
1034         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1035         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1036         IDirectInputDevice2AImpl_Escape,
1037         JoystickAImpl_Poll,
1038         IDirectInputDevice2AImpl_SendDeviceData,
1039         IDirectInputDevice7AImpl_EnumEffectsInFile,
1040         IDirectInputDevice7AImpl_WriteEffectToFile,
1041         IDirectInputDevice8AImpl_BuildActionMap,
1042         IDirectInputDevice8AImpl_SetActionMap,
1043         IDirectInputDevice8AImpl_GetImageInfo
1044 };
1045
1046 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1047 # define XCAST(fun)     (typeof(JoystickWvt.fun))
1048 #else
1049 # define XCAST(fun)     (void*)
1050 #endif
1051
1052 static ICOM_VTABLE(IDirectInputDevice8W) JoystickWvt =
1053 {
1054         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1055         IDirectInputDevice2WImpl_QueryInterface,
1056         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1057         XCAST(Release)JoystickAImpl_Release,
1058         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1059         JoystickWImpl_EnumObjects,
1060         XCAST(GetProperty)JoystickAImpl_GetProperty,
1061         XCAST(SetProperty)JoystickAImpl_SetProperty,
1062         XCAST(Acquire)JoystickAImpl_Acquire,
1063         XCAST(Unacquire)JoystickAImpl_Unacquire,
1064         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1065         XCAST(GetDeviceData)JoystickAImpl_GetDeviceData,
1066         XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1067         XCAST(SetEventNotification)JoystickAImpl_SetEventNotification,
1068         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1069         IDirectInputDevice2WImpl_GetObjectInfo,
1070         IDirectInputDevice2WImpl_GetDeviceInfo,
1071         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1072         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1073         XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
1074         IDirectInputDevice2WImpl_EnumEffects,
1075         IDirectInputDevice2WImpl_GetEffectInfo,
1076         XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
1077         XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1078         XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1079         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1080         XCAST(Poll)JoystickAImpl_Poll,
1081         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1082         IDirectInputDevice7WImpl_EnumEffectsInFile,
1083         IDirectInputDevice7WImpl_WriteEffectToFile,
1084         IDirectInputDevice8WImpl_BuildActionMap,
1085         IDirectInputDevice8WImpl_SetActionMap,
1086         IDirectInputDevice8WImpl_GetImageInfo
1087 };
1088 #undef XCAST
1089
1090 #endif  /* HAVE_LINUX_INPUT_H */
1091
1092 #endif