~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Wine Cross Reference
wine/dlls/dinput/joystick_linux.c

Version: ~ [ wine-0.9.61 ] ~ [ wine-0.9.60 ] ~ [ wine-0.9.59 ] ~ [ wine-0.9.58 ] ~ [ wine-0.9.57 ] ~ [ wine-0.9.56 ] ~ [ wine-0.9.55 ] ~ [ wine-0.9.54 ] ~ [ wine-0.9.53 ] ~ [ wine-0.9.52 ] ~ [ wine-0.9.51 ] ~ [ wine-0.9.50 ] ~ [ wine-0.9.49 ] ~ [ wine-0.9.48 ] ~ [ wine-0.9.47 ] ~ [ wine-0.9.46 ] ~ [ wine-0.9.45 ] ~ [ wine-0.9.44 ] ~ [ wine-0.9.43 ] ~ [ wine-0.9.42 ] ~ [ wine-0.9.41 ] ~ [ wine-0.9.40 ] ~ [ wine-0.9.39 ] ~ [ wine-0.9.38 ] ~ [ wine-0.9.37 ] ~ [ wine-0.9.36 ] ~ [ wine-0.9.35 ] ~ [ wine-0.9.34 ] ~ [ wine-0.9.33 ] ~ [ wine-0.9.32 ] ~ [ wine-0.9.31 ] ~ [ wine-0.9.30 ] ~ [ wine-0.9.29 ] ~ [ wine-0.9.28 ] ~ [ wine-0.9.27 ] ~ [ wine-0.9.26 ] ~ [ wine-0.9.25 ] ~ [ wine-0.9.24 ] ~ [ wine-0.9.23 ] ~ [ wine-0.9.22 ] ~ [ wine-0.9.21 ] ~ [ wine-0.9.20 ] ~ [ wine-0.9.19 ] ~ [ wine-0.9.18 ] ~ [ wine-0.9.17 ] ~ [ wine-0.9.16 ] ~ [ wine-0.9.15 ] ~ [ wine-0.9.14 ] ~ [ wine-0.9.13 ] ~ [ wine-0.9.12 ] ~ [ wine-0.9.11 ] ~ [ wine-0.9.10 ] ~ [ wine-0.9.9 ] ~ [ wine-0.9.8 ] ~ [ wine-0.9.7 ] ~ [ wine-0.9.6 ] ~ [ wine-0.9.5 ] ~ [ wine-0.9.4 ] ~ [ wine-0.9.3 ] ~ [ wine-0.9.2 ] ~ [ wine-0.9.1 ] ~ [ wine-0.9 ] ~ [ wine20050930 ] ~ [ wine20050830 ] ~ [ wine20050725 ] ~ [ wine20050628 ] ~ [ wine20050524 ] ~ [ wine20050419 ] ~ [ wine20050310 ] ~ [ wine20050211 ] ~ [ wine20050111 ] ~ [ wine20041201 ] ~ [ wine20041019 ] ~ [ wine20040914 ] ~ [ wine20040813 ] ~ [ wine20040716 ] ~ [ wine20040615 ] ~ [ wine20040505 ] ~ [ wine20040408 ] ~ [ wine20040309 ] ~ [ wine20040213 ] ~ [ wine20040121 ] ~ [ wine20031212 ] ~ [ wine20031118 ] ~ [ wine20031016 ] ~ [ wine20030911 ] ~ [ wine20030813 ] ~ [ wine20030709 ] ~ [ wine20030618 ] ~ [ wine20030508 ] ~ [ wine20030408 ] ~ [ wine20030318 ] ~ [ wine20030219 ] ~ [ wine20030115 ] ~ [ wine20021219 ] ~ [ wine20021125 ] ~ [ wine20021031 ] ~ [ wine20021007 ] ~ [ wine20020904 ] ~ [ wine20020804 ] ~ [ wine20020710 ] ~ [ wine20020605 ] ~ [ wine20020509 ] ~ [ wine20020411 ] ~ [ wine20020310 ] ~ [ wine20020228 ] ~ [ wine20011226 ] ~ [ wine20011108 ] ~ [ wine20011004 ] ~ [ wine20010824 ] ~ [ wine20010731 ] ~ [ wine20010629 ] ~ [ wine20010510 ] ~ [ wine20010418 ] ~ [ wine20010326 ] ~ [ wine20010305 ] ~ [ wine20010216 ] ~ [ wine20010112 ] ~ [ wine20001222 ] ~ [ wine20001202 ] ~ [ wine20001026 ] ~ [ wine20001002 ] ~ [ wine20000909 ] ~ [ wine20000821 ] ~ [ wine20000801 ] ~ [ wine20000716 ] ~ [ wine20000326 ] ~ [ wine20000227 ] ~ [ wine20000130 ] ~ [ wine20000109 ] ~

  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_SYS_ERRNO_H
 47 # include <sys/errno.h>
 48 #endif
 49 #ifdef HAVE_LINUX_IOCTL_H
 50 # include <linux/ioctl.h>
 51 #endif
 52 #ifdef HAVE_LINUX_JOYSTICK_H
 53 # include <linux/joystick.h>
 54 # undef SW_MAX
 55 #endif
 56 #ifdef HAVE_SYS_POLL_H
 57 # include <sys/poll.h>
 58 #endif
 59 
 60 #include "wine/debug.h"
 61 #include "wine/unicode.h"
 62 #include "windef.h"
 63 #include "winbase.h"
 64 #include "winerror.h"
 65 #include "winreg.h"
 66 #include "dinput.h"
 67 
 68 #include "dinput_private.h"
 69 #include "device_private.h"
 70 
 71 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
 72 
 73 #ifdef HAVE_LINUX_22_JOYSTICK_API
 74 
 75 #define JOYDEV_NEW "/dev/input/js"
 76 #define JOYDEV_OLD "/dev/js"
 77 
 78 typedef struct JoystickImpl JoystickImpl;
 79 static const IDirectInputDevice8AVtbl JoystickAvt;
 80 static const IDirectInputDevice8WVtbl JoystickWvt;
 81 struct JoystickImpl
 82 {
 83         struct IDirectInputDevice2AImpl base;
 84 
 85         char                            dev[32];
 86 
 87         /* joystick private */
 88         int                             joyfd;
 89         DIJOYSTATE2                     js;             /* wine data */
 90         ObjProps                        *props;
 91         char                            *name;
 92         DIDEVCAPS                       devcaps;
 93         LONG                            deadzone;
 94         int                             *axis_map;
 95         int                             axes;
 96         int                             buttons;
 97         POINTL                          povs[4];
 98 };
 99 
