opengl32: Avoid generating a wrapper for internal functions when we can call the...
[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  * Copyright 2005 Daniel Remenak
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <assert.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
36 #endif
37 #include <sys/fcntl.h>
38 #ifdef HAVE_SYS_IOCTL_H
39 # include <sys/ioctl.h>
40 #endif
41 #include <errno.h>
42 #ifdef HAVE_SYS_ERRNO_H
43 # include <sys/errno.h>
44 #endif
45 #ifdef HAVE_LINUX_INPUT_H
46 # include <linux/input.h>
47 # if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
48 #  define HAVE_CORRECT_LINUXINPUT_H
49 # endif
50 #endif
51 #ifdef HAVE_SYS_POLL_H
52 # include <sys/poll.h>
53 #endif
54
55 #include "wine/debug.h"
56 #include "wine/unicode.h"
57 #include "windef.h"
58 #include "winbase.h"
59 #include "winerror.h"
60 #include "dinput.h"
61
62 #include "dinput_private.h"
63 #include "device_private.h"
64
65 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
66
67 #ifdef HAVE_CORRECT_LINUXINPUT_H
68
69 #define EVDEVPREFIX     "/dev/input/event"
70
71 /* Wine joystick driver object instances */
72 #define WINE_JOYSTICK_AXIS_BASE   0
73 #define WINE_JOYSTICK_POV_BASE    6
74 #define WINE_JOYSTICK_BUTTON_BASE 8
75
76 typedef struct EffectListItem EffectListItem;
77 struct EffectListItem
78 {
79         LPDIRECTINPUTEFFECT ref;
80         struct EffectListItem* next;
81 };
82
83 /* implemented in effect_linuxinput.c */
84 HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
85 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
86 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
87
88 typedef struct JoystickImpl JoystickImpl;
89 static const IDirectInputDevice8AVtbl JoystickAvt;
90 static const IDirectInputDevice8WVtbl JoystickWvt;
91
92 struct JoyDev {
93         char *device;
94         char *name;
95         GUID guid;
96
97         int has_ff;
98         int num_effects;
99
100         /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
101         BYTE                            evbits[(EV_MAX+7)/8];
102         BYTE                            absbits[(ABS_MAX+7)/8];
103         BYTE                            keybits[(KEY_MAX+7)/8];
104         BYTE                            ffbits[(FF_MAX+7)/8];   
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         /* data returned by the EVIOCGABS() ioctl */
113         int                             axes[ABS_MAX][5];
114         /* LUT for KEY_ to offset in rgbButtons */
115         BYTE                            buttons[KEY_MAX];
116
117         /* autodetecting ranges per axe by following movement */
118         LONG                            havemax[ABS_MAX];
119         LONG                            havemin[ABS_MAX];
120 };
121
122 struct JoystickImpl
123 {
124         struct IDirectInputDevice2AImpl base;
125
126         struct JoyDev                  *joydev;
127
128         /* The 'parent' DInput */
129         IDirectInputImpl               *dinput;
130
131         /* joystick private */
132         /* what range and deadzone the game wants */
133         LONG                            wantmin[ABS_MAX];
134         LONG                            wantmax[ABS_MAX];
135         LONG                            deadz[ABS_MAX];
136
137         int                             joyfd;
138
139         LPDIDATAFORMAT                  df;
140         DataFormat                      *transform;     /* wine to user format converter */
141         int                             *offsets;       /* object offsets */
142         DIJOYSTATE2                     js;
143
144         /* Force feedback variables */
145         EffectListItem*                 top_effect;
146         int                             ff_state;
147 };
148
149 static void fake_current_js_state(JoystickImpl *ji);
150 static DWORD map_pov(int event_value, int is_x);
151 static void find_joydevs(void);
152 static int lxinput_to_user_offset(JoystickImpl *This, int ie_type, int ie_code);
153
154 /* This GUID is slightly different from the linux joystick one. Take note. */
155 static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
156   0x9e573eda,
157   0x7734,
158   0x11d2,
159   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
160 };
161
162 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
163
164 #define MAX_JOYDEV 64
165
166 static int have_joydevs = -1;
167 static struct JoyDev *joydevs = NULL;
168
169 static void find_joydevs(void)
170 {
171   int i;
172
173   if (have_joydevs!=-1) {
174     return;
175   }
176
177   have_joydevs = 0;
178
179   for (i=0;i<MAX_JOYDEV;i++) {
180     char        buf[MAX_PATH];
181     struct JoyDev joydev = {0};
182     int fd;
183     int no_ff_check = 0;
184     int j, buttons;
185
186     snprintf(buf,MAX_PATH,EVDEVPREFIX"%d",i);
187     buf[MAX_PATH-1] = 0;
188
189     if ((fd=open(buf, O_RDWR))==-1) {
190       fd = open(buf, O_RDONLY);
191       no_ff_check = 1;
192     }
193
194     if (fd!=-1) {
195       if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(joydev.evbits)),joydev.evbits))) {
196         perror("EVIOCGBIT 0");
197         close(fd);
198         continue; 
199       }
200       if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(joydev.absbits)),joydev.absbits)) {
201         perror("EVIOCGBIT EV_ABS");
202         close(fd);
203         continue;
204       }
205       if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(joydev.keybits)),joydev.keybits)) {
206         perror("EVIOCGBIT EV_KEY");
207         close(fd);
208         continue;
209       }
210       /* A true joystick has at least axis X and Y, and at least 1
211        * button. copied from linux/drivers/input/joydev.c */
212       if (test_bit(joydev.absbits,ABS_X) && test_bit(joydev.absbits,ABS_Y) &&
213           (   test_bit(joydev.keybits,BTN_TRIGGER)      ||
214               test_bit(joydev.keybits,BTN_A)    ||
215               test_bit(joydev.keybits,BTN_1)
216           )
217          ) {
218
219         joydev.device = strdup(buf);
220
221         if (-1!=ioctl(fd, EVIOCGNAME(MAX_PATH-1), buf)) {
222           buf[MAX_PATH-1] = 0;
223           joydev.name = strdup(buf);
224         } else {
225           joydev.name = joydev.device;
226         }
227
228         joydev.guid = DInput_Wine_Joystick_Base_GUID;
229         joydev.guid.Data3 += have_joydevs;
230
231         TRACE("Found a joystick on %s: %s (%s)\n", 
232             joydev.device, joydev.name, 
233             debugstr_guid(&joydev.guid)
234             );
235
236 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
237         if (!no_ff_check) {
238           if ((!test_bit(joydev.evbits,EV_FF))
239               || (-1==ioctl(fd,EVIOCGBIT(EV_FF,sizeof(joydev.ffbits)),joydev.ffbits)) 
240               || (-1==ioctl(fd,EVIOCGEFFECTS,&joydev.num_effects))
241               || (joydev.num_effects <= 0)) {
242             close(fd);
243           } else {
244             TRACE(" ... with force feedback\n");
245             joydev.has_ff = 1;
246           }
247         }
248 #endif
249
250         for (j=0;j<ABS_MAX;j++) {
251           if (test_bit(joydev.absbits,j)) {
252             if (-1!=ioctl(fd,EVIOCGABS(j),&(joydev.axes[j]))) {
253               TRACE(" ... with axe %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
254                   j,
255                   joydev.axes[j][AXE_ABS],
256                   joydev.axes[j][AXE_ABSMIN],
257                   joydev.axes[j][AXE_ABSMAX],
258                   joydev.axes[j][AXE_ABSFUZZ],
259                   joydev.axes[j][AXE_ABSFLAT]
260                   );
261               joydev.havemin[j] = joydev.axes[j][AXE_ABSMIN];
262               joydev.havemax[j] = joydev.axes[j][AXE_ABSMAX];
263             }
264           }
265         }
266
267         buttons = 0;
268         for (j=0;j<KEY_MAX;j++) {
269           if (test_bit(joydev.keybits,j)) {
270             TRACE(" ... with button %d: %d\n", j, buttons);
271             joydev.buttons[j] = 0x80 | buttons;
272             buttons++;
273           }
274         }
275
276         if (have_joydevs==0) {
277           joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
278         } else {
279           HeapReAlloc(GetProcessHeap(), 0, joydevs, (1+have_joydevs) * sizeof(struct JoyDev));
280         }
281         memcpy(joydevs+have_joydevs, &joydev, sizeof(struct JoyDev));
282         have_joydevs++;
283       }
284
285       close(fd);
286     }
287   }
288 }
289
290 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
291 {
292   find_joydevs();
293
294   if (id >= have_joydevs) {
295     return FALSE;
296   }
297  
298   if (!((dwDevType == 0) ||
299         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
300         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
301     return FALSE;
302
303 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
304   if (dwFlags & DIEDFL_FORCEFEEDBACK)
305     return FALSE;
306 #endif
307
308   if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
309     lpddi->guidInstance = joydevs[id].guid;
310     lpddi->guidProduct  = DInput_Wine_Joystick_Base_GUID;
311
312     lpddi->guidFFDriver = GUID_NULL;
313     if (version >= 0x0800)
314       lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
315     else
316       lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
317
318     strcpy(lpddi->tszInstanceName, joydevs[id].name);
319     strcpy(lpddi->tszProductName, joydevs[id].device);
320     return TRUE;
321   }
322   return FALSE;
323 }
324
325 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
326 {
327   find_joydevs();
328
329   if (id >= have_joydevs) {
330     return FALSE;
331   }
332
333   if (!((dwDevType == 0) ||
334         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
335         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
336     return FALSE;
337
338 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
339   if (dwFlags & DIEDFL_FORCEFEEDBACK)
340     return FALSE;
341 #endif
342
343   if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
344     lpddi->guidInstance = joydevs[id].guid;
345     lpddi->guidProduct  = DInput_Wine_Joystick_Base_GUID;
346
347     lpddi->guidFFDriver = GUID_NULL;
348     if (version >= 0x0800)
349       lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
350     else
351       lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
352
353     MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
354     MultiByteToWideChar(CP_ACP, 0, joydevs[id].device, -1, lpddi->tszProductName, MAX_PATH);
355     return TRUE;
356   }
357   return FALSE;
358 }
359
360 static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, struct JoyDev *joydev)
361 {
362   JoystickImpl* newDevice;
363   int i;
364
365   newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
366   if (newDevice==NULL) {
367     return NULL;
368   }
369
370   newDevice->base.lpVtbl = jvt;
371   newDevice->base.ref = 1;
372   memcpy(&newDevice->base.guid, rguid, sizeof(*rguid));
373   InitializeCriticalSection(&newDevice->base.crit);
374   newDevice->joyfd = -1;
375   newDevice->dinput = dinput;
376   newDevice->joydev = joydev;
377 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
378   newDevice->ff_state = FF_STATUS_STOPPED;
379 #endif
380   for (i=0;i<ABS_MAX;i++) {
381     /* apps expect the range to be the same they would get from the
382      * GetProperty/range method */
383     newDevice->wantmin[i] = newDevice->joydev->havemin[i];
384     newDevice->wantmax[i] = newDevice->joydev->havemax[i];
385     /* TODO: 
386      * direct input defines a default for the deadzone somewhere; but as long
387      * as in map_axis the code for the dead zone is commented out its no
388      * problem
389      */
390     newDevice->deadz[i]   =  0;
391   }
392   fake_current_js_state(newDevice);
393
394   /* wine uses DIJOYSTATE2 as it's internal format so copy
395    * the already defined format c_dfDIJoystick2 */
396   newDevice->df = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwSize);
397   if (newDevice->df == 0)
398     goto FAILED;
399   CopyMemory(newDevice->df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
400
401   /* copy default objects */
402   newDevice->df->rgodf = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
403   if (newDevice->df->rgodf == 0)
404     goto FAILED;
405   CopyMemory(newDevice->df->rgodf,c_dfDIJoystick2.rgodf,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
406
407   /* create an offsets array */
408   newDevice->offsets = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,c_dfDIJoystick2.dwNumObjs*sizeof(int));
409   if (newDevice->offsets == 0)
410     goto FAILED;
411
412   /* create the default transform filter */
413   newDevice->transform = create_DataFormat(&c_dfDIJoystick2, newDevice->df, newDevice->offsets);
414
415   return newDevice;
416
417 FAILED:
418   HeapFree(GetProcessHeap(),0,newDevice->df->rgodf);
419   HeapFree(GetProcessHeap(),0,newDevice->df);
420   HeapFree(GetProcessHeap(),0,newDevice);
421   return NULL;
422 }
423
424 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
425 {
426   int i;
427
428   find_joydevs();
429
430   for (i=0; i<have_joydevs; i++) {
431     if (IsEqualGUID(&GUID_Joystick,rguid) ||
432         IsEqualGUID(&joydevs[i].guid,rguid)
433         ) {
434       if ((riid == NULL) ||
435           IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
436           IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
437           IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
438           IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
439         *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput, &joydevs[i]);
440         TRACE("Creating a Joystick device (%p)\n", *pdev);
441         if (*pdev==0) {
442           ERR("out of memory\n");
443           return DIERR_OUTOFMEMORY;
444         }
445         return DI_OK;
446       } else {
447         return DIERR_NOINTERFACE;
448       }
449     }
450   }
451
452   return DIERR_DEVICENOTREG;
453 }
454
455
456 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
457 {
458   int i;
459
460   find_joydevs();
461
462   for (i=0; i<have_joydevs; i++) {
463     if (IsEqualGUID(&GUID_Joystick,rguid) ||
464         IsEqualGUID(&joydevs[i].guid,rguid)
465         ) {
466       if ((riid == NULL) ||
467           IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
468           IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
469           IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
470           IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
471         *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput, &joydevs[i]);
472         TRACE("Creating a Joystick device (%p)\n", *pdev);
473         if (*pdev==0) {
474           ERR("out of memory\n");
475           return DIERR_OUTOFMEMORY;
476         }
477         return DI_OK;
478       } else {
479         return DIERR_NOINTERFACE;
480       }
481     }
482   }
483
484   return DIERR_DEVICENOTREG;
485 }
486
487 const struct dinput_device joystick_linuxinput_device = {
488   "Wine Linux-input joystick driver",
489   joydev_enum_deviceA,
490   joydev_enum_deviceW,
491   joydev_create_deviceA,
492   joydev_create_deviceW
493 };
494
495 /******************************************************************************
496  *      Joystick
497  */
498 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
499 {
500         JoystickImpl *This = (JoystickImpl *)iface;
501         ULONG ref;
502
503         ref = InterlockedDecrement(&This->base.ref);
504         if (ref)
505                 return ref;
506
507         /* Reset the FF state, free all effects, etc */
508         IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
509
510         /* Free the data queue */
511         HeapFree(GetProcessHeap(), 0, This->base.data_queue);
512
513         /* Free the DataFormat */
514         HeapFree(GetProcessHeap(), 0, This->df->rgodf);
515         HeapFree(GetProcessHeap(), 0, This->df);
516
517         /* Free the offsets array */
518         HeapFree(GetProcessHeap(),0,This->offsets);
519         
520         /* release the data transform filter */
521         release_DataFormat(This->transform);
522
523         DeleteCriticalSection(&This->base.crit);
524         
525         HeapFree(GetProcessHeap(),0,This);
526         return 0;
527 }
528
529 /******************************************************************************
530   *   SetDataFormat : the application can choose the format of the data
531   *   the device driver sends back with GetDeviceState.
532   */
533 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
534         LPDIRECTINPUTDEVICE8A iface,LPCDIDATAFORMAT df
535 )
536 {
537   JoystickImpl *This = (JoystickImpl *)iface;
538
539   TRACE("(this=%p,%p)\n",This,df);
540
541   if (df == NULL) {
542     WARN("invalid pointer\n");
543     return E_POINTER;
544   }
545
546   if (df->dwSize != sizeof(*df)) {
547     WARN("invalid argument\n");
548     return DIERR_INVALIDPARAM;
549   }
550
551   _dump_DIDATAFORMAT(df);
552
553   if (This->joyfd!=-1) {
554     WARN("acquired\n");
555     return DIERR_ACQUIRED;
556   }
557
558   HeapFree(GetProcessHeap(),0,This->df->rgodf);
559   HeapFree(GetProcessHeap(),0,This->df);
560   release_DataFormat(This->transform);
561
562   /* Store the new data format */
563   This->df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
564   if (This->df==NULL) {
565     return DIERR_OUTOFMEMORY;
566   }
567   memcpy(This->df, df, df->dwSize);
568   This->df->rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
569   if (This->df->rgodf==NULL) {
570     HeapFree(GetProcessHeap(), 0, This->df);
571     return DIERR_OUTOFMEMORY;
572   }
573   memcpy(This->df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
574
575   This->transform = create_DataFormat(&c_dfDIJoystick2, This->df, This->offsets);
576
577   return DI_OK;
578 }
579
580 /******************************************************************************
581   *     Acquire : gets exclusive control of the joystick
582   */
583 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
584 {
585     JoystickImpl *This = (JoystickImpl *)iface;
586     HRESULT res;
587
588     TRACE("(this=%p)\n",This);
589
590     if (This->df==NULL) {
591       return DIERR_INVALIDPARAM;
592     }
593     res = IDirectInputDevice2AImpl_Acquire(iface);
594     if (res==DI_OK) {
595       if (-1==(This->joyfd=open(This->joydev->device,O_RDWR))) {
596         if (-1==(This->joyfd=open(This->joydev->device,O_RDONLY))) {
597           /* Couldn't open the device at all */
598           perror(This->joydev->device);
599           IDirectInputDevice2AImpl_Unacquire(iface);
600           return DIERR_NOTFOUND;
601         } else {
602           /* Couldn't open in r/w but opened in read-only. */
603           WARN("Could not open %s in read-write mode.  Force feedback will be disabled.\n", This->joydev->device);
604         }
605       }
606     }
607
608     return res;
609 }
610
611 /******************************************************************************
612   *     Unacquire : frees the joystick
613   */
614 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
615 {
616     JoystickImpl *This = (JoystickImpl *)iface;
617     HRESULT res;
618
619     TRACE("(this=%p)\n",This);
620     res = IDirectInputDevice2AImpl_Unacquire(iface);
621     if (res==DI_OK && This->joyfd!=-1) {
622       close(This->joyfd);
623       This->joyfd = -1;
624     }
625     return res;
626 }
627
628 /*
629  * This maps the read value (from the input event) to a value in the
630  * 'wanted' range. It also autodetects the possible range of the axe and
631  * adapts values accordingly.
632  */
633 static int
634 map_axis(JoystickImpl* This, int axis, int val) {
635     int xmin = This->joydev->axes[axis][AXE_ABSMIN];
636     int xmax = This->joydev->axes[axis][AXE_ABSMAX];
637     int hmax = This->joydev->havemax[axis];
638     int hmin = This->joydev->havemin[axis];
639     int wmin = This->wantmin[axis];
640     int wmax = This->wantmax[axis];
641     int ret;
642
643     if (val > hmax) This->joydev->havemax[axis] = hmax = val;
644     if (val < hmin) This->joydev->havemin[axis] = hmin = val;
645
646     if (xmin == xmax) return val;
647
648     /* map the value from the hmin-hmax range into the wmin-wmax range */
649     ret = MulDiv( val - hmin, wmax - wmin, hmax - hmin ) + wmin;
650
651     TRACE("xmin=%d xmax=%d hmin=%d hmax=%d wmin=%d wmax=%d val=%d ret=%d\n", xmin, xmax, hmin, hmax, wmin, wmax, val, ret);
652
653 #if 0
654     /* deadzone doesn't work comfortably enough right now. needs more testing*/
655     if ((ret > -deadz/2 ) && (ret < deadz/2)) {
656         FIXME("%d in deadzone, return mid.\n",val);
657         return (wmax-wmin)/2+wmin;
658     }
659 #endif
660     return ret;
661 }
662
663 /* 
664  * set the current state of the js device as it would be with the middle
665  * values on the axes
666  */
667 static void fake_current_js_state(JoystickImpl *ji)
668 {
669         int i;
670         /* center the axes */
671         ji->js.lX           = test_bit(ji->joydev->absbits, ABS_X)        ? map_axis(ji, ABS_X,        ji->joydev->axes[ABS_X       ][AXE_ABS]) : 0;
672         ji->js.lY           = test_bit(ji->joydev->absbits, ABS_Y)        ? map_axis(ji, ABS_Y,        ji->joydev->axes[ABS_Y       ][AXE_ABS]) : 0;
673         ji->js.lZ           = test_bit(ji->joydev->absbits, ABS_Z)        ? map_axis(ji, ABS_Z,        ji->joydev->axes[ABS_Z       ][AXE_ABS]) : 0;
674         ji->js.lRx          = test_bit(ji->joydev->absbits, ABS_RX)       ? map_axis(ji, ABS_RX,       ji->joydev->axes[ABS_RX      ][AXE_ABS]) : 0;
675         ji->js.lRy          = test_bit(ji->joydev->absbits, ABS_RY)       ? map_axis(ji, ABS_RY,       ji->joydev->axes[ABS_RY      ][AXE_ABS]) : 0;
676         ji->js.lRz          = test_bit(ji->joydev->absbits, ABS_RZ)       ? map_axis(ji, ABS_RZ,       ji->joydev->axes[ABS_RZ      ][AXE_ABS]) : 0;
677         ji->js.rglSlider[0] = test_bit(ji->joydev->absbits, ABS_THROTTLE) ? map_axis(ji, ABS_THROTTLE, ji->joydev->axes[ABS_THROTTLE][AXE_ABS]) : 0;
678         ji->js.rglSlider[1] = test_bit(ji->joydev->absbits, ABS_RUDDER)   ? map_axis(ji, ABS_RUDDER,   ji->joydev->axes[ABS_RUDDER  ][AXE_ABS]) : 0;
679         /* POV center is -1 */
680         for (i=0; i<4; i++) {
681                 ji->js.rgdwPOV[i] = -1;
682         }
683 }
684
685 /*
686  * Maps an event value to a DX "clock" position:
687  *           0
688  * 27000    -1 9000
689  *       18000
690  */
691 static DWORD map_pov(int event_value, int is_x) 
692 {
693         DWORD ret = -1;
694         if (is_x) {
695                 if (event_value<0) {
696                         ret = 27000;
697                 } else if (event_value>0) {
698                         ret = 9000;
699                 }
700         } else {
701                 if (event_value<0) {
702                         ret = 0;
703                 } else if (event_value>0) {
704                         ret = 18000;
705                 }
706         }
707         return ret;
708 }
709
710 /* defines how the linux input system offset mappings into c_dfDIJoystick2 */
711 static int
712 lxinput_to_user_offset(JoystickImpl *This, int ie_type, int ie_code )
713 {
714   int offset = -1;
715   switch (ie_type) {
716     case EV_ABS:
717       switch (ie_code) {
718         case ABS_X:                     offset = 0; break;
719         case ABS_Y:                     offset = 1; break;
720         case ABS_Z:                     offset = 2; break;
721         case ABS_RX:                    offset = 3; break;
722         case ABS_RY:                    offset = 4; break;
723         case ABS_RZ:                    offset = 5; break;
724         case ABS_THROTTLE:              offset = 6; break;
725         case ABS_RUDDER:                offset = 7; break;
726         case ABS_HAT0X: case ABS_HAT0Y: offset = 8; break;
727         case ABS_HAT1X: case ABS_HAT1Y: offset = 9; break;
728         case ABS_HAT2X: case ABS_HAT2Y: offset = 10; break;
729         case ABS_HAT3X: case ABS_HAT3Y: offset = 11; break;
730         /* XXX when adding new axes here also fix the offset for the buttons bellow */
731         default:
732           FIXME("Unhandled EV_ABS(0x%02X)\n", ie_code);
733           return -1;
734       }
735       break;
736     case EV_KEY:
737       if (ie_code >= 128) {
738         WARN("DX8 does not support more than 128 buttons\n");
739         return -1;
740       }
741       offset = 12 + ie_code; /* XXX */
742       break;
743     default:
744       FIXME("Unhandled type(0x%02X)\n", ie_type);
745       return -1;
746   }
747   return This->offsets[offset];
748 }
749
750 /* convert wine format offset to user format object index */
751 static void joy_polldev(JoystickImpl *This) {
752     struct pollfd plfd;
753     struct      input_event ie;
754     int         btn, offset;
755
756     if (This->joyfd==-1)
757         return;
758
759     while (1) {
760         plfd.fd = This->joyfd;
761         plfd.events = POLLIN;
762
763         if (poll(&plfd,1,0) != 1)
764             return;
765
766         /* we have one event, so we can read */
767         if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
768             return;
769
770         TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
771         switch (ie.type) {
772         case EV_KEY:    /* button */
773             btn = This->joydev->buttons[ie.code];
774             TRACE("(%p) %d -> %d\n", This, ie.code, btn);
775             if (btn&0x80) {
776               btn &= 0x7F;
777               if ((offset = lxinput_to_user_offset(This, ie.type, btn)) == -1) {
778                 return;
779               }
780               This->js.rgbButtons[btn] = ie.value?0x80:0x00;
781               queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgbButtons[btn],
782                           ie.time.tv_usec, This->dinput->evsequence++);
783             }
784             break;
785         case EV_ABS:
786             if ((offset = lxinput_to_user_offset(This, ie.type, ie.code)) == -1) {
787               return;
788             }
789             switch (ie.code) {
790             case ABS_X:
791                 This->js.lX = map_axis(This,ABS_X,ie.value);
792                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lX,
793                             ie.time.tv_usec, This->dinput->evsequence++);
794                 break;
795             case ABS_Y:
796                 This->js.lY = map_axis(This,ABS_Y,ie.value);
797                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lY,
798                             ie.time.tv_usec, This->dinput->evsequence++);
799                 break;
800             case ABS_Z:
801                 This->js.lZ = map_axis(This,ABS_Z,ie.value);
802                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lZ,
803                             ie.time.tv_usec, This->dinput->evsequence++);
804                 break;
805             case ABS_RX:
806                 This->js.lRx = map_axis(This,ABS_RX,ie.value);
807                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lRx,
808                             ie.time.tv_usec, This->dinput->evsequence++);
809                 break;
810             case ABS_RY:
811                 This->js.lRy = map_axis(This,ABS_RY,ie.value);
812                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lRy,
813                             ie.time.tv_usec, This->dinput->evsequence++);
814                 break;
815             case ABS_RZ:
816                 This->js.lRz = map_axis(This,ABS_RZ,ie.value);
817                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.lRz,
818                             ie.time.tv_usec, This->dinput->evsequence++);
819                 break;
820             case ABS_THROTTLE:
821                 This->js.rglSlider[0] = map_axis(This,ABS_THROTTLE,ie.value);
822                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rglSlider[0],
823                             ie.time.tv_usec, This->dinput->evsequence++);
824                 break;
825             case ABS_RUDDER:
826                 This->js.rglSlider[1] = map_axis(This,ABS_RUDDER,ie.value);
827                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rglSlider[1],
828                             ie.time.tv_usec, This->dinput->evsequence++);
829                 break;
830             case ABS_HAT0X:
831             case ABS_HAT0Y:
832                 This->js.rgdwPOV[0] = map_pov(ie.value,ie.code==ABS_HAT0X);
833                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[0],
834                             ie.time.tv_usec, This->dinput->evsequence++);
835                 break;
836             case ABS_HAT1X:
837             case ABS_HAT1Y:
838                 This->js.rgdwPOV[1] = map_pov(ie.value,ie.code==ABS_HAT1X);
839                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[1],
840                             ie.time.tv_usec, This->dinput->evsequence++);
841                 break;
842             case ABS_HAT2X:
843             case ABS_HAT2Y:
844                 This->js.rgdwPOV[2] = map_pov(ie.value,ie.code==ABS_HAT2X);
845                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[2],
846                             ie.time.tv_usec, This->dinput->evsequence++);
847                 break;
848             case ABS_HAT3X:
849             case ABS_HAT3Y:
850                 This->js.rgdwPOV[3] = map_pov(ie.value,ie.code==ABS_HAT3X);
851                 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, This->js.rgdwPOV[3],
852                             ie.time.tv_usec, This->dinput->evsequence++);
853                 break;
854             default:
855                 FIXME("unhandled joystick axe event (code %d, value %d)\n",ie.code,ie.value);
856                 break;
857             }
858             break;
859 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
860         case EV_FF_STATUS:
861             This->ff_state = ie.value;
862             break;
863 #endif
864 #ifdef EV_SYN
865         case EV_SYN:
866             /* there is nothing to do */
867             break;
868 #endif
869         default:
870             FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
871             break;
872         }
873     }
874 }
875
876 /******************************************************************************
877   *     GetDeviceState : returns the "state" of the joystick.
878   *
879   */
880 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
881         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
882 ) {
883     JoystickImpl *This = (JoystickImpl *)iface;
884
885     TRACE("(this=%p,0x%08x,%p)\n", This, len, ptr);
886
887     if (This->joyfd==-1) {
888         WARN("not acquired\n");
889         return DIERR_NOTACQUIRED;
890     }
891
892     joy_polldev(This);
893
894     /* convert and copy data to user supplied buffer */
895     fill_DataFormat(ptr, &This->js, This->transform);
896
897     return DI_OK;
898 }
899
900 /******************************************************************************
901   *     SetProperty : change input device properties
902   */
903 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
904                                             REFGUID rguid,
905                                             LPCDIPROPHEADER ph)
906 {
907   JoystickImpl *This = (JoystickImpl *)iface;
908
909   if (!ph) {
910     WARN("invalid argument\n");
911     return DIERR_INVALIDPARAM;
912   }
913
914   TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
915   TRACE("ph.dwSize = %d, ph.dwHeaderSize =%d, ph.dwObj = %d, ph.dwHow= %d\n",
916         ph->dwSize, ph->dwHeaderSize, ph->dwObj, ph->dwHow);
917
918   if (!HIWORD(rguid)) {
919     switch (LOWORD(rguid)) {
920     case (DWORD)DIPROP_RANGE: {
921       LPCDIPROPRANGE    pr = (LPCDIPROPRANGE)ph;
922
923       if (ph->dwHow == DIPH_DEVICE) {
924         int i;
925         TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
926         for (i = 0; i < This->df->dwNumObjs; i++) {
927           This->wantmin[i] = pr->lMin;
928           This->wantmax[i] = pr->lMax;
929         }
930       } else {
931         int obj = find_property(This->df, ph);
932         TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
933         if (obj >= 0) {
934           This->wantmin[obj] = pr->lMin;
935           This->wantmax[obj] = pr->lMax;
936         }
937       }
938       fake_current_js_state(This);
939       break;
940     }
941     case (DWORD)DIPROP_DEADZONE: {
942       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
943       if (ph->dwHow == DIPH_DEVICE) {
944         int i;
945         TRACE("deadzone(%d) all\n", pd->dwData);
946         for (i = 0; i < This->df->dwNumObjs; i++) {
947           This->deadz[i] = pd->dwData;
948         }
949       } else {
950         int obj = find_property(This->df, ph);
951         TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
952         if (obj >= 0) {
953           This->deadz[obj] = pd->dwData;
954         }
955       }
956       fake_current_js_state(This);
957       break;
958     }
959     case (DWORD)DIPROP_CALIBRATIONMODE: {
960       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
961       FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
962       break;
963     }
964     default:
965       return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
966     }
967   }
968   return DI_OK;
969 }
970
971 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
972         LPDIRECTINPUTDEVICE8A iface,
973         LPDIDEVCAPS lpDIDevCaps)
974 {
975     JoystickImpl *This = (JoystickImpl *)iface;
976     int         i,axes,buttons,povs;
977
978     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
979
980     if (!lpDIDevCaps) {
981         WARN("invalid pointer\n");
982         return E_POINTER;
983     }
984
985     if (lpDIDevCaps->dwSize < sizeof(DIDEVCAPS)) {
986         WARN("invalid argument\n");
987         return DIERR_INVALIDPARAM;
988     }
989
990     lpDIDevCaps->dwFlags        = DIDC_ATTACHED;
991     if (This->dinput->dwVersion >= 0x0800)
992         lpDIDevCaps->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
993     else
994         lpDIDevCaps->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
995
996     axes=0;
997     for (i=0;i<ABS_MAX;i++) {
998       if (!test_bit(This->joydev->absbits,i)) continue;
999       switch (i) {
1000       case ABS_HAT0X: case ABS_HAT0Y:
1001       case ABS_HAT1X: case ABS_HAT1Y:
1002       case ABS_HAT2X: case ABS_HAT2Y:
1003       case ABS_HAT3X: case ABS_HAT3Y:
1004         /* will be handled as POV - see below */
1005         break;
1006       default:
1007         axes++;
1008       }
1009     }
1010     buttons=0;
1011     for (i=0;i<KEY_MAX;i++) if (test_bit(This->joydev->keybits,i)) buttons++;
1012     povs=0;
1013     for (i=0; i<4; i++) {
1014       if (test_bit(This->joydev->absbits,ABS_HAT0X+(i<<1)) && test_bit(This->joydev->absbits,ABS_HAT0Y+(i<<1))) {
1015         povs ++;
1016       }
1017     }
1018
1019     if (This->joydev->has_ff) 
1020          lpDIDevCaps->dwFlags |= DIDC_FORCEFEEDBACK;
1021
1022     lpDIDevCaps->dwAxes = axes;
1023     lpDIDevCaps->dwButtons = buttons;
1024     lpDIDevCaps->dwPOVs = povs;
1025
1026     return DI_OK;
1027 }
1028
1029 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface) {
1030     JoystickImpl *This = (JoystickImpl *)iface;
1031     TRACE("(%p)\n",This);
1032
1033     if (This->joyfd==-1) {
1034       return DIERR_NOTACQUIRED;
1035     }
1036
1037     joy_polldev(This);
1038     return DI_OK;
1039 }
1040
1041 /******************************************************************************
1042   *     EnumObjects : enumerate the different buttons and axis...
1043   */
1044 static HRESULT WINAPI JoystickAImpl_EnumObjects(
1045         LPDIRECTINPUTDEVICE8A iface,
1046         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
1047         LPVOID lpvRef,
1048         DWORD dwFlags)
1049 {
1050   JoystickImpl *This = (JoystickImpl *)iface;
1051   DIDEVICEOBJECTINSTANCEA ddoi;
1052   int user_offset, user_object;
1053
1054   TRACE("(this=%p,%p,%p,%08x)\n", This, lpCallback, lpvRef, dwFlags);
1055   if (TRACE_ON(dinput)) {
1056     TRACE("  - flags = ");
1057     _dump_EnumObjects_flags(dwFlags);
1058     TRACE("\n");
1059   }
1060
1061   /* Only the fields till dwFFMaxForce are relevant */
1062   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
1063
1064   /* For the joystick, do as is done in the GetCapabilities function */
1065   /* FIXME: needs more items */
1066   if ((dwFlags == DIDFT_ALL) ||
1067       (dwFlags & DIDFT_AXIS)) {
1068     BYTE i;
1069
1070     for (i = 0; i < ABS_MAX; i++) {
1071       if (!test_bit(This->joydev->absbits,i)) continue;
1072
1073       switch (i) {
1074       case ABS_X:
1075         ddoi.guidType = GUID_XAxis;
1076         break;
1077       case ABS_Y:
1078         ddoi.guidType = GUID_YAxis;
1079         break;
1080       case ABS_Z:
1081         ddoi.guidType = GUID_ZAxis;
1082         break;
1083       case ABS_RX:
1084         ddoi.guidType = GUID_RxAxis;
1085         break;
1086       case ABS_RY:
1087         ddoi.guidType = GUID_RyAxis;
1088         break;
1089       case ABS_RZ:
1090         ddoi.guidType = GUID_RzAxis;
1091         break;
1092       case ABS_THROTTLE:
1093       case ABS_RUDDER:
1094         ddoi.guidType = GUID_Slider;
1095         break;
1096       case ABS_HAT0X: case ABS_HAT0Y:
1097       case ABS_HAT1X: case ABS_HAT1Y:
1098       case ABS_HAT2X: case ABS_HAT2Y:
1099       case ABS_HAT3X: case ABS_HAT3Y:
1100         /* will be handled as POV - see below */
1101         continue;
1102       default:
1103         FIXME("unhandled abs axis 0x%02x, ignoring!\n",i);
1104         continue;
1105       }
1106       if ((user_offset = lxinput_to_user_offset(This, EV_ABS, i)) == -1) {
1107         continue;
1108       }
1109       user_object = offset_to_object(This->df, user_offset);
1110       ddoi.dwType = This->df->rgodf[user_object].dwType & 0x00ffffff;
1111       ddoi.dwOfs =  This->df->rgodf[user_object].dwOfs;
1112       /* Linux event force feedback supports only (and always) x and y axes */
1113       if (i == ABS_X || i == ABS_Y) {
1114         if (This->joydev->has_ff)
1115           ddoi.dwFlags |= DIDOI_FFACTUATOR;
1116       }
1117       sprintf(ddoi.tszName, "%d-Axis", i);
1118       _dump_OBJECTINSTANCEA(&ddoi);
1119       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1120         return DI_OK;
1121       }
1122     }
1123   }
1124
1125   if ((dwFlags == DIDFT_ALL) ||
1126       (dwFlags & DIDFT_POV)) {
1127     int i;
1128     ddoi.guidType = GUID_POV;
1129     for (i=0; i<4; i++) {
1130       if (test_bit(This->joydev->absbits,ABS_HAT0X+(i<<1)) && test_bit(This->joydev->absbits,ABS_HAT0Y+(i<<1))) {
1131         if ((user_offset = lxinput_to_user_offset(This, EV_ABS, ABS_HAT0X+i))== -1) {
1132           continue;
1133         }
1134         user_object = offset_to_object(This->df, user_offset);
1135         ddoi.dwType = This->df->rgodf[user_object].dwType & 0x00ffffff;
1136         ddoi.dwOfs =  This->df->rgodf[user_object].dwOfs;
1137         sprintf(ddoi.tszName, "%d-POV", i);
1138         _dump_OBJECTINSTANCEA(&ddoi);
1139         if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1140           return DI_OK;
1141         }
1142       }
1143     }
1144   }
1145
1146   if ((dwFlags == DIDFT_ALL) ||
1147       (dwFlags & DIDFT_BUTTON)) {
1148     int i, btncount=0;
1149
1150     /*The DInput SDK says that GUID_Button is only for mouse buttons but well*/
1151
1152     ddoi.guidType = GUID_Button;
1153
1154     for (i = 0; i < KEY_MAX; i++) {
1155       if (!test_bit(This->joydev->keybits,i)) continue;
1156       if ((user_offset = lxinput_to_user_offset(This, EV_KEY, btncount)) == -1) {
1157         continue;
1158       }
1159       user_object = offset_to_object(This->df, user_offset);
1160       ddoi.dwType = This->df->rgodf[user_object].dwType & 0x00ffffff;
1161       ddoi.dwOfs =  This->df->rgodf[user_object].dwOfs;
1162       sprintf(ddoi.tszName, "%d-Button", btncount);
1163       btncount++;
1164       _dump_OBJECTINSTANCEA(&ddoi);
1165       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) {
1166         return DI_OK;
1167       }
1168     }
1169   }
1170
1171   return DI_OK;
1172 }
1173
1174 static HRESULT WINAPI JoystickWImpl_EnumObjects(LPDIRECTINPUTDEVICE8W iface,
1175                                                 LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
1176                                                 LPVOID lpvRef,
1177                                                 DWORD dwFlags)
1178 {
1179   JoystickImpl *This = (JoystickImpl *)iface;
1180
1181   device_enumobjects_AtoWcb_data data;
1182
1183   data.lpCallBack = lpCallback;
1184   data.lpvRef = lpvRef;
1185
1186   return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
1187 }
1188
1189 /******************************************************************************
1190   *     GetProperty : get input device properties
1191   */
1192 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
1193                                                 REFGUID rguid,
1194                                                 LPDIPROPHEADER pdiph)
1195 {
1196   JoystickImpl *This = (JoystickImpl *)iface;
1197
1198   TRACE("(this=%p,%s,%p)\n",
1199         iface, debugstr_guid(rguid), pdiph);
1200
1201   if (TRACE_ON(dinput))
1202     _dump_DIPROPHEADER(pdiph);
1203
1204   if (!HIWORD(rguid)) {
1205     switch (LOWORD(rguid)) {
1206     case (DWORD) DIPROP_RANGE: {
1207       LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
1208       int obj = find_property(This->df, pdiph);
1209       if (obj >= 0) {
1210         pr->lMin = This->joydev->havemin[obj];
1211         pr->lMax = This->joydev->havemax[obj];
1212         TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
1213       }
1214       break;
1215     }
1216
1217     default:
1218       return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
1219     }
1220   }
1221
1222
1223   return DI_OK;
1224 }
1225
1226 /****************************************************************************** 
1227   *     CreateEffect - Create a new FF effect with the specified params
1228   */
1229 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
1230                                                  REFGUID rguid,
1231                                                  LPCDIEFFECT lpeff,
1232                                                  LPDIRECTINPUTEFFECT *ppdef,
1233                                                  LPUNKNOWN pUnkOuter)
1234 {
1235 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1236     EffectListItem* new = NULL;
1237     HRESULT retval = DI_OK;
1238 #endif
1239
1240     JoystickImpl* This = (JoystickImpl*)iface;
1241     TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
1242
1243 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
1244     TRACE("not available (compiled w/o ff support)\n");
1245     *ppdef = NULL;
1246     return DI_OK;
1247 #else
1248
1249     new = HeapAlloc(GetProcessHeap(), 0, sizeof(EffectListItem));
1250     new->next = This->top_effect;
1251     This->top_effect = new;
1252
1253     retval = linuxinput_create_effect(&(This->joyfd), rguid, &(new->ref));
1254     if (retval != DI_OK)
1255         return retval;
1256
1257     if (lpeff != NULL)
1258         retval = IDirectInputEffect_SetParameters(new->ref, lpeff, 0);
1259     if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
1260         return retval;
1261
1262     *ppdef = new->ref;
1263
1264     if (pUnkOuter != NULL)
1265         FIXME("Interface aggregation not implemented.\n");
1266
1267     return DI_OK;
1268
1269 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
1270
1271
1272 /*******************************************************************************
1273  *      EnumEffects - Enumerate available FF effects
1274  */
1275 static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
1276                                                 LPDIENUMEFFECTSCALLBACKA lpCallback,
1277                                                 LPVOID pvRef,
1278                                                 DWORD dwEffType)
1279 {
1280 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1281     DIEFFECTINFOA dei; /* feif */
1282     DWORD type = DIEFT_GETTYPE(dwEffType);
1283     JoystickImpl* This = (JoystickImpl*)iface;
1284
1285     TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
1286
1287     dei.dwSize = sizeof(DIEFFECTINFOA);          
1288
1289     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1290         && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1291         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1292         (*lpCallback)(&dei, pvRef);
1293     }
1294
1295     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1296         && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1297         if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1298             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1299             (*lpCallback)(&dei, pvRef);
1300         }
1301         if (test_bit(This->joydev->ffbits, FF_SINE)) {
1302             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1303             (*lpCallback)(&dei, pvRef);
1304         }
1305         if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1306             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1307             (*lpCallback)(&dei, pvRef);
1308         }
1309         if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1310             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1311             (*lpCallback)(&dei, pvRef);
1312         }
1313         if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1314             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1315             (*lpCallback)(&dei, pvRef);
1316         }
1317     } 
1318
1319     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1320         && test_bit(This->joydev->ffbits, FF_RAMP)) {
1321         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1322         (*lpCallback)(&dei, pvRef);
1323     }
1324
1325     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1326         if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1327             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1328             (*lpCallback)(&dei, pvRef);
1329         }
1330         if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1331             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1332             (*lpCallback)(&dei, pvRef);
1333         }
1334         if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1335             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1336             (*lpCallback)(&dei, pvRef);
1337         }
1338         if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1339             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1340             (*lpCallback)(&dei, pvRef);
1341         }
1342     }
1343
1344 #endif
1345
1346     return DI_OK;
1347 }
1348
1349 static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
1350                                                 LPDIENUMEFFECTSCALLBACKW lpCallback,
1351                                                 LPVOID pvRef,
1352                                                 DWORD dwEffType)
1353 {
1354 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1355     /* seems silly to duplicate all this code but all the structures and functions
1356      * are actually different (A/W) */
1357     DIEFFECTINFOW dei; /* feif */
1358     DWORD type = DIEFT_GETTYPE(dwEffType);
1359     JoystickImpl* This = (JoystickImpl*)iface;
1360     int xfd = This->joyfd;
1361
1362     TRACE("(this=%p,%p,%d) type=%d fd=%d\n", This, pvRef, dwEffType, type, xfd);
1363
1364     dei.dwSize = sizeof(DIEFFECTINFOW);          
1365
1366     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1367         && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1368         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1369         (*lpCallback)(&dei, pvRef);
1370     }
1371
1372     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1373         && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1374         if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1375             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1376             (*lpCallback)(&dei, pvRef);
1377         }
1378         if (test_bit(This->joydev->ffbits, FF_SINE)) {
1379             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1380             (*lpCallback)(&dei, pvRef);
1381         }
1382         if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1383             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1384             (*lpCallback)(&dei, pvRef);
1385         }
1386         if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1387             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1388             (*lpCallback)(&dei, pvRef);
1389         }
1390         if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1391             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1392             (*lpCallback)(&dei, pvRef);
1393         }
1394     } 
1395
1396     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1397         && test_bit(This->joydev->ffbits, FF_RAMP)) {
1398         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1399         (*lpCallback)(&dei, pvRef);
1400     }
1401
1402     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1403         if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1404             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1405             (*lpCallback)(&dei, pvRef);
1406         }
1407         if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1408             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1409             (*lpCallback)(&dei, pvRef);
1410         }
1411         if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1412             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1413             (*lpCallback)(&dei, pvRef);
1414         }
1415         if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1416             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1417             (*lpCallback)(&dei, pvRef);
1418         }
1419     }
1420
1421     /* return to unacquired state if that's where it was */
1422     if (xfd == -1)
1423         IDirectInputDevice8_Unacquire(iface);
1424 #endif
1425
1426     return DI_OK;
1427 }
1428
1429 /*******************************************************************************
1430  *      GetEffectInfo - Get information about a particular effect 
1431  */
1432 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
1433                                                   LPDIEFFECTINFOA pdei,
1434                                                   REFGUID guid)
1435 {
1436     JoystickImpl* This = (JoystickImpl*)iface;
1437
1438     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1439
1440 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1441     return linuxinput_get_info_A(This->joyfd, guid, pdei); 
1442 #else
1443     return DI_OK;
1444 #endif
1445 }
1446
1447 static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
1448                                                   LPDIEFFECTINFOW pdei,
1449                                                   REFGUID guid)
1450 {
1451     JoystickImpl* This = (JoystickImpl*)iface;
1452             
1453     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1454         
1455 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1456     return linuxinput_get_info_W(This->joyfd, guid, pdei);
1457 #else
1458     return DI_OK;
1459 #endif
1460 }
1461
1462 /*******************************************************************************
1463  *      GetForceFeedbackState - Get information about the device's FF state 
1464  */
1465 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(
1466         LPDIRECTINPUTDEVICE8A iface,
1467         LPDWORD pdwOut)
1468 {
1469     JoystickImpl* This = (JoystickImpl*)iface;
1470
1471     TRACE("(this=%p,%p)\n", This, pdwOut);
1472
1473     (*pdwOut) = 0;
1474
1475 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1476     /* DIGFFS_STOPPED is the only mandatory flag to report */
1477     if (This->ff_state == FF_STATUS_STOPPED)
1478         (*pdwOut) |= DIGFFS_STOPPED;
1479 #endif
1480
1481     return DI_OK;
1482 }
1483
1484 /*******************************************************************************
1485  *      SendForceFeedbackCommand - Send a command to the device's FF system
1486  */
1487 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(
1488         LPDIRECTINPUTDEVICE8A iface,
1489         DWORD dwFlags)
1490 {
1491     JoystickImpl* This = (JoystickImpl*)iface;
1492     TRACE("(this=%p,%d)\n", This, dwFlags);
1493
1494 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1495     if (dwFlags == DISFFC_STOPALL) {
1496         /* Stop all effects */
1497         EffectListItem* itr = This->top_effect;
1498         while (itr) {
1499             IDirectInputEffect_Stop(itr->ref);
1500             itr = itr->next;
1501         }
1502     } else if (dwFlags == DISFFC_RESET) {
1503         /* Stop, unload, release and free all effects */
1504         /* This returns the device to its "bare" state */
1505         while (This->top_effect) {
1506             EffectListItem* temp = This->top_effect;
1507             IDirectInputEffect_Stop(temp->ref);
1508             IDirectInputEffect_Unload(temp->ref);
1509             IDirectInputEffect_Release(temp->ref);
1510             This->top_effect = temp->next;
1511             HeapFree(GetProcessHeap(), 0, temp);
1512         }
1513     } else if (dwFlags == DISFFC_PAUSE || dwFlags == DISFFC_CONTINUE) {
1514         FIXME("No support for Pause or Continue in linux\n");
1515     } else if (dwFlags == DISFFC_SETACTUATORSOFF 
1516                 || dwFlags == DISFFC_SETACTUATORSON) {
1517         FIXME("No direct actuator control in linux\n");
1518     } else {
1519         FIXME("Unknown Force Feedback Command!\n");
1520         return DIERR_INVALIDPARAM;
1521     }
1522     return DI_OK;
1523 #else
1524     return DIERR_UNSUPPORTED;
1525 #endif
1526 }
1527
1528 /*******************************************************************************
1529  *      EnumCreatedEffectObjects - Enumerate all the effects that have been
1530  *              created for this device.
1531  */
1532 static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(
1533         LPDIRECTINPUTDEVICE8A iface,
1534         LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1535         LPVOID pvRef,
1536         DWORD dwFlags)
1537 {
1538     /* this function is safe to call on non-ff-enabled builds */
1539
1540     JoystickImpl* This = (JoystickImpl*)iface;
1541     EffectListItem* itr = This->top_effect;
1542     TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags);
1543
1544     if (!lpCallback)
1545         return DIERR_INVALIDPARAM;
1546
1547     if (dwFlags != 0)
1548         FIXME("Flags specified, but no flags exist yet (DX9)!\n");
1549
1550     while (itr) {
1551         (*lpCallback)(itr->ref, pvRef);
1552         itr = itr->next;
1553     }
1554
1555     return DI_OK;
1556 }
1557
1558 static const IDirectInputDevice8AVtbl JoystickAvt =
1559 {
1560         IDirectInputDevice2AImpl_QueryInterface,
1561         IDirectInputDevice2AImpl_AddRef,
1562         JoystickAImpl_Release,
1563         JoystickAImpl_GetCapabilities,
1564         JoystickAImpl_EnumObjects,
1565         JoystickAImpl_GetProperty,
1566         JoystickAImpl_SetProperty,
1567         JoystickAImpl_Acquire,
1568         JoystickAImpl_Unacquire,
1569         JoystickAImpl_GetDeviceState,
1570         IDirectInputDevice2AImpl_GetDeviceData,
1571         JoystickAImpl_SetDataFormat,
1572         IDirectInputDevice2AImpl_SetEventNotification,
1573         IDirectInputDevice2AImpl_SetCooperativeLevel,
1574         IDirectInputDevice2AImpl_GetObjectInfo,
1575         IDirectInputDevice2AImpl_GetDeviceInfo,
1576         IDirectInputDevice2AImpl_RunControlPanel,
1577         IDirectInputDevice2AImpl_Initialize,
1578         JoystickAImpl_CreateEffect,
1579         JoystickAImpl_EnumEffects,
1580         JoystickAImpl_GetEffectInfo,
1581         JoystickAImpl_GetForceFeedbackState,
1582         JoystickAImpl_SendForceFeedbackCommand,
1583         JoystickAImpl_EnumCreatedEffectObjects,
1584         IDirectInputDevice2AImpl_Escape,
1585         JoystickAImpl_Poll,
1586         IDirectInputDevice2AImpl_SendDeviceData,
1587         IDirectInputDevice7AImpl_EnumEffectsInFile,
1588         IDirectInputDevice7AImpl_WriteEffectToFile,
1589         IDirectInputDevice8AImpl_BuildActionMap,
1590         IDirectInputDevice8AImpl_SetActionMap,
1591         IDirectInputDevice8AImpl_GetImageInfo
1592 };
1593
1594 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1595 # define XCAST(fun)     (typeof(JoystickWvt.fun))
1596 #else
1597 # define XCAST(fun)     (void*)
1598 #endif
1599
1600 static const IDirectInputDevice8WVtbl JoystickWvt =
1601 {
1602         IDirectInputDevice2WImpl_QueryInterface,
1603         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1604         XCAST(Release)JoystickAImpl_Release,
1605         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1606         JoystickWImpl_EnumObjects,
1607         XCAST(GetProperty)JoystickAImpl_GetProperty,
1608         XCAST(SetProperty)JoystickAImpl_SetProperty,
1609         XCAST(Acquire)JoystickAImpl_Acquire,
1610         XCAST(Unacquire)JoystickAImpl_Unacquire,
1611         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1612         XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
1613         XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1614         XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
1615         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1616         IDirectInputDevice2WImpl_GetObjectInfo,
1617         IDirectInputDevice2WImpl_GetDeviceInfo,
1618         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1619         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1620         XCAST(CreateEffect)JoystickAImpl_CreateEffect,
1621         JoystickWImpl_EnumEffects,
1622         JoystickWImpl_GetEffectInfo,
1623         XCAST(GetForceFeedbackState)JoystickAImpl_GetForceFeedbackState,
1624         XCAST(SendForceFeedbackCommand)JoystickAImpl_SendForceFeedbackCommand,
1625         XCAST(EnumCreatedEffectObjects)JoystickAImpl_EnumCreatedEffectObjects,
1626         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1627         XCAST(Poll)JoystickAImpl_Poll,
1628         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1629         IDirectInputDevice7WImpl_EnumEffectsInFile,
1630         IDirectInputDevice7WImpl_WriteEffectToFile,
1631         IDirectInputDevice8WImpl_BuildActionMap,
1632         IDirectInputDevice8WImpl_SetActionMap,
1633         IDirectInputDevice8WImpl_GetImageInfo
1634 };
1635 #undef XCAST
1636
1637 #else  /* HAVE_CORRECT_LINUXINPUT_H */
1638
1639 const struct dinput_device joystick_linuxinput_device = {
1640   "Wine Linux-input joystick driver",
1641   NULL,
1642   NULL,
1643   NULL,
1644   NULL
1645 };
1646
1647 #endif  /* HAVE_CORRECT_LINUXINPUT_H */