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