msi/tests: automation: Session::Installer conformance test.
[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 # undef SW_MAX
48 # if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
49 #  define HAVE_CORRECT_LINUXINPUT_H
50 # endif
51 #endif
52 #ifdef HAVE_SYS_POLL_H
53 # include <sys/poll.h>
54 #endif
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 #ifdef HAVE_CORRECT_LINUXINPUT_H
69
70 #define EVDEVPREFIX     "/dev/input/event"
71
72 /* Wine joystick driver object instances */
73 #define WINE_JOYSTICK_MAX_AXES    8
74 #define WINE_JOYSTICK_MAX_POVS    4
75 #define WINE_JOYSTICK_MAX_BUTTONS 128
76
77 typedef struct EffectListItem EffectListItem;
78 struct EffectListItem
79 {
80         LPDIRECTINPUTEFFECT ref;
81         struct EffectListItem* next;
82 };
83
84 /* implemented in effect_linuxinput.c */
85 HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, LPDIRECTINPUTEFFECT* peff);
86 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
87 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
88
89 typedef struct JoystickImpl JoystickImpl;
90 static const IDirectInputDevice8AVtbl JoystickAvt;
91 static const IDirectInputDevice8WVtbl JoystickWvt;
92
93 struct JoyDev {
94         char *device;
95         char *name;
96         GUID guid;
97
98         int has_ff;
99         int num_effects;
100
101         /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
102         BYTE                            evbits[(EV_MAX+7)/8];
103         BYTE                            absbits[(ABS_MAX+7)/8];
104         BYTE                            keybits[(KEY_MAX+7)/8];
105         BYTE                            ffbits[(FF_MAX+7)/8];   
106
107 #define AXIS_ABS     0
108 #define AXIS_ABSMIN  1
109 #define AXIS_ABSMAX  2
110 #define AXIS_ABSFUZZ 3
111 #define AXIS_ABSFLAT 4
112
113         /* data returned by the EVIOCGABS() ioctl */
114         int                             axes[ABS_MAX][5];
115 };
116
117 struct ObjProps
118 {
119         /* what we have */
120         LONG                            havemax;
121         LONG                            havemin;
122         /* what range and deadzone the game wants */
123         LONG                            wantmin;
124         LONG                            wantmax;
125         LONG                            deadzone;
126 };
127
128 struct JoystickImpl
129 {
130         struct IDirectInputDevice2AImpl base;
131
132         struct JoyDev                  *joydev;
133
134         /* The 'parent' DInput */
135         IDirectInputImpl               *dinput;
136
137         /* joystick private */
138         int                             joyfd;
139
140         DIJOYSTATE2                     js;
141
142         struct ObjProps                 props[ABS_MAX];
143
144         int                             axes[ABS_MAX];
145
146         /* LUT for KEY_ to offset in rgbButtons */
147         BYTE                            buttons[KEY_MAX];
148
149         DWORD                           numAxes;
150         DWORD                           numPOVs;
151         DWORD                           numButtons;
152
153         /* Force feedback variables */
154         EffectListItem*                 top_effect;
155         int                             ff_state;
156 };
157
158 static void fake_current_js_state(JoystickImpl *ji);
159 static DWORD map_pov(int event_value, int is_x);
160 static void find_joydevs(void);
161
162 /* This GUID is slightly different from the linux joystick one. Take note. */
163 static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
164   0x9e573eda,
165   0x7734,
166   0x11d2,
167   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
168 };
169
170 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
171
172 #define MAX_JOYDEV 64
173
174 static int have_joydevs = -1;
175 static struct JoyDev *joydevs = NULL;
176
177 static void find_joydevs(void)
178 {
179   int i;
180
181   if (have_joydevs!=-1) {
182     return;
183   }
184
185   have_joydevs = 0;
186
187   for (i=0;i<MAX_JOYDEV;i++) {
188     char        buf[MAX_PATH];
189     struct JoyDev joydev = {0};
190     int fd;
191     int no_ff_check = 0;
192     int j;
193
194     snprintf(buf,MAX_PATH,EVDEVPREFIX"%d",i);
195     buf[MAX_PATH-1] = 0;
196
197     if ((fd=open(buf, O_RDWR))==-1) {
198       fd = open(buf, O_RDONLY);
199       no_ff_check = 1;
200     }
201
202     if (fd!=-1) {
203       if ((-1==ioctl(fd,EVIOCGBIT(0,sizeof(joydev.evbits)),joydev.evbits))) {
204         perror("EVIOCGBIT 0");
205         close(fd);
206         continue; 
207       }
208       if (-1==ioctl(fd,EVIOCGBIT(EV_ABS,sizeof(joydev.absbits)),joydev.absbits)) {
209         perror("EVIOCGBIT EV_ABS");
210         close(fd);
211         continue;
212       }
213       if (-1==ioctl(fd,EVIOCGBIT(EV_KEY,sizeof(joydev.keybits)),joydev.keybits)) {
214         perror("EVIOCGBIT EV_KEY");
215         close(fd);
216         continue;
217       }
218       /* A true joystick has at least axis X and Y, and at least 1
219        * button. copied from linux/drivers/input/joydev.c */
220       if (test_bit(joydev.absbits,ABS_X) && test_bit(joydev.absbits,ABS_Y) &&
221           (   test_bit(joydev.keybits,BTN_TRIGGER)      ||
222               test_bit(joydev.keybits,BTN_A)    ||
223               test_bit(joydev.keybits,BTN_1)
224           )
225          ) {
226
227         joydev.device = strdup(buf);
228
229         if (-1!=ioctl(fd, EVIOCGNAME(MAX_PATH-1), buf)) {
230           buf[MAX_PATH-1] = 0;
231           joydev.name = strdup(buf);
232         } else {
233           joydev.name = joydev.device;
234         }
235
236         joydev.guid = DInput_Wine_Joystick_Base_GUID;
237         joydev.guid.Data3 += have_joydevs;
238
239         TRACE("Found a joystick on %s: %s (%s)\n", 
240             joydev.device, joydev.name, 
241             debugstr_guid(&joydev.guid)
242             );
243
244 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
245         if (!no_ff_check &&
246             test_bit(joydev.evbits, EV_FF) &&
247             ioctl(fd, EVIOCGBIT(EV_FF, sizeof(joydev.ffbits)), joydev.ffbits) != -1 &&
248             ioctl(fd, EVIOCGEFFECTS, &joydev.num_effects) != -1 &&
249             joydev.num_effects > 0)
250         {
251             TRACE(" ... with force feedback\n");
252             joydev.has_ff = 1;
253         }
254 #endif
255
256         for (j=0;j<ABS_MAX;j++) {
257           if (test_bit(joydev.absbits,j)) {
258             if (-1!=ioctl(fd,EVIOCGABS(j),&(joydev.axes[j]))) {
259               TRACE(" ... with axis %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
260                   j,
261                   joydev.axes[j][AXIS_ABS],
262                   joydev.axes[j][AXIS_ABSMIN],
263                   joydev.axes[j][AXIS_ABSMAX],
264                   joydev.axes[j][AXIS_ABSFUZZ],
265                   joydev.axes[j][AXIS_ABSFLAT]
266                   );
267             }
268           }
269         }
270
271         if (have_joydevs==0) {
272           joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
273         } else {
274           HeapReAlloc(GetProcessHeap(), 0, joydevs, (1+have_joydevs) * sizeof(struct JoyDev));
275         }
276         memcpy(joydevs+have_joydevs, &joydev, sizeof(struct JoyDev));
277         have_joydevs++;
278       }
279
280       close(fd);
281     }
282   }
283 }
284
285 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
286 {
287   find_joydevs();
288
289   if (id >= have_joydevs) {
290     return FALSE;
291   }
292  
293   if (!((dwDevType == 0) ||
294         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
295         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
296     return FALSE;
297
298 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
299   if (dwFlags & DIEDFL_FORCEFEEDBACK)
300     return FALSE;
301 #endif
302
303   if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
304     lpddi->guidInstance = joydevs[id].guid;
305     lpddi->guidProduct  = DInput_Wine_Joystick_Base_GUID;
306
307     lpddi->guidFFDriver = GUID_NULL;
308     if (version >= 0x0800)
309       lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
310     else
311       lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
312
313     strcpy(lpddi->tszInstanceName, joydevs[id].name);
314     strcpy(lpddi->tszProductName, joydevs[id].device);
315     return TRUE;
316   }
317   return FALSE;
318 }
319
320 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
321 {
322   find_joydevs();
323
324   if (id >= have_joydevs) {
325     return FALSE;
326   }
327
328   if (!((dwDevType == 0) ||
329         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version < 0x0800)) ||
330         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
331     return FALSE;
332
333 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
334   if (dwFlags & DIEDFL_FORCEFEEDBACK)
335     return FALSE;
336 #endif
337
338   if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
339     lpddi->guidInstance = joydevs[id].guid;
340     lpddi->guidProduct  = DInput_Wine_Joystick_Base_GUID;
341
342     lpddi->guidFFDriver = GUID_NULL;
343     if (version >= 0x0800)
344       lpddi->dwDevType    = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
345     else
346       lpddi->dwDevType    = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
347
348     MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
349     MultiByteToWideChar(CP_ACP, 0, joydevs[id].device, -1, lpddi->tszProductName, MAX_PATH);
350     return TRUE;
351   }
352   return FALSE;
353 }
354
355 static JoystickImpl *alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, struct JoyDev *joydev)
356 {
357     JoystickImpl* newDevice;
358     LPDIDATAFORMAT df = NULL;
359     int i, idx = 0;
360
361     newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
362     if (!newDevice) return NULL;
363
364   newDevice->base.lpVtbl = jvt;
365   newDevice->base.ref = 1;
366   memcpy(&newDevice->base.guid, rguid, sizeof(*rguid));
367   InitializeCriticalSection(&newDevice->base.crit);
368   newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
369   newDevice->joyfd = -1;
370   newDevice->dinput = dinput;
371   newDevice->joydev = joydev;
372 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
373   newDevice->ff_state = FF_STATUS_STOPPED;
374 #endif
375
376     /* Create copy of default data format */
377     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto failed;
378     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
379     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
380
381     /* Supported Axis & POVs should map 1-to-1 */
382     for (i = 0; i < WINE_JOYSTICK_MAX_AXES; i++)
383     {
384         if (!test_bit(newDevice->joydev->absbits, i)) {
385             newDevice->axes[i] = -1;
386             continue;
387         }
388
389         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i], df->dwObjSize);
390         newDevice->axes[i] = idx;
391         newDevice->props[idx].havemin = newDevice->joydev->axes[i][AXIS_ABSMIN];
392         newDevice->props[idx].havemax = newDevice->joydev->axes[i][AXIS_ABSMAX];
393         newDevice->props[idx].wantmin = 0;
394         newDevice->props[idx].wantmax = 0xffff;
395         newDevice->props[idx].deadzone = 0;
396         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->numAxes++) | DIDFT_ABSAXIS;
397     }
398
399     for (i = 0; i < WINE_JOYSTICK_MAX_POVS; i++)
400     {
401         if (!test_bit(newDevice->joydev->absbits, ABS_HAT0X + i * 2) ||
402             !test_bit(newDevice->joydev->absbits, ABS_HAT0Y + i * 2)) {
403             newDevice->axes[ABS_HAT0X + i * 2] = newDevice->axes[ABS_HAT0Y + i * 2] = -1;
404             continue;
405         }
406
407         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + WINE_JOYSTICK_MAX_AXES], df->dwObjSize);
408         newDevice->axes[ABS_HAT0X + i * 2] = newDevice->axes[ABS_HAT0Y + i * 2] = i;
409         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->numPOVs++) | DIDFT_POV;
410     }
411
412     /* Buttons can be anywhere, so check all */
413     for (i = 0; i < KEY_MAX && newDevice->numButtons < WINE_JOYSTICK_MAX_BUTTONS; i++)
414     {
415         if (!test_bit(newDevice->joydev->keybits, i)) continue;
416
417         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[newDevice->numButtons + WINE_JOYSTICK_MAX_AXES + WINE_JOYSTICK_MAX_POVS], df->dwObjSize);
418         newDevice->buttons[i] = 0x80 | newDevice->numButtons;
419         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->numButtons++) | DIDFT_PSHBUTTON;
420     }
421     df->dwNumObjs = idx;
422
423     fake_current_js_state(newDevice);
424
425     newDevice->base.data_format.wine_df = df;
426     IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->dinput);
427     return newDevice;
428
429 failed:
430     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
431     HeapFree(GetProcessHeap(), 0, df);
432     HeapFree(GetProcessHeap(), 0, newDevice);
433     return NULL;
434 }
435
436 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
437 {
438   int i;
439
440   find_joydevs();
441
442   for (i=0; i<have_joydevs; i++) {
443     if (IsEqualGUID(&GUID_Joystick,rguid) ||
444         IsEqualGUID(&joydevs[i].guid,rguid)
445         ) {
446       if ((riid == NULL) ||
447           IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
448           IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
449           IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
450           IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
451         *pdev = (IDirectInputDeviceA*) alloc_device(rguid, &JoystickAvt, dinput, &joydevs[i]);
452         TRACE("Creating a Joystick device (%p)\n", *pdev);
453         if (*pdev==0) {
454           ERR("out of memory\n");
455           return DIERR_OUTOFMEMORY;
456         }
457         return DI_OK;
458       } else {
459         return DIERR_NOINTERFACE;
460       }
461     }
462   }
463
464   return DIERR_DEVICENOTREG;
465 }
466
467
468 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
469 {
470   int i;
471
472   find_joydevs();
473
474   for (i=0; i<have_joydevs; i++) {
475     if (IsEqualGUID(&GUID_Joystick,rguid) ||
476         IsEqualGUID(&joydevs[i].guid,rguid)
477         ) {
478       if ((riid == NULL) ||
479           IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
480           IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
481           IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
482           IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
483         *pdev = (IDirectInputDeviceW*) alloc_device(rguid, &JoystickWvt, dinput, &joydevs[i]);
484         TRACE("Creating a Joystick device (%p)\n", *pdev);
485         if (*pdev==0) {
486           ERR("out of memory\n");
487           return DIERR_OUTOFMEMORY;
488         }
489         return DI_OK;
490       } else {
491         return DIERR_NOINTERFACE;
492       }
493     }
494   }
495
496   return DIERR_DEVICENOTREG;
497 }
498
499 const struct dinput_device joystick_linuxinput_device = {
500   "Wine Linux-input joystick driver",
501   joydev_enum_deviceA,
502   joydev_enum_deviceW,
503   joydev_create_deviceA,
504   joydev_create_deviceW
505 };
506
507 /******************************************************************************
508  *      Joystick
509  */
510 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
511 {
512         JoystickImpl *This = (JoystickImpl *)iface;
513         ULONG ref;
514
515         ref = InterlockedDecrement(&This->base.ref);
516         if (ref)
517                 return ref;
518
519         /* Reset the FF state, free all effects, etc */
520         IDirectInputDevice8_SendForceFeedbackCommand(iface, DISFFC_RESET);
521
522         /* Free the data queue */
523         HeapFree(GetProcessHeap(), 0, This->base.data_queue);
524
525         /* release the data transform filter */
526         HeapFree(GetProcessHeap(), 0, This->base.data_format.wine_df->rgodf);
527         HeapFree(GetProcessHeap(), 0, This->base.data_format.wine_df);
528         release_DataFormat(&This->base.data_format);
529
530         IDirectInput_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
531         This->base.crit.DebugInfo->Spare[0] = 0;
532         DeleteCriticalSection(&This->base.crit);
533         
534         HeapFree(GetProcessHeap(),0,This);
535         return 0;
536 }
537
538 /******************************************************************************
539   *     Acquire : gets exclusive control of the joystick
540   */
541 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
542 {
543     JoystickImpl *This = (JoystickImpl *)iface;
544     HRESULT res;
545
546     TRACE("(this=%p)\n",This);
547
548     res = IDirectInputDevice2AImpl_Acquire(iface);
549     if (res==DI_OK) {
550       if (-1==(This->joyfd=open(This->joydev->device,O_RDWR))) {
551         if (-1==(This->joyfd=open(This->joydev->device,O_RDONLY))) {
552           /* Couldn't open the device at all */
553           perror(This->joydev->device);
554           IDirectInputDevice2AImpl_Unacquire(iface);
555           return DIERR_NOTFOUND;
556         } else {
557           /* Couldn't open in r/w but opened in read-only. */
558           WARN("Could not open %s in read-write mode.  Force feedback will be disabled.\n", This->joydev->device);
559         }
560       }
561     }
562
563     return res;
564 }
565
566 /******************************************************************************
567   *     Unacquire : frees the joystick
568   */
569 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
570 {
571     JoystickImpl *This = (JoystickImpl *)iface;
572     HRESULT res;
573
574     TRACE("(this=%p)\n",This);
575     res = IDirectInputDevice2AImpl_Unacquire(iface);
576     if (res==DI_OK && This->joyfd!=-1) {
577       close(This->joyfd);
578       This->joyfd = -1;
579     }
580     return res;
581 }
582
583 /*
584  * This maps the read value (from the input event) to a value in the
585  * 'wanted' range. It also autodetects the possible range of the axis and
586  * adapts values accordingly.
587  */
588 static int
589 map_axis(JoystickImpl* This, int axis, int val) {
590     int hmax = This->props[axis].havemax;
591     int hmin = This->props[axis].havemin;
592     int wmin = This->props[axis].wantmin;
593     int wmax = This->props[axis].wantmax;
594     int ret;
595
596     /* map the value from the hmin-hmax range into the wmin-wmax range */
597     ret = MulDiv( val - hmin, wmax - wmin, hmax - hmin ) + wmin;
598
599     TRACE("hmin=%d hmax=%d wmin=%d wmax=%d val=%d ret=%d\n", hmin, hmax, wmin, wmax, val, ret);
600
601 #if 0
602     /* deadzone doesn't work comfortably enough right now. needs more testing*/
603     if ((ret > -deadz/2 ) && (ret < deadz/2)) {
604         FIXME("%d in deadzone, return mid.\n",val);
605         return (wmax-wmin)/2+wmin;
606     }
607 #endif
608     return ret;
609 }
610
611 /* 
612  * set the current state of the js device as it would be with the middle
613  * values on the axes
614  */
615 static void fake_current_js_state(JoystickImpl *ji)
616 {
617         int i;
618         /* center the axes */
619         ji->js.lX           = ji->axes[ABS_X]!=-1        ? map_axis(ji, ji->axes[ABS_X],        ji->joydev->axes[ABS_X       ][AXIS_ABS]) : 0;
620         ji->js.lY           = ji->axes[ABS_Y]!=-1        ? map_axis(ji, ji->axes[ABS_Y],        ji->joydev->axes[ABS_Y       ][AXIS_ABS]) : 0;
621         ji->js.lZ           = ji->axes[ABS_Z]!=-1        ? map_axis(ji, ji->axes[ABS_Z],        ji->joydev->axes[ABS_Z       ][AXIS_ABS]) : 0;
622         ji->js.lRx          = ji->axes[ABS_RX]!=-1       ? map_axis(ji, ji->axes[ABS_RX],       ji->joydev->axes[ABS_RX      ][AXIS_ABS]) : 0;
623         ji->js.lRy          = ji->axes[ABS_RY]!=-1       ? map_axis(ji, ji->axes[ABS_RY],       ji->joydev->axes[ABS_RY      ][AXIS_ABS]) : 0;
624         ji->js.lRz          = ji->axes[ABS_RZ]!=-1       ? map_axis(ji, ji->axes[ABS_RZ],       ji->joydev->axes[ABS_RZ      ][AXIS_ABS]) : 0;
625         ji->js.rglSlider[0] = ji->axes[ABS_THROTTLE]!=-1 ? map_axis(ji, ji->axes[ABS_THROTTLE], ji->joydev->axes[ABS_THROTTLE][AXIS_ABS]) : 0;
626         ji->js.rglSlider[1] = ji->axes[ABS_RUDDER]!=-1   ? map_axis(ji, ji->axes[ABS_RUDDER],   ji->joydev->axes[ABS_RUDDER  ][AXIS_ABS]) : 0;
627         /* POV center is -1 */
628         for (i=0; i<4; i++) {
629                 ji->js.rgdwPOV[i] = -1;
630         }
631 }
632
633 /*
634  * Maps an event value to a DX "clock" position:
635  *           0
636  * 27000    -1 9000
637  *       18000
638  */
639 static DWORD map_pov(int event_value, int is_x) 
640 {
641         DWORD ret = -1;
642         if (is_x) {
643                 if (event_value<0) {
644                         ret = 27000;
645                 } else if (event_value>0) {
646                         ret = 9000;
647                 }
648         } else {
649                 if (event_value<0) {
650                         ret = 0;
651                 } else if (event_value>0) {
652                         ret = 18000;
653                 }
654         }
655         return ret;
656 }
657
658 /* convert wine format offset to user format object index */
659 static void joy_polldev(JoystickImpl *This)
660 {
661     struct pollfd plfd;
662     struct input_event ie;
663
664     if (This->joyfd==-1)
665         return;
666
667     while (1)
668     {
669         LONG value = 0;
670         int inst_id = -1;
671
672         plfd.fd = This->joyfd;
673         plfd.events = POLLIN;
674
675         if (poll(&plfd,1,0) != 1)
676             return;
677
678         /* we have one event, so we can read */
679         if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
680             return;
681
682         TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
683         switch (ie.type) {
684         case EV_KEY:    /* button */
685         {
686             int btn = This->buttons[ie.code];
687
688             TRACE("(%p) %d -> %d\n", This, ie.code, btn);
689             if (btn & 0x80)
690             {
691                 btn &= 0x7F;
692                 inst_id = DIDFT_MAKEINSTANCE(btn) | DIDFT_PSHBUTTON;
693                 This->js.rgbButtons[btn] = value = ie.value ? 0x80 : 0x00;
694             }
695             break;
696         }
697         case EV_ABS:
698         {
699             int axis = This->axes[ie.code];
700             if (axis==-1) {
701                 break;
702             }
703             inst_id = DIDFT_MAKEINSTANCE(axis) | (ie.code < ABS_HAT0X ? DIDFT_ABSAXIS : DIDFT_POV);
704             value = map_axis(This, axis, ie.value);
705
706             switch (ie.code) {
707             case ABS_X:         This->js.lX  = value; break;
708             case ABS_Y:         This->js.lY  = value; break;
709             case ABS_Z:         This->js.lZ  = value; break;
710             case ABS_RX:        This->js.lRx = value; break;
711             case ABS_RY:        This->js.lRy = value; break;
712             case ABS_RZ:        This->js.lRz = value; break;
713             case ABS_THROTTLE:  This->js.rglSlider[0] = value; break;
714             case ABS_RUDDER:    This->js.rglSlider[1] = value; break;
715             case ABS_HAT0X:
716             case ABS_HAT0Y:
717                 This->js.rgdwPOV[0] = value = map_pov(ie.value, ie.code==ABS_HAT0X);
718                 break;
719             case ABS_HAT1X:
720             case ABS_HAT1Y:
721                 This->js.rgdwPOV[1] = value = map_pov(ie.value, ie.code==ABS_HAT1X);
722                 break;
723             case ABS_HAT2X:
724             case ABS_HAT2Y:
725                 This->js.rgdwPOV[2] = value  = map_pov(ie.value, ie.code==ABS_HAT2X);
726                 break;
727             case ABS_HAT3X:
728             case ABS_HAT3Y:
729                 This->js.rgdwPOV[3] = value  = map_pov(ie.value, ie.code==ABS_HAT3X);
730                 break;
731             default:
732                 FIXME("unhandled joystick axis event (code %d, value %d)\n",ie.code,ie.value);
733                 break;
734             }
735             break;
736         }
737 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
738         case EV_FF_STATUS:
739             This->ff_state = ie.value;
740             break;
741 #endif
742 #ifdef EV_SYN
743         case EV_SYN:
744             /* there is nothing to do */
745             break;
746 #endif
747         default:
748             FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
749             break;
750         }
751         if (inst_id >= 0)
752             queue_event((LPDIRECTINPUTDEVICE8A)This,
753                         id_to_offset(&This->base.data_format, inst_id),
754                         value, ie.time.tv_usec, This->dinput->evsequence++);
755     }
756 }
757
758 /******************************************************************************
759   *     GetDeviceState : returns the "state" of the joystick.
760   *
761   */
762 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
763         LPDIRECTINPUTDEVICE8A iface,DWORD len,LPVOID ptr
764 ) {
765     JoystickImpl *This = (JoystickImpl *)iface;
766
767     TRACE("(this=%p,0x%08x,%p)\n", This, len, ptr);
768
769     if (This->joyfd==-1) {
770         WARN("not acquired\n");
771         return DIERR_NOTACQUIRED;
772     }
773
774     joy_polldev(This);
775
776     /* convert and copy data to user supplied buffer */
777     fill_DataFormat(ptr, &This->js, &This->base.data_format);
778
779     return DI_OK;
780 }
781
782 /******************************************************************************
783   *     SetProperty : change input device properties
784   */
785 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface,
786                                             REFGUID rguid,
787                                             LPCDIPROPHEADER ph)
788 {
789   JoystickImpl *This = (JoystickImpl *)iface;
790
791   if (!ph) {
792     WARN("invalid argument\n");
793     return DIERR_INVALIDPARAM;
794   }
795
796   TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
797   TRACE("ph.dwSize = %d, ph.dwHeaderSize =%d, ph.dwObj = %d, ph.dwHow= %d\n",
798         ph->dwSize, ph->dwHeaderSize, ph->dwObj, ph->dwHow);
799
800   if (!HIWORD(rguid)) {
801     switch (LOWORD(rguid)) {
802     case (DWORD)DIPROP_RANGE: {
803       LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
804
805       if (ph->dwHow == DIPH_DEVICE) {
806         int i;
807         TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
808         for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
809           This->props[i].wantmin = pr->lMin;
810           This->props[i].wantmax = pr->lMax;
811         }
812       } else {
813         int obj = find_property(&This->base.data_format, ph);
814
815         TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
816         if (obj >= 0) {
817           This->props[obj].wantmin = pr->lMin;
818           This->props[obj].wantmax = pr->lMax;
819         }
820       }
821       fake_current_js_state(This);
822       break;
823     }
824     case (DWORD)DIPROP_DEADZONE: {
825       LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
826       if (ph->dwHow == DIPH_DEVICE) {
827         int i;
828         TRACE("deadzone(%d) all\n", pd->dwData);
829         for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
830           This->props[i].deadzone = pd->dwData;
831         }
832       } else {
833         int obj = find_property(&This->base.data_format, ph);
834
835         TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
836         if (obj >= 0) {
837           This->props[obj].deadzone = pd->dwData;
838         }
839       }
840       fake_current_js_state(This);
841       break;
842     }
843     case (DWORD)DIPROP_CALIBRATIONMODE: {
844       LPCDIPROPDWORD    pd = (LPCDIPROPDWORD)ph;
845       FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
846       break;
847     }
848     default:
849       return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
850     }
851   }
852   return DI_OK;
853 }
854
855 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
856         LPDIRECTINPUTDEVICE8A iface,
857         LPDIDEVCAPS lpDIDevCaps)
858 {
859     JoystickImpl *This = (JoystickImpl *)iface;
860
861     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
862
863     if (!lpDIDevCaps) {
864         WARN("invalid pointer\n");
865         return E_POINTER;
866     }
867
868     if (lpDIDevCaps->dwSize != sizeof(DIDEVCAPS)) {
869         WARN("invalid argument\n");
870         return DIERR_INVALIDPARAM;
871     }
872
873     lpDIDevCaps->dwFlags        = DIDC_ATTACHED;
874     if (This->dinput->dwVersion >= 0x0800)
875         lpDIDevCaps->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
876     else
877         lpDIDevCaps->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
878
879     if (This->joydev->has_ff) 
880          lpDIDevCaps->dwFlags |= DIDC_FORCEFEEDBACK;
881
882     lpDIDevCaps->dwAxes = This->numAxes;
883     lpDIDevCaps->dwButtons = This->numButtons;
884     lpDIDevCaps->dwPOVs = This->numPOVs;
885
886     return DI_OK;
887 }
888
889 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface) {
890     JoystickImpl *This = (JoystickImpl *)iface;
891     TRACE("(%p)\n",This);
892
893     if (This->joyfd==-1) {
894       return DIERR_NOTACQUIRED;
895     }
896
897     joy_polldev(This);
898     return DI_OK;
899 }
900
901 /******************************************************************************
902   *     GetProperty : get input device properties
903   */
904 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,
905                                                 REFGUID rguid,
906                                                 LPDIPROPHEADER pdiph)
907 {
908     JoystickImpl *This = (JoystickImpl *)iface;
909
910     TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
911     _dump_DIPROPHEADER(pdiph);
912
913     if (HIWORD(rguid)) return DI_OK;
914
915     switch (LOWORD(rguid)) {
916     case (DWORD) DIPROP_RANGE:
917     {
918         LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
919         int obj = find_property(&This->base.data_format, pdiph);
920
921         if (obj < 0) return DIERR_OBJECTNOTFOUND;
922
923         pr->lMin = This->props[obj].wantmin;
924         pr->lMax = This->props[obj].wantmax;
925         TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
926         break;
927     }
928     case (DWORD) DIPROP_DEADZONE:
929     {
930         LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
931         int obj = find_property(&This->base.data_format, pdiph);
932
933         if (obj < 0) return DIERR_OBJECTNOTFOUND;
934
935         pd->dwData = This->props[obj].deadzone;
936         TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
937         break;
938     }
939
940     default:
941         return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
942     }
943
944     return DI_OK;
945 }
946
947 /******************************************************************************
948   *     GetObjectInfo : get information about a device object such as a button
949   *                     or axis
950   */
951 static HRESULT WINAPI JoystickWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
952         LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
953 {
954     static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0};
955     static const WCHAR povW[] = {'P','O','V',' ','%','d',0};
956     static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
957     HRESULT res;
958
959     res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
960     if (res != DI_OK) return res;
961
962     if      (pdidoi->dwType & DIDFT_AXIS)
963         sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType));
964     else if (pdidoi->dwType & DIDFT_POV)
965         sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType));
966     else if (pdidoi->dwType & DIDFT_BUTTON)
967         sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType));
968
969     _dump_OBJECTINSTANCEW(pdidoi);
970     return res;
971 }
972
973 static HRESULT WINAPI JoystickAImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
974         LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
975 {
976     HRESULT res;
977     DIDEVICEOBJECTINSTANCEW didoiW;
978     DWORD dwSize = pdidoi->dwSize;
979
980     didoiW.dwSize = sizeof(didoiW);
981     res = JoystickWImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W)iface, &didoiW, dwObj, dwHow);
982     if (res != DI_OK) return res;
983
984     memset(pdidoi, 0, pdidoi->dwSize);
985     memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
986     pdidoi->dwSize = dwSize;
987     WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
988                         sizeof(pdidoi->tszName), NULL, NULL);
989
990     return res;
991 }
992
993 /****************************************************************************** 
994   *     CreateEffect - Create a new FF effect with the specified params
995   */
996 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface,
997                                                  REFGUID rguid,
998                                                  LPCDIEFFECT lpeff,
999                                                  LPDIRECTINPUTEFFECT *ppdef,
1000                                                  LPUNKNOWN pUnkOuter)
1001 {
1002 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1003     EffectListItem* new = NULL;
1004     HRESULT retval = DI_OK;
1005 #endif
1006
1007     JoystickImpl* This = (JoystickImpl*)iface;
1008     TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
1009
1010 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
1011     TRACE("not available (compiled w/o ff support)\n");
1012     *ppdef = NULL;
1013     return DI_OK;
1014 #else
1015
1016     new = HeapAlloc(GetProcessHeap(), 0, sizeof(EffectListItem));
1017     new->next = This->top_effect;
1018     This->top_effect = new;
1019
1020     retval = linuxinput_create_effect(&(This->joyfd), rguid, &(new->ref));
1021     if (retval != DI_OK)
1022         return retval;
1023
1024     if (lpeff != NULL)
1025         retval = IDirectInputEffect_SetParameters(new->ref, lpeff, 0);
1026     if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
1027         return retval;
1028
1029     *ppdef = new->ref;
1030
1031     if (pUnkOuter != NULL)
1032         FIXME("Interface aggregation not implemented.\n");
1033
1034     return DI_OK;
1035
1036 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
1037
1038
1039 /*******************************************************************************
1040  *      EnumEffects - Enumerate available FF effects
1041  */
1042 static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
1043                                                 LPDIENUMEFFECTSCALLBACKA lpCallback,
1044                                                 LPVOID pvRef,
1045                                                 DWORD dwEffType)
1046 {
1047 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1048     DIEFFECTINFOA dei; /* feif */
1049     DWORD type = DIEFT_GETTYPE(dwEffType);
1050     JoystickImpl* This = (JoystickImpl*)iface;
1051
1052     TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
1053
1054     dei.dwSize = sizeof(DIEFFECTINFOA);          
1055
1056     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1057         && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1058         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1059         (*lpCallback)(&dei, pvRef);
1060     }
1061
1062     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1063         && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1064         if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1065             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1066             (*lpCallback)(&dei, pvRef);
1067         }
1068         if (test_bit(This->joydev->ffbits, FF_SINE)) {
1069             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1070             (*lpCallback)(&dei, pvRef);
1071         }
1072         if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1073             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1074             (*lpCallback)(&dei, pvRef);
1075         }
1076         if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1077             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1078             (*lpCallback)(&dei, pvRef);
1079         }
1080         if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1081             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1082             (*lpCallback)(&dei, pvRef);
1083         }
1084     } 
1085
1086     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1087         && test_bit(This->joydev->ffbits, FF_RAMP)) {
1088         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1089         (*lpCallback)(&dei, pvRef);
1090     }
1091
1092     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1093         if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1094             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1095             (*lpCallback)(&dei, pvRef);
1096         }
1097         if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1098             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1099             (*lpCallback)(&dei, pvRef);
1100         }
1101         if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1102             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1103             (*lpCallback)(&dei, pvRef);
1104         }
1105         if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1106             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1107             (*lpCallback)(&dei, pvRef);
1108         }
1109     }
1110
1111 #endif
1112
1113     return DI_OK;
1114 }
1115
1116 static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
1117                                                 LPDIENUMEFFECTSCALLBACKW lpCallback,
1118                                                 LPVOID pvRef,
1119                                                 DWORD dwEffType)
1120 {
1121 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1122     /* seems silly to duplicate all this code but all the structures and functions
1123      * are actually different (A/W) */
1124     DIEFFECTINFOW dei; /* feif */
1125     DWORD type = DIEFT_GETTYPE(dwEffType);
1126     JoystickImpl* This = (JoystickImpl*)iface;
1127     int xfd = This->joyfd;
1128
1129     TRACE("(this=%p,%p,%d) type=%d fd=%d\n", This, pvRef, dwEffType, type, xfd);
1130
1131     dei.dwSize = sizeof(DIEFFECTINFOW);          
1132
1133     if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1134         && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1135         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1136         (*lpCallback)(&dei, pvRef);
1137     }
1138
1139     if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1140         && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1141         if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1142             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1143             (*lpCallback)(&dei, pvRef);
1144         }
1145         if (test_bit(This->joydev->ffbits, FF_SINE)) {
1146             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1147             (*lpCallback)(&dei, pvRef);
1148         }
1149         if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1150             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1151             (*lpCallback)(&dei, pvRef);
1152         }
1153         if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1154             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1155             (*lpCallback)(&dei, pvRef);
1156         }
1157         if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1158             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1159             (*lpCallback)(&dei, pvRef);
1160         }
1161     } 
1162
1163     if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1164         && test_bit(This->joydev->ffbits, FF_RAMP)) {
1165         IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1166         (*lpCallback)(&dei, pvRef);
1167     }
1168
1169     if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1170         if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1171             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1172             (*lpCallback)(&dei, pvRef);
1173         }
1174         if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1175             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1176             (*lpCallback)(&dei, pvRef);
1177         }
1178         if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1179             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1180             (*lpCallback)(&dei, pvRef);
1181         }
1182         if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1183             IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1184             (*lpCallback)(&dei, pvRef);
1185         }
1186     }
1187
1188     /* return to unacquired state if that's where it was */
1189     if (xfd == -1)
1190         IDirectInputDevice8_Unacquire(iface);
1191 #endif
1192
1193     return DI_OK;
1194 }
1195
1196 /*******************************************************************************
1197  *      GetEffectInfo - Get information about a particular effect 
1198  */
1199 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
1200                                                   LPDIEFFECTINFOA pdei,
1201                                                   REFGUID guid)
1202 {
1203     JoystickImpl* This = (JoystickImpl*)iface;
1204
1205     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1206
1207 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1208     return linuxinput_get_info_A(This->joyfd, guid, pdei); 
1209 #else
1210     return DI_OK;
1211 #endif
1212 }
1213
1214 static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
1215                                                   LPDIEFFECTINFOW pdei,
1216                                                   REFGUID guid)
1217 {
1218     JoystickImpl* This = (JoystickImpl*)iface;
1219             
1220     TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1221         
1222 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1223     return linuxinput_get_info_W(This->joyfd, guid, pdei);
1224 #else
1225     return DI_OK;
1226 #endif
1227 }
1228
1229 /*******************************************************************************
1230  *      GetForceFeedbackState - Get information about the device's FF state 
1231  */
1232 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(
1233         LPDIRECTINPUTDEVICE8A iface,
1234         LPDWORD pdwOut)
1235 {
1236     JoystickImpl* This = (JoystickImpl*)iface;
1237
1238     TRACE("(this=%p,%p)\n", This, pdwOut);
1239
1240     (*pdwOut) = 0;
1241
1242 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1243     /* DIGFFS_STOPPED is the only mandatory flag to report */
1244     if (This->ff_state == FF_STATUS_STOPPED)
1245         (*pdwOut) |= DIGFFS_STOPPED;
1246 #endif
1247
1248     return DI_OK;
1249 }
1250
1251 /*******************************************************************************
1252  *      SendForceFeedbackCommand - Send a command to the device's FF system
1253  */
1254 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(
1255         LPDIRECTINPUTDEVICE8A iface,
1256         DWORD dwFlags)
1257 {
1258     JoystickImpl* This = (JoystickImpl*)iface;
1259     TRACE("(this=%p,%d)\n", This, dwFlags);
1260
1261 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1262     if (dwFlags == DISFFC_STOPALL) {
1263         /* Stop all effects */
1264         EffectListItem* itr = This->top_effect;
1265         while (itr) {
1266             IDirectInputEffect_Stop(itr->ref);
1267             itr = itr->next;
1268         }
1269     } else if (dwFlags == DISFFC_RESET) {
1270         /* Stop, unload, release and free all effects */
1271         /* This returns the device to its "bare" state */
1272         while (This->top_effect) {
1273             EffectListItem* temp = This->top_effect;
1274             IDirectInputEffect_Stop(temp->ref);
1275             IDirectInputEffect_Unload(temp->ref);
1276             IDirectInputEffect_Release(temp->ref);
1277             This->top_effect = temp->next;
1278             HeapFree(GetProcessHeap(), 0, temp);
1279         }
1280     } else if (dwFlags == DISFFC_PAUSE || dwFlags == DISFFC_CONTINUE) {
1281         FIXME("No support for Pause or Continue in linux\n");
1282     } else if (dwFlags == DISFFC_SETACTUATORSOFF 
1283                 || dwFlags == DISFFC_SETACTUATORSON) {
1284         FIXME("No direct actuator control in linux\n");
1285     } else {
1286         FIXME("Unknown Force Feedback Command!\n");
1287         return DIERR_INVALIDPARAM;
1288     }
1289     return DI_OK;
1290 #else
1291     return DIERR_UNSUPPORTED;
1292 #endif
1293 }
1294
1295 /*******************************************************************************
1296  *      EnumCreatedEffectObjects - Enumerate all the effects that have been
1297  *              created for this device.
1298  */
1299 static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(
1300         LPDIRECTINPUTDEVICE8A iface,
1301         LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1302         LPVOID pvRef,
1303         DWORD dwFlags)
1304 {
1305     /* this function is safe to call on non-ff-enabled builds */
1306
1307     JoystickImpl* This = (JoystickImpl*)iface;
1308     EffectListItem* itr = This->top_effect;
1309     TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags);
1310
1311     if (!lpCallback)
1312         return DIERR_INVALIDPARAM;
1313
1314     if (dwFlags != 0)
1315         FIXME("Flags specified, but no flags exist yet (DX9)!\n");
1316
1317     while (itr) {
1318         (*lpCallback)(itr->ref, pvRef);
1319         itr = itr->next;
1320     }
1321
1322     return DI_OK;
1323 }
1324
1325 static const IDirectInputDevice8AVtbl JoystickAvt =
1326 {
1327         IDirectInputDevice2AImpl_QueryInterface,
1328         IDirectInputDevice2AImpl_AddRef,
1329         JoystickAImpl_Release,
1330         JoystickAImpl_GetCapabilities,
1331         IDirectInputDevice2AImpl_EnumObjects,
1332         JoystickAImpl_GetProperty,
1333         JoystickAImpl_SetProperty,
1334         JoystickAImpl_Acquire,
1335         JoystickAImpl_Unacquire,
1336         JoystickAImpl_GetDeviceState,
1337         IDirectInputDevice2AImpl_GetDeviceData,
1338         IDirectInputDevice2AImpl_SetDataFormat,
1339         IDirectInputDevice2AImpl_SetEventNotification,
1340         IDirectInputDevice2AImpl_SetCooperativeLevel,
1341         JoystickAImpl_GetObjectInfo,
1342         IDirectInputDevice2AImpl_GetDeviceInfo,
1343         IDirectInputDevice2AImpl_RunControlPanel,
1344         IDirectInputDevice2AImpl_Initialize,
1345         JoystickAImpl_CreateEffect,
1346         JoystickAImpl_EnumEffects,
1347         JoystickAImpl_GetEffectInfo,
1348         JoystickAImpl_GetForceFeedbackState,
1349         JoystickAImpl_SendForceFeedbackCommand,
1350         JoystickAImpl_EnumCreatedEffectObjects,
1351         IDirectInputDevice2AImpl_Escape,
1352         JoystickAImpl_Poll,
1353         IDirectInputDevice2AImpl_SendDeviceData,
1354         IDirectInputDevice7AImpl_EnumEffectsInFile,
1355         IDirectInputDevice7AImpl_WriteEffectToFile,
1356         IDirectInputDevice8AImpl_BuildActionMap,
1357         IDirectInputDevice8AImpl_SetActionMap,
1358         IDirectInputDevice8AImpl_GetImageInfo
1359 };
1360
1361 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1362 # define XCAST(fun)     (typeof(JoystickWvt.fun))
1363 #else
1364 # define XCAST(fun)     (void*)
1365 #endif
1366
1367 static const IDirectInputDevice8WVtbl JoystickWvt =
1368 {
1369         IDirectInputDevice2WImpl_QueryInterface,
1370         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1371         XCAST(Release)JoystickAImpl_Release,
1372         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1373         IDirectInputDevice2WImpl_EnumObjects,
1374         XCAST(GetProperty)JoystickAImpl_GetProperty,
1375         XCAST(SetProperty)JoystickAImpl_SetProperty,
1376         XCAST(Acquire)JoystickAImpl_Acquire,
1377         XCAST(Unacquire)JoystickAImpl_Unacquire,
1378         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1379         XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
1380         XCAST(SetDataFormat)IDirectInputDevice2AImpl_SetDataFormat,
1381         XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
1382         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1383         JoystickWImpl_GetObjectInfo,
1384         IDirectInputDevice2WImpl_GetDeviceInfo,
1385         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1386         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1387         XCAST(CreateEffect)JoystickAImpl_CreateEffect,
1388         JoystickWImpl_EnumEffects,
1389         JoystickWImpl_GetEffectInfo,
1390         XCAST(GetForceFeedbackState)JoystickAImpl_GetForceFeedbackState,
1391         XCAST(SendForceFeedbackCommand)JoystickAImpl_SendForceFeedbackCommand,
1392         XCAST(EnumCreatedEffectObjects)JoystickAImpl_EnumCreatedEffectObjects,
1393         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1394         XCAST(Poll)JoystickAImpl_Poll,
1395         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1396         IDirectInputDevice7WImpl_EnumEffectsInFile,
1397         IDirectInputDevice7WImpl_WriteEffectToFile,
1398         IDirectInputDevice8WImpl_BuildActionMap,
1399         IDirectInputDevice8WImpl_SetActionMap,
1400         IDirectInputDevice8WImpl_GetImageInfo
1401 };
1402 #undef XCAST
1403
1404 #else  /* HAVE_CORRECT_LINUXINPUT_H */
1405
1406 const struct dinput_device joystick_linuxinput_device = {
1407   "Wine Linux-input joystick driver",
1408   NULL,
1409   NULL,
1410   NULL,
1411   NULL
1412 };
1413
1414 #endif  /* HAVE_CORRECT_LINUXINPUT_H */