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