winex11.drv: Fix the typos in the fullscreen state debug trace.
[wine] / dlls / dinput / joystick_linux.c
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 #include <errno.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 #endif
42 #include <sys/fcntl.h>
43 #ifdef HAVE_SYS_IOCTL_H
44 # include <sys/ioctl.h>
45 #endif
46 #include <errno.h>
47 #ifdef HAVE_SYS_ERRNO_H
48 # include <sys/errno.h>
49 #endif
50 #ifdef HAVE_LINUX_IOCTL_H
51 # include <linux/ioctl.h>
52 #endif
53 #ifdef HAVE_LINUX_JOYSTICK_H
54 # include <linux/joystick.h>
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 {
79     LONG lMin;
80     LONG lMax;
81     LONG lDeadZone;
82     LONG lSaturation;
83 } ObjProps;
84
85 typedef struct {
86     LONG lX;
87     LONG lY;
88 } POV;
89
90 typedef struct JoystickImpl JoystickImpl;
91 static const IDirectInputDevice8AVtbl JoystickAvt;
92 static const IDirectInputDevice8WVtbl JoystickWvt;
93 struct JoystickImpl
94 {
95         struct IDirectInputDevice2AImpl base;
96
97         char                            dev[32];
98
99         /* The 'parent' DInput */
100         IDirectInputImpl               *dinput;
101
102         /* joystick private */
103         int                             joyfd;
104         DIJOYSTATE2                     js;             /* wine data */
105         ObjProps                        *props;
106         char                            *name;
107         DIDEVCAPS                       devcaps;
108         LONG                            deadzone;
109         int                             *axis_map;
110         int                             axes;
111         int                             buttons;
112         POV                             povs[4];
113 };
114
115 static GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
116   0x9e573ed9,
117   0x7734,
118   0x11d2,
119   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
120 };
121
122 static void _dump_DIDEVCAPS(LPDIDEVCAPS lpDIDevCaps)
123 {
124     TRACE("dwSize: %d\n", lpDIDevCaps->dwSize);
125     TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags);
126     TRACE("dwDevType: %08x %s\n", lpDIDevCaps->dwDevType,
127           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
128           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
129           lpDIDevCaps->dwDevType == DIDEVTYPE_MOUSE ? "DIDEVTYPE_MOUSE" :
130           lpDIDevCaps->dwDevType == DIDEVTYPE_KEYBOARD ? "DIDEVTYPE_KEYBOARD" :
131           lpDIDevCaps->dwDevType == DIDEVTYPE_JOYSTICK ? "DIDEVTYPE_JOYSTICK" :
132           lpDIDevCaps->dwDevType == DIDEVTYPE_HID ? "DIDEVTYPE_HID" : "UNKNOWN");
133     TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes);
134     TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons);
135     TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs);
136     if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
137         TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod);
138         TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution);
139         TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision);
140         TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision);
141         TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion);
142     }
143 }
144
145 static int joydev_get_device(char *dev, int id)
146 {
147     int ret;
148     sprintf(dev, "%s%d", JOYDEV_NEW, id);
149     if ((ret = open(dev, O_RDONLY)) < 0) {
150         sprintf(dev, "%s%d", JOYDEV_OLD, id);
151         if ((ret = open(dev, O_RDONLY)) < 0) {
152             return -1;
153         }
154     }
155     return ret;
156 }
157
158 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
159 {
160     int fd = -1;
161     char dev[32];
162
163     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
164         WARN("force feedback not supported\n");
165         return FALSE;
166     }
167
168     if ((dwDevType == 0) ||
169         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
170         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
171         /* check whether we have a joystick */
172         if ((fd = joydev_get_device(dev, id)) < 0) {
173             WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
174             return FALSE;
175         }
176
177         /* Return joystick */
178         lpddi->guidInstance = DInput_Wine_Joystick_GUID;
179         lpddi->guidInstance.Data3 = id;
180         lpddi->guidProduct = DInput_Wine_Joystick_GUID;
181         /* we only support traditional joysticks for now */
182         if (version >= 0x0800)
183             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
184         else
185             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
186         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
187 #if defined(JSIOCGNAME)
188         if (ioctl(fd,JSIOCGNAME(sizeof(lpddi->tszProductName)),lpddi->tszProductName) < 0) {
189             WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
190             strcpy(lpddi->tszProductName, "Wine Joystick");
191         }
192 #else
193         strcpy(lpddi->tszProductName, "Wine Joystick");
194 #endif
195
196         lpddi->guidFFDriver = GUID_NULL;
197         close(fd);
198         TRACE("Enumerating the linux Joystick device: %s (%s)\n", dev, lpddi->tszProductName);
199         return TRUE;
200     }
201
202     return FALSE;
203 }
204
205 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
206 {
207     int fd = -1;
208     char name[MAX_PATH];
209     char dev[32];
210     char friendly[32];
211
212     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
213         WARN("force feedback not supported\n");
214         return FALSE;
215     }
216
217     if ((dwDevType == 0) ||
218         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
219         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
220         /* check whether we have a joystick */
221         if ((fd = joydev_get_device(dev, id)) < 0) {
222             WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
223             return FALSE;
224         }
225
226         /* Return joystick */
227         lpddi->guidInstance = DInput_Wine_Joystick_GUID;
228         lpddi->guidInstance.Data3 = id;
229         lpddi->guidProduct = DInput_Wine_Joystick_GUID;
230         /* we only support traditional joysticks for now */
231         if (version >= 0x0800)
232             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
233         else
234             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
235         sprintf(friendly, "Joystick %d", id);
236         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
237 #if defined(JSIOCGNAME)
238         if (ioctl(fd,JSIOCGNAME(sizeof(name)),name) < 0) {
239             WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
240             strcpy(name, "Wine Joystick");
241         }
242 #else
243         strcpy(name, "Wine Joystick");
244 #endif
245         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
246         lpddi->guidFFDriver = GUID_NULL;
247         close(fd);
248         TRACE("Enumerating the linux Joystick device: %s (%s)\n",dev,name);
249         return TRUE;
250     }
251
252     return FALSE;
253 }
254
255 /*
256  * Get a config key from either the app-specific or the default config
257  */
258
259 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
260                                     char *buffer, DWORD size )
261 {
262     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
263         return 0;
264
265     if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
266         return 0;
267
268     return ERROR_FILE_NOT_FOUND;
269 }
270
271 /*
272  * Setup the dinput options.
273  */
274
275 static HRESULT setup_dinput_options(JoystickImpl * device)
276 {
277     char buffer[MAX_PATH+16];
278     HKEY hkey, appkey = 0;
279     DWORD len;
280
281     buffer[MAX_PATH]='\0';
282
283     /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
284     if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", &hkey)) hkey = 0;
285
286     len = GetModuleFileNameA( 0, buffer, MAX_PATH );
287     if (len && len < MAX_PATH) {
288         HKEY tmpkey;
289         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
290         if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
291         {
292             char *p, *appname = buffer;
293             if ((p = strrchr( appname, '/' ))) appname = p + 1;
294             if ((p = strrchr( appname, '\\' ))) appname = p + 1;
295             strcat( appname, "\\DirectInput" );
296             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
297             RegCloseKey( tmpkey );
298         }
299     }
300
301     /* get options */
302
303     if (!get_config_key( hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH )) {
304         device->deadzone = atoi(buffer);
305         TRACE("setting default deadzone to: \"%s\" %d\n", buffer, device->deadzone);
306     }
307
308     if (!get_config_key( hkey, appkey, device->name, buffer, MAX_PATH )) {
309         int tokens = 0;
310         int axis = 0;
311         int pov = 0;
312         const char *delim = ",";
313         char * ptr;
314         TRACE("\"%s\" = \"%s\"\n", device->name, buffer);
315
316         device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
317         if (device->axis_map == 0)
318             return DIERR_OUTOFMEMORY;
319
320         if ((ptr = strtok(buffer, delim)) != NULL) {
321             do {
322                 if (strcmp(ptr, "X") == 0) {
323                     device->axis_map[tokens] = 0;
324                     axis++;
325                 } else if (strcmp(ptr, "Y") == 0) {
326                     device->axis_map[tokens] = 1;
327                     axis++;
328                 } else if (strcmp(ptr, "Z") == 0) {
329                     device->axis_map[tokens] = 2;
330                     axis++;
331                 } else if (strcmp(ptr, "Rx") == 0) {
332                     device->axis_map[tokens] = 3;
333                     axis++;
334                 } else if (strcmp(ptr, "Ry") == 0) {
335                     device->axis_map[tokens] = 4;
336                     axis++;
337                 } else if (strcmp(ptr, "Rz") == 0) {
338                     device->axis_map[tokens] = 5;
339                     axis++;
340                 } else if (strcmp(ptr, "Slider1") == 0) {
341                     device->axis_map[tokens] = 6;
342                     axis++;
343                 } else if (strcmp(ptr, "Slider2") == 0) {
344                     device->axis_map[tokens] = 7;
345                     axis++;
346                 } else if (strcmp(ptr, "POV1") == 0) {
347                     device->axis_map[tokens++] = 8;
348                     device->axis_map[tokens] = 8;
349                     pov++;
350                 } else if (strcmp(ptr, "POV2") == 0) {
351                     device->axis_map[tokens++] = 9;
352                     device->axis_map[tokens] = 9;
353                     pov++;
354                 } else if (strcmp(ptr, "POV3") == 0) {
355                     device->axis_map[tokens++] = 10;
356                     device->axis_map[tokens] = 10;
357                     pov++;
358                 } else if (strcmp(ptr, "POV4") == 0) {
359                     device->axis_map[tokens++] = 11;
360                     device->axis_map[tokens] = 11;
361                     pov++;
362                 } else {
363                     ERR("invalid joystick axis type: %s\n", ptr);
364                     device->axis_map[tokens] = tokens;
365                     axis++;
366                 }
367
368                 tokens++;
369             } while ((ptr = strtok(NULL, delim)) != NULL);
370
371             if (tokens != device->devcaps.dwAxes) {
372                 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", device->axes, axis, pov,tokens);
373                 while (tokens < device->axes) {
374                     device->axis_map[tokens] = tokens;
375                     tokens++;
376                 }
377             }
378         }
379
380         device->devcaps.dwAxes = axis;
381         device->devcaps.dwPOVs = pov;
382     }
383
384     if (appkey)
385         RegCloseKey( appkey );
386
387     if (hkey)
388         RegCloseKey( hkey );
389
390     return DI_OK;
391 }
392
393 static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, LPDIRECTINPUTDEVICEA* pdev)
394 {
395     DWORD i;
396     JoystickImpl* newDevice;
397     char name[MAX_PATH];
398     HRESULT hr;
399     LPDIDATAFORMAT df = NULL;
400     int idx = 0;
401
402     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
403     if (newDevice == 0) {
404         WARN("out of memory\n");
405         *pdev = 0;
406         return DIERR_OUTOFMEMORY;
407     }
408
409     if ((newDevice->joyfd = joydev_get_device(newDevice->dev, rguid->Data3)) < 0) {
410         WARN("open(%s,O_RDONLY) failed: %s\n", newDevice->dev, strerror(errno));
411         HeapFree(GetProcessHeap(), 0, newDevice);
412         return DIERR_DEVICENOTREG;
413     }
414
415     /* get the device name */
416 #if defined(JSIOCGNAME)
417     if (ioctl(newDevice->joyfd,JSIOCGNAME(MAX_PATH),name) < 0) {
418         WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", newDevice->dev, strerror(errno));
419         strcpy(name, "Wine Joystick");
420     }
421 #else
422     strcpy(name, "Wine Joystick");
423 #endif
424
425     /* copy the device name */
426     newDevice->name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
427     strcpy(newDevice->name, name);
428
429 #ifdef JSIOCGAXES
430     if (ioctl(newDevice->joyfd,JSIOCGAXES,&newDevice->axes) < 0) {
431         WARN("ioctl(%s,JSIOCGAXES) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
432         newDevice->axes = 2;
433     }
434 #endif
435 #ifdef JSIOCGBUTTONS
436     if (ioctl(newDevice->joyfd,JSIOCGBUTTONS,&newDevice->buttons) < 0) {
437         WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
438         newDevice->buttons = 2;
439     }
440 #endif
441
442     newDevice->base.lpVtbl = jvt;
443     newDevice->base.ref = 1;
444     newDevice->dinput = dinput;
445     CopyMemory(&newDevice->base.guid, rguid, sizeof(*rguid));
446     InitializeCriticalSection(&newDevice->base.crit);
447     newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)"DINPUT_joystick";
448
449     /* setup_dinput_options may change these */
450     newDevice->deadzone = 5000;
451     newDevice->devcaps.dwButtons = newDevice->buttons;
452     newDevice->devcaps.dwAxes = newDevice->axes;
453     newDevice->devcaps.dwPOVs = 0;
454
455     /* do any user specified configuration */
456     hr = setup_dinput_options(newDevice);
457     if (hr != DI_OK)
458         goto FAILED1;
459
460     if (newDevice->axis_map == 0) {
461         newDevice->axis_map = HeapAlloc(GetProcessHeap(), 0, newDevice->axes * sizeof(int));
462         if (newDevice->axis_map == 0)
463             goto FAILED;
464
465         for (i = 0; i < newDevice->axes; i++)
466             newDevice->axis_map[i] = i;
467     }
468
469     /* Create copy of default data format */
470     if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
471     memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
472
473     /* Axes include POVs */
474     df->dwNumObjs = newDevice->axes + newDevice->buttons;
475     if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
476
477     for (i = 0; i < newDevice->axes; i++)
478     {
479         int wine_obj = newDevice->axis_map[i];
480
481         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
482         if (wine_obj < 8)
483             df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
484         else
485             df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
486     }
487     for (i = 0; i < newDevice->buttons; i++)
488     {
489         memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
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].lMin = 0;
502         newDevice->props[i].lMax = 0xffff;
503         newDevice->props[i].lDeadZone = newDevice->deadzone;    /* % * 1000 */
504         newDevice->props[i].lSaturation = 0;
505     }
506
507     IDirectInput_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->dinput);
508
509     newDevice->devcaps.dwSize = sizeof(newDevice->devcaps);
510     newDevice->devcaps.dwFlags = DIDC_ATTACHED;
511     if (newDevice->dinput->dwVersion >= 0x0800)
512         newDevice->devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
513     else
514         newDevice->devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
515     newDevice->devcaps.dwFFSamplePeriod = 0;
516     newDevice->devcaps.dwFFMinTimeResolution = 0;
517     newDevice->devcaps.dwFirmwareRevision = 0;
518     newDevice->devcaps.dwHardwareRevision = 0;
519     newDevice->devcaps.dwFFDriverVersion = 0;
520
521     if (TRACE_ON(dinput)) {
522         _dump_DIDATAFORMAT(newDevice->base.data_format.wine_df);
523        for (i = 0; i < (newDevice->axes); i++)
524            TRACE("axis_map[%d] = %d\n", i, newDevice->axis_map[i]);
525         _dump_DIDEVCAPS(&newDevice->devcaps);
526     }
527
528     *pdev = (LPDIRECTINPUTDEVICEA)newDevice;
529
530     return DI_OK;
531
532 FAILED:
533     hr = DIERR_OUTOFMEMORY;
534 FAILED1:
535     if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
536     HeapFree(GetProcessHeap(), 0, df);
537     release_DataFormat(&newDevice->base.data_format);
538     HeapFree(GetProcessHeap(),0,newDevice->axis_map);
539     HeapFree(GetProcessHeap(),0,newDevice->name);
540     HeapFree(GetProcessHeap(),0,newDevice->props);
541     HeapFree(GetProcessHeap(),0,newDevice);
542     *pdev = 0;
543
544     return hr;
545 }
546
547 static BOOL IsJoystickGUID(REFGUID guid)
548 {
549     GUID wine_joystick = DInput_Wine_Joystick_GUID;
550     GUID dev_guid = *guid;
551
552     wine_joystick.Data3 = 0;
553     dev_guid.Data3 = 0;
554
555     return IsEqualGUID(&wine_joystick, &dev_guid);
556 }
557
558 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
559 {
560   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
561       (IsJoystickGUID(rguid))) {
562     if ((riid == NULL) ||
563         IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
564         IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
565         IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
566         IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
567       return alloc_device(rguid, &JoystickAvt, dinput, pdev);
568     } else {
569       WARN("no interface\n");
570       *pdev = 0;
571       return DIERR_NOINTERFACE;
572     }
573   }
574
575   WARN("invalid device GUID\n");
576   *pdev = 0;
577   return DIERR_DEVICENOTREG;
578 }
579
580 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
581 {
582   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
583       (IsJoystickGUID(rguid))) {
584     if ((riid == NULL) ||
585         IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
586         IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
587         IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
588         IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
589       return alloc_device(rguid, &JoystickWvt, dinput, (LPDIRECTINPUTDEVICEA *)pdev);
590     } else {
591       WARN("no interface\n");
592       *pdev = 0;
593       return DIERR_NOINTERFACE;
594     }
595   }
596
597   WARN("invalid device GUID\n");
598   *pdev = 0;
599   return DIERR_DEVICENOTREG;
600 }
601
602 const struct dinput_device joystick_linux_device = {
603   "Wine Linux joystick driver",
604   joydev_enum_deviceA,
605   joydev_enum_deviceW,
606   joydev_create_deviceA,
607   joydev_create_deviceW
608 };
609
610 /******************************************************************************
611  *      Joystick
612  */
613 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
614 {
615     JoystickImpl *This = (JoystickImpl *)iface;
616     ULONG ref;
617
618     ref = InterlockedDecrement(&This->base.ref);
619     if (ref)
620         return ref;
621
622     /* Free the device name */
623     HeapFree(GetProcessHeap(),0,This->name);
624
625     /* Free the axis map */
626     HeapFree(GetProcessHeap(),0,This->axis_map);
627
628     /* Free the data queue */
629     HeapFree(GetProcessHeap(), 0, This->base.data_queue);
630
631     /* Free the properties */
632     HeapFree(GetProcessHeap(), 0, This->props);
633
634     /* release the data transform filter */
635     HeapFree(GetProcessHeap(), 0, This->base.data_format.wine_df->rgodf);
636     HeapFree(GetProcessHeap(), 0, This->base.data_format.wine_df);
637     release_DataFormat(&This->base.data_format);
638
639     This->base.crit.DebugInfo->Spare[0] = 0;
640     IDirectInput_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
641     DeleteCriticalSection(&This->base.crit);
642
643     HeapFree(GetProcessHeap(),0,This);
644     return 0;
645 }
646
647 /******************************************************************************
648   *   SetDataFormat : the application can choose the format of the data
649   *   the device driver sends back with GetDeviceState.
650   */
651 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
652     LPDIRECTINPUTDEVICE8A iface,
653     LPCDIDATAFORMAT df)
654 {
655     JoystickImpl *This = (JoystickImpl *)iface;
656     unsigned int i;
657     HRESULT hr;
658
659     TRACE("(%p,%p)\n",This,df);
660
661     hr = IDirectInputDevice2AImpl_SetDataFormat(iface, df);
662     if (FAILED(hr)) return hr;
663
664     for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
665     {
666         This->props[i].lMin = 0;
667         This->props[i].lMax = 0xffff;
668         This->props[i].lDeadZone = 1000;
669         This->props[i].lSaturation = 0;
670     }
671
672     return DI_OK;
673 }
674
675 /******************************************************************************
676   *     Acquire : gets exclusive control of the joystick
677   */
678 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
679 {
680     JoystickImpl *This = (JoystickImpl *)iface;
681
682     TRACE("(%p)\n",This);
683
684     if (This->base.acquired) {
685         WARN("already acquired\n");
686         return S_FALSE;
687     }
688
689     /* open the joystick device */
690     if (This->joyfd==-1) {
691         TRACE("opening joystick device %s\n", This->dev);
692
693         This->joyfd=open(This->dev,O_RDONLY);
694         if (This->joyfd==-1) {
695             ERR("open(%s) failed: %s\n", This->dev, strerror(errno));
696             return DIERR_NOTFOUND;
697         }
698     }
699
700     This->base.acquired = 1;
701
702     return DI_OK;
703 }
704
705 /******************************************************************************
706   *     Unacquire : frees the joystick
707   */
708 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
709 {
710     JoystickImpl *This = (JoystickImpl *)iface;
711     HRESULT res;
712
713     TRACE("(%p)\n",This);
714
715     if ((res = IDirectInputDevice2AImpl_Unacquire(iface)) != DI_OK) return res;
716
717     if (This->joyfd!=-1) {
718         TRACE("closing joystick device\n");
719         close(This->joyfd);
720         This->joyfd = -1;
721         return DI_OK;
722     }
723
724     return DI_NOEFFECT;
725 }
726
727 static LONG map_axis(JoystickImpl * This, short val, short index)
728 {
729     double    fval = val;
730     double    fmin = This->props[index].lMin;
731     double    fmax = This->props[index].lMax;
732     double    fret;
733
734     fret = (((fval + 32767.0) * (fmax - fmin)) / (32767.0*2.0)) + fmin;
735
736     if (fret >= 0.0)
737         fret += 0.5;
738     else
739         fret -= 0.5;
740
741     return fret;
742 }
743
744 static LONG calculate_pov(JoystickImpl *This, int index)
745 {
746     if (This->povs[index].lX < -16384) {
747         if (This->povs[index].lY < -16384)
748             This->js.rgdwPOV[index] = 31500;
749         else if (This->povs[index].lY > 16384)
750             This->js.rgdwPOV[index] = 22500;
751         else
752             This->js.rgdwPOV[index] = 27000;
753     } else if (This->povs[index].lX > 16384) {
754         if (This->povs[index].lY < -16384)
755             This->js.rgdwPOV[index] = 4500;
756         else if (This->povs[index].lY > 16384)
757             This->js.rgdwPOV[index] = 13500;
758         else
759             This->js.rgdwPOV[index] = 9000;
760     } else {
761         if (This->povs[index].lY < -16384)
762             This->js.rgdwPOV[index] = 0;
763         else if (This->povs[index].lY > 16384)
764             This->js.rgdwPOV[index] = 18000;
765         else
766             This->js.rgdwPOV[index] = -1;
767     }
768
769     return This->js.rgdwPOV[index];
770 }
771
772 static void joy_polldev(JoystickImpl *This) {
773     struct pollfd plfd;
774     struct      js_event jse;
775     TRACE("(%p)\n", This);
776
777     if (This->joyfd==-1) {
778         WARN("no device\n");
779         return;
780     }
781     while (1)
782     {
783         LONG value;
784         int inst_id = -1;
785
786         plfd.fd = This->joyfd;
787         plfd.events = POLLIN;
788         if (poll(&plfd,1,0) != 1)
789             return;
790         /* we have one event, so we can read */
791         if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
792             return;
793         }
794         TRACE("js_event: type 0x%x, number %d, value %d\n",
795               jse.type,jse.number,jse.value);
796         if (jse.type & JS_EVENT_BUTTON)
797         {
798             inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
799             This->js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
800         }
801         else if (jse.type & JS_EVENT_AXIS)
802         {
803             int number = This->axis_map[jse.number];    /* wine format object index */
804
805             if (number < 12)
806             {
807                 inst_id = DIDFT_MAKEINSTANCE(jse.number) | (number < 8 ? DIDFT_AXIS : DIDFT_POV);
808                 value = map_axis(This, jse.value, number);
809                 /* FIXME do deadzone and saturation here */
810
811                 TRACE("changing axis %d => %d\n", jse.number, number);
812                 switch (number) {
813                 case 0:
814                     This->js.lX = value;
815                     break;
816                 case 1:
817                     This->js.lY = value;
818                     break;
819                 case 2:
820                     This->js.lZ = value;
821                     break;
822                 case 3:
823                     This->js.lRx = value;
824                     break;
825                 case 4:
826                     This->js.lRy = value;
827                     break;
828                 case 5:
829                     This->js.lRz = value;
830                     break;
831                 case 6:
832                     This->js.rglSlider[0] = value;
833                     break;
834                 case 7:
835                     This->js.rglSlider[1] = value;
836                     break;
837                 case 8:
838                     /* FIXME don't go off array */
839                     if (This->axis_map[jse.number + 1] == number)
840                         This->povs[0].lX = jse.value;
841                     else if (This->axis_map[jse.number - 1] == number)
842                         This->povs[0].lY = jse.value;
843                     value = calculate_pov(This, 0);
844                     break;
845                 case 9:
846                     if (This->axis_map[jse.number + 1] == number)
847                         This->povs[1].lX = jse.value;
848                     else if (This->axis_map[jse.number - 1] == number)
849                         This->povs[1].lY = jse.value;
850                     value = calculate_pov(This, 1);
851                     break;
852                 case 10:
853                     if (This->axis_map[jse.number + 1] == number)
854                         This->povs[2].lX = jse.value;
855                     else if (This->axis_map[jse.number - 1] == number)
856                         This->povs[2].lY = jse.value;
857                     value = calculate_pov(This, 2);
858                     break;
859                 case 11:
860                     if (This->axis_map[jse.number + 1] == number)
861                         This->povs[3].lX = jse.value;
862                     else if (This->axis_map[jse.number - 1] == number)
863                         This->povs[3].lY = jse.value;
864                     value = calculate_pov(This, 3);
865                     break;
866                 }
867             } else
868                 WARN("axis %d not supported\n", number);
869         }
870         if (inst_id >= 0)
871             queue_event((LPDIRECTINPUTDEVICE8A)This,
872                         id_to_offset(&This->base.data_format, inst_id),
873                         value, jse.time, This->dinput->evsequence++);
874     }
875 }
876
877 /******************************************************************************
878   *     GetDeviceState : returns the "state" of the joystick.
879   *
880   */
881 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
882     LPDIRECTINPUTDEVICE8A iface,
883     DWORD len,
884     LPVOID ptr)
885 {
886     JoystickImpl *This = (JoystickImpl *)iface;
887
888     TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
889
890     if (!This->base.acquired) {
891         WARN("not acquired\n");
892         return DIERR_NOTACQUIRED;
893     }
894
895     /* update joystick state */
896     joy_polldev(This);
897
898     /* convert and copy data to user supplied buffer */
899     fill_DataFormat(ptr, &This->js, &This->base.data_format);
900
901     return DI_OK;
902 }
903
904 /******************************************************************************
905   *     SetProperty : change input device properties
906   */
907 static HRESULT WINAPI JoystickAImpl_SetProperty(
908     LPDIRECTINPUTDEVICE8A iface,
909     REFGUID rguid,
910     LPCDIPROPHEADER ph)
911 {
912     JoystickImpl *This = (JoystickImpl *)iface;
913     int i;
914
915     TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
916
917     if (ph == NULL) {
918         WARN("invalid parameter: ph == NULL\n");
919         return DIERR_INVALIDPARAM;
920     }
921
922     if (TRACE_ON(dinput))
923         _dump_DIPROPHEADER(ph);
924
925     if (!HIWORD(rguid)) {
926         switch (LOWORD(rguid)) {
927         case (DWORD)DIPROP_RANGE: {
928             LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
929             if (ph->dwHow == DIPH_DEVICE) {
930                 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
931                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) {
932                     This->props[i].lMin = pr->lMin;
933                     This->props[i].lMax = pr->lMax;
934                 }
935             } else {
936                 int obj = find_property(&This->base.data_format, ph);
937
938                 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
939                 if (obj >= 0) {
940                     This->props[obj].lMin = pr->lMin;
941                     This->props[obj].lMax = pr->lMax;
942                     return DI_OK;
943                 }
944             }
945             break;
946         }
947         case (DWORD)DIPROP_DEADZONE: {
948             LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
949             if (ph->dwHow == DIPH_DEVICE) {
950                 TRACE("deadzone(%d) all\n", pd->dwData);
951                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
952                     This->props[i].lDeadZone  = pd->dwData;
953             } else {
954                 int obj = find_property(&This->base.data_format, ph);
955
956                 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
957                 if (obj >= 0) {
958                     This->props[obj].lDeadZone  = pd->dwData;
959                     return DI_OK;
960                 }
961             }
962             break;
963         }
964         case (DWORD)DIPROP_SATURATION: {
965             LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
966             if (ph->dwHow == DIPH_DEVICE) {
967                 TRACE("saturation(%d) all\n", pd->dwData);
968                 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++)
969                     This->props[i].lSaturation = pd->dwData;
970             } else {
971                 int obj = find_property(&This->base.data_format, ph);
972
973                 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
974                 if (obj >= 0) {
975                     This->props[obj].lSaturation = pd->dwData;
976                     return DI_OK;
977                 }
978             }
979             break;
980         }
981         default:
982             return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
983         }
984     }
985
986     return DI_OK;
987 }
988
989 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
990         LPDIRECTINPUTDEVICE8A iface,
991         LPDIDEVCAPS lpDIDevCaps)
992 {
993     JoystickImpl *This = (JoystickImpl *)iface;
994     int size;
995
996     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
997
998     if (lpDIDevCaps == NULL) {
999         WARN("invalid pointer\n");
1000         return E_POINTER;
1001     }
1002
1003     size = lpDIDevCaps->dwSize;
1004
1005     if (!(size == sizeof(DIDEVCAPS) || size == sizeof(DIDEVCAPS_DX3))) {
1006         WARN("invalid parameter\n");
1007         return DIERR_INVALIDPARAM;
1008     }
1009
1010     CopyMemory(lpDIDevCaps, &This->devcaps, size);
1011     lpDIDevCaps->dwSize = size;
1012
1013     if (TRACE_ON(dinput))
1014         _dump_DIDEVCAPS(lpDIDevCaps);
1015
1016     return DI_OK;
1017 }
1018
1019 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
1020 {
1021     JoystickImpl *This = (JoystickImpl *)iface;
1022
1023     TRACE("(%p)\n",This);
1024
1025     if (!This->base.acquired) {
1026         WARN("not acquired\n");
1027         return DIERR_NOTACQUIRED;
1028     }
1029
1030     joy_polldev(This);
1031     return DI_OK;
1032 }
1033
1034 /******************************************************************************
1035   *     GetProperty : get input device properties
1036   */
1037 static HRESULT WINAPI JoystickAImpl_GetProperty(
1038     LPDIRECTINPUTDEVICE8A iface,
1039     REFGUID rguid,
1040     LPDIPROPHEADER pdiph)
1041 {
1042     JoystickImpl *This = (JoystickImpl *)iface;
1043
1044     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
1045
1046     if (TRACE_ON(dinput))
1047         _dump_DIPROPHEADER(pdiph);
1048
1049     if (!HIWORD(rguid)) {
1050         switch (LOWORD(rguid)) {
1051         case (DWORD) DIPROP_RANGE: {
1052             LPDIPROPRANGE pr = (LPDIPROPRANGE)pdiph;
1053             int obj = find_property(&This->base.data_format, pdiph);
1054
1055             /* The app is querying the current range of the axis
1056              * return the lMin and lMax values */
1057             if (obj >= 0) {
1058                 pr->lMin = This->props[obj].lMin;
1059                 pr->lMax = This->props[obj].lMax;
1060                 TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
1061                 return DI_OK;
1062             }
1063             break;
1064         }
1065         case (DWORD) DIPROP_DEADZONE: {
1066             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1067             int obj = find_property(&This->base.data_format, pdiph);
1068
1069             if (obj >= 0) {
1070                 pd->dwData = This->props[obj].lDeadZone;
1071                 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
1072                 return DI_OK;
1073             }
1074             break;
1075         }
1076         case (DWORD) DIPROP_SATURATION: {
1077             LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1078             int obj = find_property(&This->base.data_format, pdiph);
1079
1080             if (obj >= 0) {
1081                 pd->dwData = This->props[obj].lSaturation;
1082                 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
1083                 return DI_OK;
1084             }
1085             break;
1086         }
1087         default:
1088             return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
1089         }
1090     }
1091
1092     return DI_OK;
1093 }
1094
1095 /******************************************************************************
1096   *     GetObjectInfo : get object info
1097   */
1098 static HRESULT WINAPI JoystickWImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface,
1099         LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow)
1100 {
1101     static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0};
1102     static const WCHAR povW[] = {'P','O','V',' ','%','d',0};
1103     static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0};
1104     HRESULT res;
1105
1106     res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow);
1107     if (res != DI_OK) return res;
1108
1109     if      (pdidoi->dwType & DIDFT_AXIS)
1110         sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType));
1111     else if (pdidoi->dwType & DIDFT_POV)
1112         sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType));
1113     else if (pdidoi->dwType & DIDFT_BUTTON)
1114         sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType));
1115
1116     _dump_OBJECTINSTANCEW(pdidoi);
1117     return res;
1118 }
1119
1120 static HRESULT WINAPI JoystickAImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface,
1121         LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow)
1122 {
1123     HRESULT res;
1124     DIDEVICEOBJECTINSTANCEW didoiW;
1125     DWORD dwSize = pdidoi->dwSize;
1126
1127     didoiW.dwSize = sizeof(didoiW);
1128     res = JoystickWImpl_GetObjectInfo((LPDIRECTINPUTDEVICE8W)iface, &didoiW, dwObj, dwHow);
1129     if (res != DI_OK) return res;
1130
1131     memset(pdidoi, 0, pdidoi->dwSize);
1132     memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
1133     pdidoi->dwSize = dwSize;
1134     WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName,
1135                         sizeof(pdidoi->tszName), NULL, NULL);
1136
1137     return res;
1138 }
1139
1140 /******************************************************************************
1141   *     GetDeviceInfo : get information about a device's identity
1142   */
1143 static HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
1144     LPDIRECTINPUTDEVICE8A iface,
1145     LPDIDEVICEINSTANCEA pdidi)
1146 {
1147     JoystickImpl *This = (JoystickImpl *)iface;
1148
1149     TRACE("(%p,%p)\n", iface, pdidi);
1150
1151     if (pdidi == NULL) {
1152         WARN("invalid pointer\n");
1153         return E_POINTER;
1154     }
1155
1156     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
1157         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) {
1158         WARN("invalid parameter: pdidi->dwSize = %d != %d or %d\n",
1159              pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3A),
1160              sizeof(DIDEVICEINSTANCEA));
1161         return DIERR_INVALIDPARAM;
1162     }
1163
1164     /* Return joystick */
1165     pdidi->guidInstance = GUID_Joystick;
1166     pdidi->guidProduct = DInput_Wine_Joystick_GUID;
1167     /* we only support traditional joysticks for now */
1168     pdidi->dwDevType = This->devcaps.dwDevType;
1169     strcpy(pdidi->tszInstanceName, "Joystick");
1170     strcpy(pdidi->tszProductName, This->name);
1171     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3A)) {
1172         pdidi->guidFFDriver = GUID_NULL;
1173         pdidi->wUsagePage = 0;
1174         pdidi->wUsage = 0;
1175     }
1176
1177     return DI_OK;
1178 }
1179
1180 /******************************************************************************
1181   *     GetDeviceInfo : get information about a device's identity
1182   */
1183 static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(
1184     LPDIRECTINPUTDEVICE8W iface,
1185     LPDIDEVICEINSTANCEW pdidi)
1186 {
1187     JoystickImpl *This = (JoystickImpl *)iface;
1188
1189     TRACE("(%p,%p)\n", iface, pdidi);
1190
1191     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
1192         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) {
1193         WARN("invalid parameter: pdidi->dwSize = %d != %d or %d\n",
1194              pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3W),
1195              sizeof(DIDEVICEINSTANCEW));
1196         return DIERR_INVALIDPARAM;
1197     }
1198
1199     /* Return joystick */
1200     pdidi->guidInstance = GUID_Joystick;
1201     pdidi->guidProduct = DInput_Wine_Joystick_GUID;
1202     /* we only support traditional joysticks for now */
1203     pdidi->dwDevType = This->devcaps.dwDevType;
1204     MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, pdidi->tszInstanceName, MAX_PATH);
1205     MultiByteToWideChar(CP_ACP, 0, This->name, -1, pdidi->tszProductName, MAX_PATH);
1206     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3W)) {
1207         pdidi->guidFFDriver = GUID_NULL;
1208         pdidi->wUsagePage = 0;
1209         pdidi->wUsage = 0;
1210     }
1211
1212     return DI_OK;
1213 }
1214
1215 static const IDirectInputDevice8AVtbl JoystickAvt =
1216 {
1217         IDirectInputDevice2AImpl_QueryInterface,
1218         IDirectInputDevice2AImpl_AddRef,
1219         JoystickAImpl_Release,
1220         JoystickAImpl_GetCapabilities,
1221         IDirectInputDevice2AImpl_EnumObjects,
1222         JoystickAImpl_GetProperty,
1223         JoystickAImpl_SetProperty,
1224         JoystickAImpl_Acquire,
1225         JoystickAImpl_Unacquire,
1226         JoystickAImpl_GetDeviceState,
1227         IDirectInputDevice2AImpl_GetDeviceData,
1228         JoystickAImpl_SetDataFormat,
1229         IDirectInputDevice2AImpl_SetEventNotification,
1230         IDirectInputDevice2AImpl_SetCooperativeLevel,
1231         JoystickAImpl_GetObjectInfo,
1232         JoystickAImpl_GetDeviceInfo,
1233         IDirectInputDevice2AImpl_RunControlPanel,
1234         IDirectInputDevice2AImpl_Initialize,
1235         IDirectInputDevice2AImpl_CreateEffect,
1236         IDirectInputDevice2AImpl_EnumEffects,
1237         IDirectInputDevice2AImpl_GetEffectInfo,
1238         IDirectInputDevice2AImpl_GetForceFeedbackState,
1239         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1240         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1241         IDirectInputDevice2AImpl_Escape,
1242         JoystickAImpl_Poll,
1243         IDirectInputDevice2AImpl_SendDeviceData,
1244         IDirectInputDevice7AImpl_EnumEffectsInFile,
1245         IDirectInputDevice7AImpl_WriteEffectToFile,
1246         IDirectInputDevice8AImpl_BuildActionMap,
1247         IDirectInputDevice8AImpl_SetActionMap,
1248         IDirectInputDevice8AImpl_GetImageInfo
1249 };
1250
1251 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1252 # define XCAST(fun)     (typeof(SysJoystickWvt.fun))
1253 #else
1254 # define XCAST(fun)     (void*)
1255 #endif
1256
1257 static const IDirectInputDevice8WVtbl SysJoystickWvt =
1258 {
1259         IDirectInputDevice2WImpl_QueryInterface,
1260         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1261         XCAST(Release)JoystickAImpl_Release,
1262         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1263         IDirectInputDevice2WImpl_EnumObjects,
1264         XCAST(GetProperty)JoystickAImpl_GetProperty,
1265         XCAST(SetProperty)JoystickAImpl_SetProperty,
1266         XCAST(Acquire)JoystickAImpl_Acquire,
1267         XCAST(Unacquire)JoystickAImpl_Unacquire,
1268         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1269         XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
1270         XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1271         XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
1272         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1273         IDirectInputDevice2WImpl_GetObjectInfo,
1274         JoystickWImpl_GetDeviceInfo,
1275         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1276         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1277         XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
1278         IDirectInputDevice2WImpl_EnumEffects,
1279         IDirectInputDevice2WImpl_GetEffectInfo,
1280         XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
1281         XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1282         XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1283         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1284         XCAST(Poll)JoystickAImpl_Poll,
1285         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1286         IDirectInputDevice7WImpl_EnumEffectsInFile,
1287         IDirectInputDevice7WImpl_WriteEffectToFile,
1288         IDirectInputDevice8WImpl_BuildActionMap,
1289         IDirectInputDevice8WImpl_SetActionMap,
1290         IDirectInputDevice8WImpl_GetImageInfo
1291 };
1292 #undef XCAST
1293
1294 #else  /* HAVE_LINUX_22_JOYSTICK_API */
1295
1296 const struct dinput_device joystick_linux_device = {
1297   "Wine Linux joystick driver",
1298   NULL,
1299   NULL,
1300   NULL,
1301   NULL
1302 };
1303
1304 #endif  /* HAVE_LINUX_22_JOYSTICK_API */