100 static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
101   0x9e573ed9,
102   0x7734,
103   0x11d2,
104   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
105 };
106 
107 static void _dump_DIDEVCAPS(const DIDEVCAPS *lpDIDevCaps)
108 {
109     TRACE("dwSize: %d\n", lpDIDevCaps->dwSize);
110     TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags);
111     TRACE("dwDevType: %08x %s\n", lpDIDevCaps->dwDevType,
112           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
113           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
114           lpDIDevCaps->dwDevType == DIDEVTYPE_MOUSE ? "DIDEVTYPE_MOUSE" :
115           lpDIDevCaps->dwDevType == DIDEVTYPE_KEYBOARD ? "DIDEVTYPE_KEYBOARD" :
116           lpDIDevCaps->dwDevType == DIDEVTYPE_JOYSTICK ? "DIDEVTYPE_JOYSTICK" :
117           lpDIDevCaps->dwDevType == DIDEVTYPE_HID ? "DIDEVTYPE_HID" : "UNKNOWN");
118     TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes);
119     TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons);
120     TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs);
121     if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
122         TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod);
123         TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution);
124         TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision);
125         TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision);
126         TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion);
127     }
128 }
129 
130 #define MAX_JOYSTICKS 64
131 static INT joystick_devices_count = -1;
132 static LPSTR joystick_devices[MAX_JOYSTICKS];
133 
134 static INT find_joystick_devices(void)
135 {
136     INT i;
137 
138     if (joystick_devices_count != -1) return joystick_devices_count;
139 
140     joystick_devices_count = 0;
141     for (i = 0; i < MAX_JOYSTICKS; i++)
142     {
143         CHAR device_name[MAX_PATH], *str;
144         INT len;
145         int fd;
146 
147         len = sprintf(device_name, "%s%d", JOYDEV_NEW, i) + 1;
148         if ((fd = open(device_name, O_RDONLY)) < 0)
149         {
150             len = sprintf(device_name, "%s%d", JOYDEV_OLD, i) + 1;
151             if ((fd = open(device_name, O_RDONLY)) < 0) continue;
152         }
153 
154         close(fd);
155 
156         if (!(str = HeapAlloc(GetProcessHeap(), 0, len))) break;
157         memcpy(str, device_name, len);
158 
159         joystick_devices[joystick_devices_count++] = str;
160     }
161 
162     return joystick_devices_count;
163 }
164 
165 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
166 {
167     int fd = -1;
168 
169     if (id >= find_joystick_devices()) return FALSE;
170 
171     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
172         WARN("force feedback not supported\n");
173         return FALSE;
174     }
175 
176     if ((dwDevType == 0) ||
177         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
178         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
179         /* check whether we have a joystick */
180         if ((fd = open(joystick_devices[id], O_RDONLY)) < 0)
181         {
182             WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id], strerror(errno));
183             return FALSE;
184         }
185 
186         /* Return joystick */
187         lpddi->guidInstance = DInput_Wine_Joystick_GUID;
188         lpddi->guidInstance.Data3 = id;
189         lpddi->guidProduct = DInput_Wine_Joystick_GUID;
190         /* we only support traditional joysticks for now */
191         if (version >= 0x0800)
192             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
193         else
194             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
195         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
196 #if defined(JSIOCGNAME)
197         if (ioctl(fd,JSIOCGNAME(sizeof(lpddi->tszProductName)),lpddi->tszProductName) < 0) {
198             WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joystick_devices[id], strerror(errno));
199             strcpy(lpddi->tszProductName, "Wine Joystick");
200         }
201 #else
202         strcpy(lpddi->tszProductName, "Wine Joystick");
203 #endif
204 
205         lpddi->guidFFDriver = GUID_NULL;
206         close(fd);
207         TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id], lpddi->tszProductName);
208         return TRUE;
209     }
210 
211     return FALSE;
212 }
213 
214 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
215 {
216     int fd = -1;
217     char name[MAX_PATH];
218     char friendly[32];
219 
220     if (id >= find_joystick_devices()) return FALSE;
221 
222     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
223         WARN("force feedback not supported\n");
224         return FALSE;
225     }
226 
227     if ((dwDevType == 0) ||
228         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
229         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
230         /* check whether we have a joystick */
231         if ((fd = open(joystick_devices[id], O_RDONLY)) < 0)
232         {
233             WARN("open(%s,O_RDONLY) failed: %s\n", joystick_devices[id], strerror(errno));
234             return FALSE;
235         }
236 
237         /* Return joystick */
238         lpddi->guidInstance = DInput_Wine_Joystick_GUID;
239         lpddi->guidInstance.Data3 = id;
240         lpddi->guidProduct = DInput_Wine_Joystick_GUID;
241         /* we only support traditional joysticks for now */
242         if (version >= 0x0800)
243             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
244         else
245             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
246         sprintf(friendly, "Joystick %d", id);
247         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
248 #if defined(JSIOCGNAME)
249         if (ioctl(fd,JSIOCGNAME(sizeof(name)),name) < 0) {
250             WARN("ioctl(%s, JSIOCGNAME) failed: %s\n", joystick_devices[id], strerror(errno));
251             strcpy(name, "Wine Joystick");
252         }
253 #else
254         strcpy(name, "Wine Joystick");
255 #endif
256         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
257         lpddi->guidFFDriver = GUID_NULL;
258         close(fd);
259         TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id], name);
260         return TRUE;
261     }
262 
263     return FALSE;
264 }
265 
266 /*
267  * Setup the dinput options.
268  */
269 
270 static HRESULT setup_dinput_options(JoystickImpl * device)
271 {
272     char buffer[MAX_PATH+16];
273     HKEY hkey, appkey;
274     int tokens = 0;
275     int axis = 0;
276     int pov = 0;
277 
278     buffer[MAX_PATH]='\0';
279 
280     get_app_key(&hkey, &appkey);
281 
282     /* get options */
283 
284     if (!get_config_key( hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH )) {
285         device->deadzone = atoi(buffer);
286         TRACE("setting default deadzone to: \"%s\" %d\n", buffer, device->deadzone);
287     }
288 
289     device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
290     if (!device->axis_map) return DIERR_OUTOFMEMORY;
291 
292     if (!get_config_key( hkey, appkey, device->name, buffer, MAX_PATH )) {
293         const char *delim = ",";
294         char * ptr;
295         TRACE("\"%s\" = \"%s\"\n", device->name, buffer);
296 
297         if ((ptr = strtok(buffer, delim)) != NULL) {
298             do {
299                 if (strcmp(ptr, "X") == 0) {
300                     device->axis_map[tokens] = 0;
301                     axis++;
302                 } else if (strcmp(ptr, "Y") == 0) {
303                     device->axis_map[tokens] = 1;
304                     axis++;
305                 } else if (strcmp(ptr, "Z") == 0) {
306                     device->axis_map[tokens] = 2;
307                     axis++;
308                 } else if (strcmp(ptr, "Rx") == 0) {
309                     device->axis_map[tokens] = 3;
310                     axis++;
311                 } else if (strcmp(ptr, "Ry") == 0) {
312                     device->axis_map[tokens] = 4;
313                     axis++;
314                 } else if (strcmp(ptr, "Rz") == 0) {
315                     device->axis_map[tokens] = 5;
316                     axis++;
317                 } else if (strcmp(ptr, "Slider1") == 0) {
318                     device->axis_map[tokens] = 6;
319                     axis++;
320                 } else if (strcmp(ptr, "Slider2") == 0) {
321                     device->axis_map[tokens] = 7;
322                     axis++;
323                 } else if (strcmp(ptr, "POV1") == 0) {
324                     device->axis_map[tokens++] = 8;
325                     device->axis_map[tokens] = 8;
326                     pov++;
327                 } else if (strcmp(ptr, "POV2") == 0) {
328                     device->axis_map[tokens++] = 9;
329                     device->axis_map[tokens] = 9;
330                     pov++;
331                 } else if (strcmp(ptr, "POV3") == 0) {
332                     device->axis_map[tokens++] = 10;
333                     device->axis_map[tokens] = 10;
334                     pov++;
335                 } else if (strcmp(ptr, "POV4") == 0) {
336                     device->axis_map[tokens++] = 11;
337                     device->axis_map[tokens] = 11;
338                     pov++;
339                 } else {
340                     ERR("invalid joystick axis type: %s\n", ptr);
341                     device->axis_map[tokens] = tokens;
342                     axis++;
343                 }
344 
345                 tokens++;
346             } while ((ptr = strtok(NULL, delim)) != NULL);
347 
348             if (tokens != device->devcaps.dwAxes) {
349                 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", device->axes, axis, pov,tokens);
350                 while (tokens < device->axes) {
351                     device->axis_map[tokens] = tokens;
352                     tokens++;
353                 }
354             }
355         }
356 
357     }
358     else
359     {
360         for (tokens = 0; tokens < device->axes; tokens++)
361         {
362             if (tokens < 8)
363                 device->axis_map[tokens] = axis++;
364             else
365             {
366                 device->axis_map[tokens++] = 8 + pov;
367                 device->axis_map[tokens  ] = 8 + pov++;
368             }
369         }
370     }
371     device->devcaps.dwAxes = axis;
372     device->devcaps.dwPOVs = pov;
373 
374     if (appkey)
375         RegCloseKey( appkey );
376 
377     if (hkey)
378         RegCloseKey( hkey );
379 
380     return DI_OK;
381 }
382 
383 static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput,
384     LPDIRECTINPUTDEVICEA* pdev, unsigned short index)
385 {
386     DWORD i;
387     JoystickImpl* newDevice;
388     char name[MAX_PATH];
389     HRESULT hr;
390     LPDIDATAFORMAT df = NULL;
391     int idx = 0;
392 
393     TRACE("%s %p %p %p %hu\n", debugstr_guid(rguid), jvt, dinput, pdev, index);
394 
395     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
396     if (newDevice == 0) {
397         WARN("out of memory\n");
398         *pdev = 0;
399         return DIERR_OUTOFMEMORY;
400     }
401 
402     if (!lstrcpynA(newDevice->dev, joystick_devices[index], sizeof(newDevice->dev)) ||
403         (newDevice->joyfd = open(newDevice->dev, O_RDONLY)) < 0)
404     {
405         WARN("open(%s, O_RDONLY) failed: %s\n", newDevice->dev, strerror(errno));
406         HeapFree(GetProcessHeap(), 0, newDevice);
407         return DIERR_DEVICENOTREG;
408     }
409 
410     /* get the device name */
411 #if defined(JSIOCGNAME)
412     if (ioctl(newDevice->joyfd,JSIOCGNAME(MAX_PATH),name) < 0) {
413         WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", newDevice->dev, strerror(errno));
414         strcpy(name, "Wine Joystick");
415     }
416 #else
417     strcpy(name, "Wine Joystick");
418 #endif
419 
420     /* copy the device name */
421     newDevice->name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
422     strcpy(newDevice->name, name);
423 
424 #ifdef JSIOCGAXES
425     if (ioctl(newDevice->joyfd,JSIOCGAXES,&newDevice->axes) < 0) {
426         WARN("ioctl(%s,JSIOCGAXES) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
427         newDevice->axes = 2;
428     }
429 #endif
430 #ifdef JSIOCGBUTTONS
431     if (ioctl(newDevice->joyfd,JSIOCGBUTTONS,&newDevice->buttons) < 0) {
432         WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
433         newDevice->buttons = 2;
434     }
435 #endif
436 
437     if (newDevice->axes > 16)
438     {
439         /* There are 24 more axes for velocity that we can use */
440         FIXME("Can't support %d axes. Clamping down to 16\n", newDevice->axes);
441         newDevice->axes = 16;
442     }
443 
444     if (newDevice->buttons > 128)
445     {
446         WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->buttons);
447         newDevice->buttons = 128;
448     }
449 
450     newDevice->base.lpVtbl = jvt;
451     newDevice->base.ref = 1;
452     newDevice->base.dinput = dinput;
453     newDevice->base.guid = *rguid;
454     InitializeCriticalSection(&newDevice->base.crit);
455     newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
456 
457     /* setup_dinput_options may change these */
458     newDevice->deadzone = 0;
459     newDevice->devcaps.dwButtons = newDevice->buttons;
460 
461     /* do any user specified configuration */
462     hr = setup_dinput_options(newDevice);
463     if (hr != DI_OK)
464         goto FAILED1;
465 
466     /* Create copy of default data format */
467     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
468     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
469 
470     df->dwNumObjs = newDevice->devcaps.dwAxes + newDevice->devcaps.dwPOVs + newDevice->buttons;
471     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
472 
473     for (i = 0; i < newDevice->axes; i++)
474     {
475         int wine_obj = newDevice->axis_map[i];
476 
477         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
478         if (wine_obj < 8)
479             df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
480         else
481         {
482             df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
483             i++; /* POV takes 2 axes */
484         }
485     }
486     for (i = 0; i < newDevice->buttons; i++)
487     {
488         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
489         df->rgodf[idx  ].pguid = &GUID_Button;
490         df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
491     }
492     newDevice->base.data_format.wine_df = df;
493 
494     /* create default properties */
495     newDevice->props = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*sizeof(ObjProps));
496     if (newDevice->props == 0)
497         goto FAILED;
498 
499     /* initialize default properties */
500     for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
501         newDevice->props[i].lDevMin = -32767;
502         newDevice->props[i].lDevMax = +32767;
503         newDevice->props[i].lMin = 0;
504         newDevice->props[i].lMax = 0xffff;
505         newDevice->props[i].lDeadZone = newDevice->deadzone;    /* % * 1000 */
506         newDevice->props[i].lSaturation = 0;
507     }
508 
509     IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->base.dinput);
510 
511     newDevice->devcaps.dwSize = sizeof(newDevice->devcaps);
512     newDevice->devcaps.dwFlags = DIDC_ATTACHED;
513     if (newDevice->base.dinput->dwVersion >= 0x0800)
514         newDevice->devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
515     else
516         newDevice->devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
517     newDevice->devcaps.dwFFSamplePeriod = 0;
518     newDevice->devcaps.dwFFMinTimeResolution = 0;
519     newDevice->devcaps.dwFirmwareRevision = 0;
520     newDevice->devcaps.dwHardwareRevision = 0;
521     newDevice->devcaps.dwFFDriverVersion = 0;
522 
523     if (TRACE_ON(dinput)) {
524         _dump_DIDATAFORMAT(newDevice->base.data_format.wine_df);
525        for (i = 0; i < (newDevice->axes); i++)
526            TRACE("axis_map[%d] = %d\n", i, newDevice->axis_map[i]);
527         _dump_DIDEVCAPS(&newDevice->devcaps);
528     }
529 
530     *pdev = (LPDIRECTINPUTDEVICEA)newDevice;
531 
532     return DI_OK;
533 
534 FAILED:
535     hr = DIERR_OUTOFMEMORY;
536 FAILED1:
537     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
538     HeapFree(GetProcessHeap(), 0, df);
539     release_DataFormat(&newDevice->base.data_format);
540     HeapFree(GetProcessHeap(),0,newDevice->axis_map);
541     HeapFree(GetProcessHeap(),0,newDevice->name);
542     HeapFree(GetProcessHeap(),0,newDevice->props);
543     HeapFree(GetProcessHeap(),0,newDevice);
544     *pdev = 0;
545 
546     return hr;
547 }
548 
549 /******************************************************************************
550   *     get_joystick_index : Get the joystick index from a given GUID
551   */
552 static unsigned short get_joystick_index(REFGUID guid)
553 {
554     GUID wine_joystick = DInput_Wine_Joystick_GUID;
555     GUID dev_guid = *guid;
556 
557     wine_joystick.Data3 = 0;
558     dev_guid.Data3 = 0;
559 
560     /* for the standard joystick GUID use index 0 */
561     if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
562 
563     /* for the wine joystick GUIDs use the index stored in Data3 */
564     if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
565 
566     return MAX_JOYSTICKS;
567 }
568 
569 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
570 {
571     unsigned short index;
572 
573     TRACE("%p %s %p %p\n",dinput, debugstr_guid(rguid), riid, pdev);
574     find_joystick_devices();
575     *pdev = NULL;
576 
577     if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
578         joystick_devices_count && index < joystick_devices_count)
579     {
580         if ((riid == NULL) ||
581             IsEqualGUID(&IID_IDirectInputDeviceA,  riid) ||
582             IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
583             IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
584             IsEqualGUID(&IID_IDirectInputDevice8A, riid))
585         {
586             return alloc_device(rguid, &JoystickAvt, dinput, pdev, index);
587         }
588 
589         WARN("no interface\n");
590         return DIERR_NOINTERFACE;
591     }
592 
593     return DIERR_DEVICENOTREG;
594 }
595 
596 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
597 {
598     unsigned short index;
599 
600     TRACE("%p %s %p %p\n",dinput, debugstr_guid(rguid), riid, pdev);
601     find_joystick_devices();
602     *pdev = NULL;
603 
604     if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
605         joystick_devices_count && index < joystick_devices_count)
606     {
607         if ((riid == NULL) ||
608             IsEqualGUID(&IID_IDirectInputDeviceW,  riid) ||
609             IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
610             IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
611             IsEqualGUID(&IID_IDirectInputDevice8W, riid))
612         {
613             return alloc_device(rguid, &JoystickWvt, dinput, (LPDIRECTINPUTDEVICEA *)pdev, index);
614         }
615         WARN("no interface\n");
616         return DIERR_NOINTERFACE;
617     }
618 
619     WARN("invalid device GUID %s\n",debugstr_guid(rguid));
620     return DIERR_DEVICENOTREG;
621 }
622 
623 #undef MAX_JOYSTICKS
624 
625 const struct dinput_device joystick_linux_device = {
626   "Wine Linux joystick driver",
627   joydev_enum_deviceA,
628   joydev_enum_deviceW,
629   joydev_create_deviceA,
630   joydev_create_deviceW
631 };
632 
633 /******************************************************************************
634   *     Acquire : gets exclusive control of the joystick
635   */
636 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
637 {
638     JoystickImpl *This = (JoystickImpl *)iface;
639 
640     TRACE("(%p)\n",This);
641 
642     if (This->base.acquired) {
643         WARN("already acquired\n");
644         return S_FALSE;
645     }
646 
647     /* open the joystick device */
648     if (This->joyfd==-1) {
649         TRACE("opening joystick device %s\n", This->dev);
650 
651         This->joyfd=open(This->dev,O_RDONLY);
652         if (This->joyfd==-1) {
653             ERR("open(%s) failed: %s\n", This->dev, strerror(errno));
654             return DIERR_NOTFOUND;
655         }
656     }
657 
658     This->base.acquired = 1;
659 
660     return DI_OK;
661 }
662 
663 /******************************************************************************
664   *     Unacquire : frees the joystick
665   */
666 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
667 {
668     JoystickImpl *This = (JoystickImpl *)iface;
669     HRESULT res;
670 
671     TRACE("(%p)\n",This);
672 
673     if ((res = IDirectInputDevice2AImpl_Unacquire(iface)) != DI_OK) return res;
674 
675     if (This->joyfd!=-1) {
676         TRACE("closing joystick device\n");
677         close(This->joyfd);
678         This->joyfd = -1;
679         return DI_OK;
680     }
681 
682     return DI_NOEFFECT;
683 }
684 
685 static void joy_polldev(JoystickImpl *This) {
686     struct pollfd plfd;
687     struct      js_event jse;
688     TRACE("(%p)\n", This);
689 
690     if (This->joyfd==-1) {
691         WARN("no device\n");
692         return;
693     }
694     while (1)
695     {
696         LONG value;
697         int inst_id = -1;
698 
699         plfd.fd = This->joyfd;
700         plfd.events = POLLIN;
701         if (poll(&plfd,1,0) != 1)
702             return;
703         /* we have one event, so we can read */
704         if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
705             return;
706         }
707         TRACE("js_event: type 0x%x, number %d, value %d\n",
708               jse.type,jse.number,jse.value);
709         if (jse.type & JS_EVENT_BUTTON)
710         {
711             inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
712             This->js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
713         }
714         else if (jse.type & JS_EVENT_AXIS)
715         {
716             int number = This->axis_map[jse.number];    /* wine format object index */
717 
718             if (number < 0) return;
719             inst_id = DIDFT_MAKEINSTANCE(number) | (number < 8 ? DIDFT_ABSAXIS : DIDFT_POV);
720             value = joystick_map_axis(&This->props[id_to_object(This->base.data_format.wine_df, inst_id)], jse.value);
721 
722             TRACE("changing axis %d => %d\n", jse.number, number);
723             switch (number)
724             {
725                 case 0: This->js.lX  = value; break;
726                 case 1: This->js.lY  = value; break;
727                 case 2: This->js.lZ  = value; break;
728                 case 3: This->js.lRx = value; break;
729                 case 4: This->js.lRy = value; break;
730                 case 5: This->js.lRz = value; break;
731                 case 6: This->js.rglSlider[0] = value; break;
732                 case 7: This->js.rglSlider[1] = value; break;
733                 case 8: case 9: case 10: case 11:
734                 {
735                     int idx = number - 8;
736 
737                     if (jse.number % 2)
738                         This->povs[idx].y = jse.value;
739                     else
740                         This->povs[idx].x = jse.value;
741 
742                     This->js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
743                     break;
744                 }
745                 default:
746                     WARN("axis %d not supported\n", number);
747             }
748         }
749         if (inst_id >= 0)
750             queue_event((LPDIRECTINPUTDEVICE8A)This,
751                         id_to_offset(&This->base.data_format, inst_id),
752                         value, jse.time, This->base.dinput->evsequence++);
753     }
754 }
755 
756 /******************************************************************************
757   *     GetDeviceState : returns the "state" of the joystick.
758   *
759   */
760 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
761     LPDIRECTINPUTDEVICE8A iface,
762     DWORD len,
763     LPVOID ptr)
764 {
765     JoystickImpl *This = (JoystickImpl *)iface;
766 
767     TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
768 
769     if (!This->base.acquired) {
770         WARN("not acquired\n");
771         return DIERR_NOTACQUIRED;
772     }
773 
774     /* update joystick state */
775     joy_polldev(This);
776 
777     /* convert and copy data to user supplied buffer */
778     fill_DataFormat(ptr, &This->js, &This->base.data_format);
779 
780     return DI_OK;
781 }
782 
783 /******************************************************************************
784   *     SetProperty : change input device properties
785   */
786 static HRESULT WINAPI JoystickAImpl_SetProperty(
787     LPDIRECTINPUTDEVICE8A iface,
788     REFGUID rguid,
789     LPCDIPROPHEADER ph)
790 {
791     JoystickImpl *This = (JoystickImpl *)iface;
792     int i;
793 
794     TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
795 
796     if (ph == NULL) {
797         WARN("invalid parameter: ph == NULL\n");
798         return DIERR_INVALIDPARAM;
799     }
800 
801     if (TRACE_ON(dinput))
802         _dump_DIPROPHEADER(ph);
803 
804     if (!HIWORD(rguid)) {
805         switch (LOWORD(rguid)) {
806         case (DWORD)DIPROP_RANGE: {
807             LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
808             if (ph->dwHow == DIPH_DEVICE) {
809                 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
810                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
811                     This->props[i].lMin = pr->lMin;
812                     This->props[i].lMax = pr->lMax;
813                 }
814             } else {
815                 int obj = find_property(&This->base.data_format, ph);
816 
817                 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
818                 if (obj >= 0) {
819                     This->props[obj].lMin = pr->lMin;
820                     This->props[obj].lMax = pr->lMax;
821                     return DI_OK;
822                 }
823             }
824             break;
825         }
826         case (DWORD)DIPROP_DEADZONE: {
827             LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
828             if (ph->dwHow == DIPH_DEVICE) {
829                 TRACE("deadzone(%d) all\n", pd->dwData);
830                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
831                     This->props[i].lDeadZone  = pd->dwData;
832             } else {
833                 int obj = find_property(&This->base.data_format, ph);
834 
835                 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
836                 if (obj >= 0) {
837                     This->props[obj].lDeadZone  = pd->dwData;
838                     return DI_OK;
839                 }
840             }
841             break;
842         }
843         case (DWORD)DIPROP_SATURATION: {
844             LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
845             if (ph->dwHow == DIPH_DEVICE) {
846                 TRACE("saturation(%d) all\n", pd->dwData);
847                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
848                     This->props[i].lSaturation = pd->dwData;
849             } else {
850                 int obj = find_property(&This->base.data_format, ph);
851