1 /*		DirectInput Joystick device
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998,1999 Lionel Ulmer
5  * Copyright 2000-2001 TransGaming Technologies Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 /*
23  * To Do:
24  *	dead zone
25  *	force feedback
26  */
27 
28 #include "config.h"
29 #include "wine/port.h"
30 
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
40 #endif
41 #include <fcntl.h>
42 #ifdef HAVE_SYS_IOCTL_H
43 # include <sys/ioctl.h>
44 #endif
45 #include <errno.h>
46 #ifdef HAVE_LINUX_IOCTL_H
47 # include <linux/ioctl.h>
48 #endif
49 #ifdef HAVE_LINUX_JOYSTICK_H
50 # include <linux/joystick.h>
51 # undef SW_MAX
52 #endif
53 #ifdef HAVE_SYS_POLL_H
54 # include <sys/poll.h>
55 #endif
56 
57 #include "wine/debug.h"
58 #include "wine/unicode.h"
59 #include "windef.h"
60 #include "winbase.h"
61 #include "winerror.h"
62 #include "devguid.h"
63 #include "dinput.h"
64 
65 #include "dinput_private.h"
66 #include "device_private.h"
67 #include "joystick_private.h"
68 
69 #ifdef HAVE_LINUX_22_JOYSTICK_API
70 
71 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
72 
73 #define JOYDEV_NEW "/dev/input/js"
74 #define JOYDEV_OLD "/dev/js"
75 #define JOYDEVDRIVER " (js)"
76 
77 struct JoyDev
78 {
79     char device[MAX_PATH];
80     char name[MAX_PATH];
81     GUID guid_product;
82 
83     BYTE axis_count;
84     BYTE button_count;
85     int  *dev_axes_map;
86 
87     WORD vendor_id, product_id, bus_type;
88 
89     BOOL is_joystick;
90 };
91 
92 typedef struct JoystickImpl JoystickImpl;
93 static const IDirectInputDevice8AVtbl JoystickAvt;
94 static const IDirectInputDevice8WVtbl JoystickWvt;
95 struct JoystickImpl
96 {
97         struct JoystickGenericImpl generic;
98 
99         struct JoyDev                  *joydev;
100 
101 	/* joystick private */
102 	int				joyfd;
103         POINTL                          povs[4];
104 };
105 
impl_from_IDirectInputDevice8A(IDirectInputDevice8A * iface)106 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
107 {
108     return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
109            JoystickGenericImpl, base), JoystickImpl, generic);
110 }
impl_from_IDirectInputDevice8W(IDirectInputDevice8W * iface)111 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
112 {
113     return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
114            JoystickGenericImpl, base), JoystickImpl, generic);
115 }
116 
IDirectInputDevice8W_from_impl(JoystickImpl * This)117 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
118 {
119     return &This->generic.base.IDirectInputDevice8W_iface;
120 }
121 
122 static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
123   0x9e573ed9,
124   0x7734,
125   0x11d2,
126   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
127 };
128 
129 #define MAX_JOYSTICKS 64
130 static INT joystick_devices_count = -1;
131 static struct JoyDev *joystick_devices;
132 
133 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface);
134 
135 #define SYS_PATH_FORMAT "/sys/class/input/js%d/device/id/%s"
read_sys_id_variable(int index,const char * property,WORD * value)136 static BOOL read_sys_id_variable(int index, const char *property, WORD *value)
137 {
138     char sys_path[sizeof(SYS_PATH_FORMAT) + 16], id_str[5];
139     int sys_fd;
140     BOOL ret = FALSE;
141 
142     sprintf(sys_path, SYS_PATH_FORMAT, index, property);
143     if ((sys_fd = open(sys_path, O_RDONLY)) != -1)
144     {
145         if (read(sys_fd, id_str, 4) == 4)
146         {
147             id_str[4] = '\0';
148             *value = strtol(id_str, NULL, 16);
149             ret = TRUE;
150         }
151 
152         close(sys_fd);
153     }
154     return ret;
155 }
156 #undef SYS_PATH_FORMAT
157 
find_joystick_devices(void)158 static INT find_joystick_devices(void)
159 {
160     INT i;
161 
162     if (joystick_devices_count != -1) return joystick_devices_count;
163 
164     joystick_devices_count = 0;
165     for (i = 0; i < MAX_JOYSTICKS; i++)
166     {
167         int fd;
168         struct JoyDev joydev, *new_joydevs;
169         BYTE axes_map[ABS_MAX + 1];
170         SHORT btn_map[KEY_MAX - BTN_MISC + 1];
171         BOOL is_stylus = FALSE;
172 
173         snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i);
174         if ((fd = open(joydev.device, O_RDONLY)) == -1)
175         {
176             snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_OLD, i);
177             if ((fd = open(joydev.device, O_RDONLY)) == -1) continue;
178         }
179 
180         strcpy(joydev.name, "Wine Joystick");
181 #if defined(JSIOCGNAME)
182         if (ioctl(fd, JSIOCGNAME(sizeof(joydev.name) - sizeof(JOYDEVDRIVER)), joydev.name) < 0)
183             WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joydev.device, strerror(errno));
184 #endif
185 
186         /* Append driver name */
187         strcat(joydev.name, JOYDEVDRIVER);
188 
189         if (device_disabled_registry(joydev.name)) {
190             close(fd);
191             continue;
192         }
193 
194 #ifdef JSIOCGAXES
195         if (ioctl(fd, JSIOCGAXES, &joydev.axis_count) < 0)
196         {
197             WARN("ioctl(%s,JSIOCGAXES) failed: %s, defaulting to 2\n", joydev.device, strerror(errno));
198             joydev.axis_count = 2;
199         }
200 #else
201         WARN("reading number of joystick axes unsupported in this platform, defaulting to 2\n");
202         joydev.axis_count = 2;
203 #endif
204 #ifdef JSIOCGBUTTONS
205         if (ioctl(fd, JSIOCGBUTTONS, &joydev.button_count) < 0)
206         {
207             WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defaulting to 2\n", joydev.device, strerror(errno));
208             joydev.button_count = 2;
209         }
210 #else
211         WARN("reading number of joystick buttons unsupported in this platform, defaulting to 2\n");
212         joydev.button_count = 2;
213 #endif
214 
215         joydev.is_joystick = FALSE;
216         if (ioctl(fd, JSIOCGBTNMAP, btn_map) < 0)
217         {
218             WARN("ioctl(%s,JSIOCGBTNMAP) failed: %s\n", joydev.device, strerror(errno));
219         }
220         else
221         {
222             INT j;
223             /* in lieu of properly reporting HID usage, detect presence of
224              * "joystick buttons" and report those devices as joysticks instead of
225              * gamepads */
226             for (j = 0; !joydev.is_joystick && j < joydev.button_count; j++)
227             {
228                 switch (btn_map[j])
229                 {
230                 case BTN_TRIGGER:
231                 case BTN_THUMB:
232                 case BTN_THUMB2:
233                 case BTN_TOP:
234                 case BTN_TOP2:
235                 case BTN_PINKIE:
236                 case BTN_BASE:
237                 case BTN_BASE2:
238                 case BTN_BASE3:
239                 case BTN_BASE4:
240                 case BTN_BASE5:
241                 case BTN_BASE6:
242                 case BTN_DEAD:
243                     joydev.is_joystick = TRUE;
244                     break;
245                 case BTN_STYLUS:
246                     is_stylus = TRUE;
247                     break;
248                 default:
249                     break;
250                 }
251             }
252         }
253 
254         if(is_stylus)
255         {
256             TRACE("Stylus detected. Skipping\n");
257             close(fd);
258             continue;
259         }
260 
261         if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0)
262         {
263             WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno));
264             joydev.dev_axes_map = NULL;
265         }
266         else
267             if ((joydev.dev_axes_map = HeapAlloc(GetProcessHeap(), 0, joydev.axis_count * sizeof(int))))
268             {
269                 INT j, found_axes = 0;
270 
271                 /* Remap to DI numbers */
272                 for (j = 0; j < joydev.axis_count; j++)
273                 {
274                     if (axes_map[j] < 8)
275                     {
276                         /* Axis match 1-to-1 */
277                         joydev.dev_axes_map[j] = j;
278                         found_axes++;
279                     }
280                     else if (axes_map[j] <= 10)
281                     {
282                         /* Axes 8 through 10 are Wheel, Gas and Brake,
283                          * remap to 0, 1 and 2
284                          */
285                         joydev.dev_axes_map[j] = axes_map[j] - 8;
286                         found_axes++;
287                     }
288                     else if (axes_map[j] == 16 ||
289                              axes_map[j] == 17)
290                     {
291                         /* POV axis */
292                         joydev.dev_axes_map[j] = 8;
293                         found_axes++;
294                     }
295                     else
296                         joydev.dev_axes_map[j] = -1;
297                 }
298 
299                 /* If no axes were configured but there are axes assume a 1-to-1 (wii controller) */
300                 if (joydev.axis_count && !found_axes)
301                 {
302                     int axes_limit = min(joydev.axis_count, 8); /* generic driver limit */
303 
304                     ERR("Incoherent joystick data, advertised %d axes, detected 0. Assuming 1-to-1.\n",
305                         joydev.axis_count);
306                     for (j = 0; j < axes_limit; j++)
307                         joydev.dev_axes_map[j] = j;
308 
309                     joydev.axis_count = axes_limit;
310                 }
311             }
312 
313         /* Find vendor_id and product_id in sysfs */
314         joydev.vendor_id  = 0;
315         joydev.product_id = 0;
316 
317         read_sys_id_variable(i, "vendor", &joydev.vendor_id);
318         read_sys_id_variable(i, "product", &joydev.product_id);
319         read_sys_id_variable(i, "bustype", &joydev.bus_type);
320 
321         if (joydev.vendor_id == 0 || joydev.product_id == 0)
322         {
323             joydev.guid_product = DInput_Wine_Joystick_GUID;
324         }
325         else
326         {
327             /* Concatenate product_id with vendor_id to mimic Windows behaviour */
328             joydev.guid_product       = DInput_PIDVID_Product_GUID;
329             joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
330         }
331 
332         close(fd);
333 
334         if (!joystick_devices_count)
335             new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
336         else
337             new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joystick_devices,
338                                       (joystick_devices_count + 1) * sizeof(struct JoyDev));
339         if (!new_joydevs) continue;
340 
341         TRACE("Found a joystick on %s: %s\n  with %d axes and %d buttons\n", joydev.device,
342               joydev.name, joydev.axis_count, joydev.button_count);
343 
344         joystick_devices = new_joydevs;
345         joystick_devices[joystick_devices_count++] = joydev;
346     }
347 
348     return joystick_devices_count;
349 }
350 
fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi,DWORD version,int id)351 static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
352 {
353     DWORD dwSize = lpddi->dwSize;
354 
355     TRACE("%d %p\n", dwSize, lpddi);
356     memset(lpddi, 0, dwSize);
357 
358     /* Return joystick */
359     lpddi->dwSize = dwSize;
360     lpddi->guidInstance = DInput_Wine_Joystick_GUID;
361     lpddi->guidInstance.Data3 = id;
362     lpddi->guidProduct = joystick_devices[id].guid_product;
363     lpddi->dwDevType = get_device_type(version, joystick_devices[id].is_joystick);
364 
365     /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */
366     if (joystick_devices[id].bus_type == BUS_USB &&
367         joystick_devices[id].vendor_id && joystick_devices[id].product_id)
368     {
369         lpddi->dwDevType |= DIDEVTYPE_HID;
370         lpddi->wUsagePage = 0x01; /* Desktop */
371         if (joystick_devices[id].is_joystick)
372             lpddi->wUsage = 0x04; /* Joystick */
373         else
374             lpddi->wUsage = 0x05; /* Game Pad */
375     }
376 
377     MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
378     MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszProductName, MAX_PATH);
379     lpddi->guidFFDriver = GUID_NULL;
380 }
381 
fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi,DWORD version,int id)382 static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
383 {
384     DIDEVICEINSTANCEW lpddiW;
385     DWORD dwSize = lpddi->dwSize;
386 
387     lpddiW.dwSize = sizeof(lpddiW);
388     fill_joystick_dideviceinstanceW(&lpddiW, version, id);
389 
390     TRACE("%d %p\n", dwSize, lpddi);
391     memset(lpddi, 0, dwSize);
392 
393     /* Convert W->A */
394     lpddi->dwSize = dwSize;
395     lpddi->guidInstance = lpddiW.guidInstance;
396     lpddi->guidProduct = lpddiW.guidProduct;
397     lpddi->dwDevType = lpddiW.dwDevType;
398     strcpy(lpddi->tszInstanceName, joystick_devices[id].name);
399     strcpy(lpddi->tszProductName,  joystick_devices[id].name);
400     lpddi->guidFFDriver = lpddiW.guidFFDriver;
401     lpddi->wUsagePage = lpddiW.wUsagePage;
402     lpddi->wUsage = lpddiW.wUsage;
403 }
404 
joydev_enum_deviceA(DWORD dwDevType,DWORD dwFlags,LPDIDEVICEINSTANCEA lpddi,DWORD version,int id)405 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
406 {
407     int fd = -1;
408 
409     if (id >= find_joystick_devices()) return E_FAIL;
410 
411     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
412         WARN("force feedback not supported\n");
413         return S_FALSE;
414     }
415 
416     if ((dwDevType == 0) ||
417 	((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
418 	(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
419         /* check whether we have a joystick */
420         if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1)
421         {
422             WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno));
423             return S_FALSE;
424         }
425         fill_joystick_dideviceinstanceA( lpddi, version, id );
426         close(fd);
427         TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name);
428         return S_OK;
429     }
430 
431     return S_FALSE;
432 }
433 
joydev_enum_deviceW(DWORD dwDevType,DWORD dwFlags,LPDIDEVICEINSTANCEW lpddi,DWORD version,int id)434 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
435 {
436     int fd = -1;
437 
438     if (id >= find_joystick_devices()) return E_FAIL;
439 
440     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
441         WARN("force feedback not supported\n");
442         return S_FALSE;
443     }
444 
445     if ((dwDevType == 0) ||
446 	((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
447 	(((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
448         /* check whether we have a joystick */
449         if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1)
450         {
451             WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno));
452             return S_FALSE;
453         }
454         fill_joystick_dideviceinstanceW( lpddi, version, id );
455         close(fd);
456         TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name);
457         return S_OK;
458     }
459 
460     return S_FALSE;
461 }
462 
alloc_device(REFGUID rguid,IDirectInputImpl * dinput,JoystickImpl ** pdev,unsigned short index)463 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput,
464                             JoystickImpl **pdev, unsigned short index)
465 {
466     DWORD i;
467     JoystickImpl* newDevice;
468     HRESULT hr;
469     LPDIDATAFORMAT df = NULL;
470     int idx = 0;
471     DIDEVICEINSTANCEW ddi;
472 
473     TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index);
474 
475     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
476     if (newDevice == 0) {
477         WARN("out of memory\n");
478         *pdev = 0;
479         return DIERR_OUTOFMEMORY;
480     }
481 
482     newDevice->joydev = &joystick_devices[index];
483     newDevice->joyfd = -1;
484     newDevice->generic.guidInstance = DInput_Wine_Joystick_GUID;
485     newDevice->generic.guidInstance.Data3 = index;
486     newDevice->generic.guidProduct = DInput_Wine_Joystick_GUID;
487     newDevice->generic.joy_polldev = joy_polldev;
488     newDevice->generic.name        = newDevice->joydev->name;
489     newDevice->generic.device_axis_count = newDevice->joydev->axis_count;
490     newDevice->generic.devcaps.dwButtons = newDevice->joydev->button_count;
491 
492     if (newDevice->generic.devcaps.dwButtons > 128)
493     {
494         WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
495         newDevice->generic.devcaps.dwButtons = 128;
496     }
497 
498     newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
499     newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
500     newDevice->generic.base.ref = 1;
501     newDevice->generic.base.dinput = dinput;
502     newDevice->generic.base.guid = *rguid;
503     InitializeCriticalSection(&newDevice->generic.base.crit);
504     newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
505 
506     /* setup_dinput_options may change these */
507     newDevice->generic.deadzone = 0;
508 
509     /* do any user specified configuration */
510     hr = setup_dinput_options(&newDevice->generic, newDevice->joydev->dev_axes_map);
511     if (hr != DI_OK)
512         goto FAILED1;
513 
514     /* Create copy of default data format */
515     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
516     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
517 
518     df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
519     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
520 
521     for (i = 0; i < newDevice->generic.device_axis_count; i++)
522     {
523         int wine_obj = newDevice->generic.axis_map[i];
524 
525         if (wine_obj < 0) continue;
526 
527         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
528         if (wine_obj < 8)
529             df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
530         else
531         {
532             df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
533             i++; /* POV takes 2 axes */
534         }
535     }
536     for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
537     {
538         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
539         df->rgodf[idx  ].pguid = &GUID_Button;
540         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
541     }
542     newDevice->generic.base.data_format.wine_df = df;
543 
544     /* initialize default properties */
545     for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
546         newDevice->generic.props[i].lDevMin = -32767;
547         newDevice->generic.props[i].lDevMax = +32767;
548         newDevice->generic.props[i].lMin = 0;
549         newDevice->generic.props[i].lMax = 0xffff;
550         newDevice->generic.props[i].lDeadZone = newDevice->generic.deadzone; /* % * 1000 */
551         newDevice->generic.props[i].lSaturation = 0;
552     }
553 
554     IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
555 
556     EnterCriticalSection(&dinput->crit);
557     list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
558     LeaveCriticalSection(&dinput->crit);
559 
560     newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
561     newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
562 
563     ddi.dwSize = sizeof(ddi);
564     fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index);
565     newDevice->generic.devcaps.dwDevType = ddi.dwDevType;
566 
567     newDevice->generic.devcaps.dwFFSamplePeriod = 0;
568     newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
569     newDevice->generic.devcaps.dwFirmwareRevision = 0;
570     newDevice->generic.devcaps.dwHardwareRevision = 0;
571     newDevice->generic.devcaps.dwFFDriverVersion = 0;
572 
573     if (TRACE_ON(dinput)) {
574         _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
575        for (i = 0; i < (newDevice->generic.device_axis_count); i++)
576            TRACE("axis_map[%d] = %d\n", i, newDevice->generic.axis_map[i]);
577         _dump_DIDEVCAPS(&newDevice->generic.devcaps);
578     }
579 
580     *pdev = newDevice;
581 
582     return DI_OK;
583 
584 FAILED:
585     hr = DIERR_OUTOFMEMORY;
586 FAILED1:
587     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
588     HeapFree(GetProcessHeap(), 0, df);
589     release_DataFormat(&newDevice->generic.base.data_format);
590     HeapFree(GetProcessHeap(),0,newDevice->generic.axis_map);
591     HeapFree(GetProcessHeap(),0,newDevice);
592     *pdev = 0;
593 
594     return hr;
595 }
596 
597 /******************************************************************************
598   *     get_joystick_index : Get the joystick index from a given GUID
599   */
get_joystick_index(REFGUID guid)600 static unsigned short get_joystick_index(REFGUID guid)
601 {
602     GUID wine_joystick = DInput_Wine_Joystick_GUID;
603     GUID dev_guid = *guid;
604 
605     wine_joystick.Data3 = 0;
606     dev_guid.Data3 = 0;
607 
608     /* for the standard joystick GUID use index 0 */
609     if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
610 
611     /* for the wine joystick GUIDs use the index stored in Data3 */
612     if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
613 
614     return MAX_JOYSTICKS;
615 }
616 
joydev_create_device(IDirectInputImpl * dinput,REFGUID rguid,REFIID riid,LPVOID * pdev,int unicode)617 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
618 {
619     unsigned short index;
620 
621     TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
622     find_joystick_devices();
623     *pdev = NULL;
624 
625     if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
626         joystick_devices_count && index < joystick_devices_count)
627     {
628         JoystickImpl *This;
629         HRESULT hr;
630 
631         if (riid == NULL)
632             ;/* nothing */
633         else if (IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
634                  IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
635                  IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
636                  IsEqualGUID(&IID_IDirectInputDevice8A, riid))
637         {
638             unicode = 0;
639         }
640         else if (IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
641                  IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
642                  IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
643                  IsEqualGUID(&IID_IDirectInputDevice8W, riid))
644         {
645             unicode = 1;
646         }
647         else
648         {
649             WARN("no interface\n");
650             return DIERR_NOINTERFACE;
651         }
652 
653         hr = alloc_device(rguid, dinput, &This, index);
654         if (!This) return hr;
655 
656         if (unicode)
657             *pdev = &This->generic.base.IDirectInputDevice8W_iface;
658         else
659             *pdev = &This->generic.base.IDirectInputDevice8A_iface;
660 
661         return hr;
662     }
663 
664     return DIERR_DEVICENOTREG;
665 }
666 
667 #undef MAX_JOYSTICKS
668 
669 const struct dinput_device joystick_linux_device = {
670   "Wine Linux joystick driver",
671   joydev_enum_deviceA,
672   joydev_enum_deviceW,
673   joydev_create_device
674 };
675 
676 /******************************************************************************
677   *     Acquire : gets exclusive control of the joystick
678   */
JoystickLinuxWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)679 static HRESULT WINAPI JoystickLinuxWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
680 {
681     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
682     HRESULT res;
683 
684     TRACE("(%p)\n",This);
685 
686     res = IDirectInputDevice2WImpl_Acquire(iface);
687     if (res != DI_OK)
688         return res;
689 
690     /* open the joystick device */
691     if (This->joyfd==-1) {
692         TRACE("opening joystick device %s\n", This->joydev->device);
693 
694         This->joyfd = open(This->joydev->device, O_RDONLY);
695         if (This->joyfd==-1) {
696             ERR("open(%s) failed: %s\n", This->joydev->device, strerror(errno));
697             IDirectInputDevice2WImpl_Unacquire(iface);
698             return DIERR_NOTFOUND;
699         }
700     }
701 
702     return DI_OK;
703 }
704 
JoystickLinuxAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)705 static HRESULT WINAPI JoystickLinuxAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
706 {
707     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
708     return JoystickLinuxWImpl_Acquire(IDirectInputDevice8W_from_impl(This));
709 }
710 
711 /******************************************************************************
712   *     GetProperty : get input device properties
713   */
JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface,REFGUID rguid,LPDIPROPHEADER pdiph)714 static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
715 {
716     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
717 
718     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
719     _dump_DIPROPHEADER(pdiph);
720 
721     if (!IS_DIPROP(rguid)) return DI_OK;
722 
723     switch (LOWORD(rguid)) {
724 
725         case (DWORD_PTR) DIPROP_VIDPID:
726         {
727             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
728 
729             if (!This->joydev->product_id || !This->joydev->vendor_id)
730                 return DIERR_UNSUPPORTED;
731             pd->dwData = MAKELONG(This->joydev->vendor_id, This->joydev->product_id);
732             TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData);
733             break;
734         }
735         case (DWORD_PTR) DIPROP_JOYSTICKID:
736         {
737             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
738 
739             pd->dwData = get_joystick_index(&This->generic.base.guid);
740             TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
741             break;
742         }
743 
744         case (DWORD_PTR) DIPROP_GUIDANDPATH:
745         {
746             static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
747                                             'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0};
748             static const WCHAR miW[] = {'m','i',0};
749             static const WCHAR igW[] = {'i','g',0};
750 
751             BOOL is_gamepad;
752             LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
753             WORD vid = This->joydev->vendor_id;
754             WORD pid = This->joydev->product_id;
755 
756             if (!pid || !vid)
757                 return DIERR_UNSUPPORTED;
758 
759             is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
760             pd->guidClass = GUID_DEVCLASS_HIDCLASS;
761             sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid));
762 
763             TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
764             break;
765         }
766 
767     default:
768         return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
769     }
770 
771     return DI_OK;
772 }
773 
JoystickLinuxAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface,REFGUID rguid,LPDIPROPHEADER pdiph)774 static HRESULT WINAPI JoystickLinuxAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
775 {
776     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
777     return JoystickLinuxWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
778 }
779 
780 /******************************************************************************
781   *     GetDeviceInfo : get information about a device's identity
782   */
JoystickLinuxAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface,LPDIDEVICEINSTANCEA ddi)783 static HRESULT WINAPI JoystickLinuxAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface, LPDIDEVICEINSTANCEA ddi)
784 {
785     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
786 
787     TRACE("(%p) %p\n", This, ddi);
788 
789     if (ddi == NULL) return E_POINTER;
790     if ((ddi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
791         (ddi->dwSize != sizeof(DIDEVICEINSTANCEA)))
792         return DIERR_INVALIDPARAM;
793 
794     fill_joystick_dideviceinstanceA( ddi, This->generic.base.dinput->dwVersion,
795                                      get_joystick_index(&This->generic.base.guid) );
796     return DI_OK;
797 }
798 
JoystickLinuxWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface,LPDIDEVICEINSTANCEW ddi)799 static HRESULT WINAPI JoystickLinuxWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, LPDIDEVICEINSTANCEW ddi)
800 {
801     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
802 
803     TRACE("(%p) %p\n", This, ddi);
804 
805     if (ddi == NULL) return E_POINTER;
806     if ((ddi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
807         (ddi->dwSize != sizeof(DIDEVICEINSTANCEW)))
808         return DIERR_INVALIDPARAM;
809 
810     fill_joystick_dideviceinstanceW( ddi, This->generic.base.dinput->dwVersion,
811                                      get_joystick_index(&This->generic.base.guid) );
812     return DI_OK;
813 }
814 
815 /******************************************************************************
816   *     Unacquire : frees the joystick
817   */
JoystickLinuxWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)818 static HRESULT WINAPI JoystickLinuxWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
819 {
820     JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
821     HRESULT res;
822 
823     TRACE("(%p)\n",This);
824 
825     res = IDirectInputDevice2WImpl_Unacquire(iface);
826 
827     if (res != DI_OK)
828         return res;
829 
830     if (This->joyfd!=-1) {
831         TRACE("closing joystick device\n");
832         close(This->joyfd);
833         This->joyfd = -1;
834         return DI_OK;
835     }
836 
837     return DI_NOEFFECT;
838 }
839 
JoystickLinuxAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)840 static HRESULT WINAPI JoystickLinuxAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
841 {
842     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
843     return JoystickLinuxWImpl_Unacquire(IDirectInputDevice8W_from_impl(This));
844 }
845 
joy_polldev(LPDIRECTINPUTDEVICE8A iface)846 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
847 {
848     struct pollfd plfd;
849     struct js_event jse;
850     JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
851 
852     TRACE("(%p)\n", This);
853 
854     if (This->joyfd==-1) {
855         WARN("no device\n");
856         return;
857     }
858     while (1)
859     {
860         LONG value;
861         int inst_id = -1;
862 
863 	plfd.fd = This->joyfd;
864 	plfd.events = POLLIN;
865 	if (poll(&plfd,1,0) != 1)
866 	    return;
867 	/* we have one event, so we can read */
868 	if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
869 	    return;
870 	}
871         TRACE("js_event: type 0x%x, number %d, value %d\n",
872               jse.type,jse.number,jse.value);
873         if (jse.type & JS_EVENT_BUTTON)
874         {
875             int button;
876             if (jse.number >= This->generic.devcaps.dwButtons) return;
877 
878             button = This->generic.button_map[jse.number];
879 
880             inst_id = DIDFT_MAKEINSTANCE(button) | DIDFT_PSHBUTTON;
881             This->generic.js.rgbButtons[button] = value = jse.value ? 0x80 : 0x00;
882         }
883         else if (jse.type & JS_EVENT_AXIS)
884         {
885             int number = This->generic.axis_map[jse.number];	/* wine format object index */
886 
887             if (number < 0) return;
888             inst_id = number < 8 ?  DIDFT_MAKEINSTANCE(number) | DIDFT_ABSAXIS :
889                                     DIDFT_MAKEINSTANCE(number - 8) | DIDFT_POV;
890             value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], jse.value);
891 
892             TRACE("changing axis %d => %d\n", jse.number, number);
893             switch (number)
894             {
895                 case 0: This->generic.js.lX  = value; break;
896                 case 1: This->generic.js.lY  = value; break;
897                 case 2: This->generic.js.lZ  = value; break;
898                 case 3: This->generic.js.lRx = value; break;
899                 case 4: This->generic.js.lRy = value; break;
900                 case 5: This->generic.js.lRz = value; break;
901                 case 6: This->generic.js.rglSlider[0] = value; break;
902                 case 7: This->generic.js.rglSlider[1] = value; break;
903                 case 8: case 9: case 10: case 11:
904                 {
905                     int idx = number - 8;
906 
907                     if (jse.number % 2)
908                         This->povs[idx].y = jse.value;
909                     else
910                         This->povs[idx].x = jse.value;
911 
912                     This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
913                     break;
914                 }
915                 default:
916                     WARN("axis %d not supported\n", number);
917             }
918         }
919         if (inst_id >= 0)
920             queue_event(iface, inst_id, value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
921     }
922 }
923 
924 static const IDirectInputDevice8AVtbl JoystickAvt =
925 {
926 	IDirectInputDevice2AImpl_QueryInterface,
927 	IDirectInputDevice2AImpl_AddRef,
928         IDirectInputDevice2AImpl_Release,
929 	JoystickAGenericImpl_GetCapabilities,
930         IDirectInputDevice2AImpl_EnumObjects,
931 	JoystickLinuxAImpl_GetProperty,
932 	JoystickAGenericImpl_SetProperty,
933 	JoystickLinuxAImpl_Acquire,
934 	JoystickLinuxAImpl_Unacquire,
935 	JoystickAGenericImpl_GetDeviceState,
936 	IDirectInputDevice2AImpl_GetDeviceData,
937 	IDirectInputDevice2AImpl_SetDataFormat,
938 	IDirectInputDevice2AImpl_SetEventNotification,
939 	IDirectInputDevice2AImpl_SetCooperativeLevel,
940 	JoystickAGenericImpl_GetObjectInfo,
941 	JoystickLinuxAImpl_GetDeviceInfo,
942 	IDirectInputDevice2AImpl_RunControlPanel,
943 	IDirectInputDevice2AImpl_Initialize,
944 	IDirectInputDevice2AImpl_CreateEffect,
945 	IDirectInputDevice2AImpl_EnumEffects,
946 	IDirectInputDevice2AImpl_GetEffectInfo,
947 	IDirectInputDevice2AImpl_GetForceFeedbackState,
948 	IDirectInputDevice2AImpl_SendForceFeedbackCommand,
949 	IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
950 	IDirectInputDevice2AImpl_Escape,
951 	JoystickAGenericImpl_Poll,
952 	IDirectInputDevice2AImpl_SendDeviceData,
953 	IDirectInputDevice7AImpl_EnumEffectsInFile,
954 	IDirectInputDevice7AImpl_WriteEffectToFile,
955 	JoystickAGenericImpl_BuildActionMap,
956 	JoystickAGenericImpl_SetActionMap,
957 	IDirectInputDevice8AImpl_GetImageInfo
958 };
959 
960 static const IDirectInputDevice8WVtbl JoystickWvt =
961 {
962     IDirectInputDevice2WImpl_QueryInterface,
963     IDirectInputDevice2WImpl_AddRef,
964     IDirectInputDevice2WImpl_Release,
965     JoystickWGenericImpl_GetCapabilities,
966     IDirectInputDevice2WImpl_EnumObjects,
967     JoystickLinuxWImpl_GetProperty,
968     JoystickWGenericImpl_SetProperty,
969     JoystickLinuxWImpl_Acquire,
970     JoystickLinuxWImpl_Unacquire,
971     JoystickWGenericImpl_GetDeviceState,
972     IDirectInputDevice2WImpl_GetDeviceData,
973     IDirectInputDevice2WImpl_SetDataFormat,
974     IDirectInputDevice2WImpl_SetEventNotification,
975     IDirectInputDevice2WImpl_SetCooperativeLevel,
976     JoystickWGenericImpl_GetObjectInfo,
977     JoystickLinuxWImpl_GetDeviceInfo,
978     IDirectInputDevice2WImpl_RunControlPanel,
979     IDirectInputDevice2WImpl_Initialize,
980     IDirectInputDevice2WImpl_CreateEffect,
981     IDirectInputDevice2WImpl_EnumEffects,
982     IDirectInputDevice2WImpl_GetEffectInfo,
983     IDirectInputDevice2WImpl_GetForceFeedbackState,
984     IDirectInputDevice2WImpl_SendForceFeedbackCommand,
985     IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
986     IDirectInputDevice2WImpl_Escape,
987     JoystickWGenericImpl_Poll,
988     IDirectInputDevice2WImpl_SendDeviceData,
989     IDirectInputDevice7WImpl_EnumEffectsInFile,
990     IDirectInputDevice7WImpl_WriteEffectToFile,
991     JoystickWGenericImpl_BuildActionMap,
992     JoystickWGenericImpl_SetActionMap,
993     IDirectInputDevice8WImpl_GetImageInfo
994 };
995 
996 #else  /* HAVE_LINUX_22_JOYSTICK_API */
997 
998 const struct dinput_device joystick_linux_device = {
999   "Wine Linux joystick driver",
1000   NULL,
1001   NULL,
1002   NULL
1003 };
1004 
1005 #endif  /* HAVE_LINUX_22_JOYSTICK_API */
1006