ole: ITypeInfo::Invoke rewrite.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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
57 #include "wine/debug.h"
58 #include "wine/unicode.h"
59 #include "windef.h"
60 #include "winbase.h"
61 #include "winerror.h"
62 #include "winreg.h"
63 #include "dinput.h"
64
65 #include "dinput_private.h"
66 #include "device_private.h"
67
68 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
69
70 #ifdef HAVE_LINUX_22_JOYSTICK_API
71
72 #define JOYDEV "/dev/js"
73
74 typedef struct {
75     LONG lMin;
76     LONG lMax;
77     LONG lDeadZone;
78     LONG lSaturation;
79 } ObjProps;
80
81 typedef struct {
82     LONG lX;
83     LONG lY;
84 } POV;
85
86 typedef struct JoystickImpl JoystickImpl;
87 static const IDirectInputDevice8AVtbl JoystickAvt;
88 static const IDirectInputDevice8WVtbl JoystickWvt;
89 struct JoystickImpl
90 {
91         const void                     *lpVtbl;
92         LONG                            ref;
93         GUID                            guid;
94         char                            dev[32];
95
96         /* The 'parent' DInput */
97         IDirectInputImpl               *dinput;
98
99         /* joystick private */
100         int                             joyfd;
101         DIJOYSTATE2                     js;             /* wine data */
102         LPDIDATAFORMAT                  user_df;        /* user defined format */
103         DataFormat                      *transform;     /* wine to user format converter */
104         int                             *offsets;       /* object offsets */
105         ObjProps                        *props;
106         HANDLE                          hEvent;
107         LPDIDEVICEOBJECTDATA            data_queue;
108         int                             queue_head, queue_tail, queue_len;
109         BOOL                            acquired;
110         char                            *name;
111         DIDEVCAPS                       devcaps;
112         LONG                            deadzone;
113         int                             *axis_map;
114         int                             axes;
115         int                             buttons;
116         POV                             povs[4];
117         CRITICAL_SECTION                crit;
118         BOOL                            overflow;
119 };
120
121 static GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
122   0x9e573ed9,
123   0x7734,
124   0x11d2,
125   {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
126 };
127
128 static void _dump_DIDEVCAPS(LPDIDEVCAPS lpDIDevCaps)
129 {
130     TRACE("dwSize: %ld\n", lpDIDevCaps->dwSize);
131     TRACE("dwFlags: %08lx\n",lpDIDevCaps->dwFlags);
132     TRACE("dwDevType: %08lx %s\n", lpDIDevCaps->dwDevType,
133           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
134           lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
135           lpDIDevCaps->dwDevType == DIDEVTYPE_MOUSE ? "DIDEVTYPE_MOUSE" :
136           lpDIDevCaps->dwDevType == DIDEVTYPE_KEYBOARD ? "DIDEVTYPE_KEYBOARD" :
137           lpDIDevCaps->dwDevType == DIDEVTYPE_JOYSTICK ? "DIDEVTYPE_JOYSTICK" :
138           lpDIDevCaps->dwDevType == DIDEVTYPE_HID ? "DIDEVTYPE_HID" : "UNKNOWN");
139     TRACE("dwAxes: %ld\n",lpDIDevCaps->dwAxes);
140     TRACE("dwButtons: %ld\n",lpDIDevCaps->dwButtons);
141     TRACE("dwPOVs: %ld\n",lpDIDevCaps->dwPOVs);
142     if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
143         TRACE("dwFFSamplePeriod: %ld\n",lpDIDevCaps->dwFFSamplePeriod);
144         TRACE("dwFFMinTimeResolution: %ld\n",lpDIDevCaps->dwFFMinTimeResolution);
145         TRACE("dwFirmwareRevision: %ld\n",lpDIDevCaps->dwFirmwareRevision);
146         TRACE("dwHardwareRevision: %ld\n",lpDIDevCaps->dwHardwareRevision);
147         TRACE("dwFFDriverVersion: %ld\n",lpDIDevCaps->dwFFDriverVersion);
148     }
149 }
150
151 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
152 {
153     int fd = -1;
154     char dev[32];
155
156     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
157         WARN("force feedback not supported\n");
158         return FALSE;
159     }
160
161     if ((dwDevType == 0) ||
162         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
163         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
164         /* check whether we have a joystick */
165         sprintf(dev, "%s%d", JOYDEV, id);
166         if ((fd = open(dev,O_RDONLY)) < 0) {
167             WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
168             return FALSE;
169         }
170
171         /* Return joystick */
172         lpddi->guidInstance = DInput_Wine_Joystick_GUID;
173         lpddi->guidInstance.Data3 = id;
174         lpddi->guidProduct = DInput_Wine_Joystick_GUID;
175         /* we only support traditional joysticks for now */
176         if (version >= 0x0800)
177             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
178         else
179             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
180         sprintf(lpddi->tszInstanceName, "Joystick %d", id);
181 #if defined(JSIOCGNAME)
182         if (ioctl(fd,JSIOCGNAME(sizeof(lpddi->tszProductName)),lpddi->tszProductName) < 0) {
183             WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
184             strcpy(lpddi->tszProductName, "Wine Joystick");
185         }
186 #else
187         strcpy(lpddi->tszProductName, "Wine Joystick");
188 #endif
189
190         lpddi->guidFFDriver = GUID_NULL;
191         close(fd);
192         TRACE("Enumerating the linux Joystick device: %s (%s)\n", dev, lpddi->tszProductName);
193         return TRUE;
194     }
195
196     return FALSE;
197 }
198
199 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
200 {
201     int fd = -1;
202     char name[MAX_PATH];
203     char dev[32];
204     char friendly[32];
205
206     if (dwFlags & DIEDFL_FORCEFEEDBACK) {
207         WARN("force feedback not supported\n");
208         return FALSE;
209     }
210
211     if ((dwDevType == 0) ||
212         ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
213         (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
214         /* check whether we have a joystick */
215         sprintf(dev, "%s%d", JOYDEV, id);
216         if ((fd = open(dev,O_RDONLY)) < 0) {
217             WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
218             return FALSE;
219         }
220
221         /* Return joystick */
222         lpddi->guidInstance = DInput_Wine_Joystick_GUID;
223         lpddi->guidInstance.Data3 = id;
224         lpddi->guidProduct = DInput_Wine_Joystick_GUID;
225         /* we only support traditional joysticks for now */
226         if (version >= 0x0800)
227             lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
228         else
229             lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
230         sprintf(friendly, "Joystick %d", id);
231         MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
232 #if defined(JSIOCGNAME)
233         if (ioctl(fd,JSIOCGNAME(sizeof(name)),name) < 0) {
234             WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
235             strcpy(name, "Wine Joystick");
236         }
237 #else
238         strcpy(name, "Wine Joystick");
239 #endif
240         MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
241         lpddi->guidFFDriver = GUID_NULL;
242         close(fd);
243         TRACE("Enumerating the linux Joystick device: %s (%s)\n",dev,name);
244         return TRUE;
245     }
246
247     return FALSE;
248 }
249
250 /*
251  * Get a config key from either the app-specific or the default config
252  */
253
254 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
255                                     char *buffer, DWORD size )
256 {
257     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
258         return 0;
259
260     if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
261         return 0;
262
263     return ERROR_FILE_NOT_FOUND;
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 = 0;
274     DWORD len;
275
276     buffer[MAX_PATH]='\0';
277
278     /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
279     if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", &hkey)) hkey = 0;
280
281     len = GetModuleFileNameA( 0, buffer, MAX_PATH );
282     if (len && len < MAX_PATH) {
283         HKEY tmpkey;
284         /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
285         if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
286         {
287             char *p, *appname = buffer;
288             if ((p = strrchr( appname, '/' ))) appname = p + 1;
289             if ((p = strrchr( appname, '\\' ))) appname = p + 1;
290             strcat( appname, "\\DirectInput" );
291             if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
292             RegCloseKey( tmpkey );
293         }
294     }
295
296     /* get options */
297
298     if (!get_config_key( hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH )) {
299         device->deadzone = atoi(buffer);
300         TRACE("setting default deadzone to: \"%s\" %ld\n", buffer, device->deadzone);
301     }
302
303     if (!get_config_key( hkey, appkey, device->name, buffer, MAX_PATH )) {
304         int tokens = 0;
305         int axis = 0;
306         int pov = 0;
307         const char *delim = ",";
308         char * ptr;
309         TRACE("\"%s\" = \"%s\"\n", device->name, buffer);
310
311         device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
312         if (device->axis_map == 0)
313             return DIERR_OUTOFMEMORY;
314
315         if ((ptr = strtok(buffer, delim)) != NULL) {
316             do {
317                 if (strcmp(ptr, "X") == 0) {
318                     device->axis_map[tokens] = 0;
319                     axis++;
320                 } else if (strcmp(ptr, "Y") == 0) {
321                     device->axis_map[tokens] = 1;
322                     axis++;
323                 } else if (strcmp(ptr, "Z") == 0) {
324                     device->axis_map[tokens] = 2;
325                     axis++;
326                 } else if (strcmp(ptr, "Rx") == 0) {
327                     device->axis_map[tokens] = 3;
328                     axis++;
329                 } else if (strcmp(ptr, "Ry") == 0) {
330                     device->axis_map[tokens] = 4;
331                     axis++;
332                 } else if (strcmp(ptr, "Rz") == 0) {
333                     device->axis_map[tokens] = 5;
334                     axis++;
335                 } else if (strcmp(ptr, "Slider1") == 0) {
336                     device->axis_map[tokens] = 6;
337                     axis++;
338                 } else if (strcmp(ptr, "Slider2") == 0) {
339                     device->axis_map[tokens] = 7;
340                     axis++;
341                 } else if (strcmp(ptr, "POV1") == 0) {
342                     device->axis_map[tokens++] = 8;
343                     device->axis_map[tokens] = 8;
344                     pov++;
345                 } else if (strcmp(ptr, "POV2") == 0) {
346                     device->axis_map[tokens++] = 9;
347                     device->axis_map[tokens] = 9;
348                     pov++;
349                 } else if (strcmp(ptr, "POV3") == 0) {
350                     device->axis_map[tokens++] = 10;
351                     device->axis_map[tokens] = 10;
352                     pov++;
353                 } else if (strcmp(ptr, "POV4") == 0) {
354                     device->axis_map[tokens++] = 11;
355                     device->axis_map[tokens] = 11;
356                     pov++;
357                 } else {
358                     ERR("invalid joystick axis type: %s\n", ptr);
359                     device->axis_map[tokens] = tokens;
360                     axis++;
361                 }
362
363                 tokens++;
364             } while ((ptr = strtok(NULL, delim)) != NULL);
365
366             if (tokens != device->devcaps.dwAxes) {
367                 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", device->axes, axis, pov,tokens);
368                 while (tokens < device->axes) {
369                     device->axis_map[tokens] = tokens;
370                     tokens++;
371                 }
372             }
373         }
374
375         device->devcaps.dwAxes = axis;
376         device->devcaps.dwPOVs = pov;
377     }
378
379     if (appkey)
380         RegCloseKey( appkey );
381
382     if (hkey)
383         RegCloseKey( hkey );
384
385     return DI_OK;
386 }
387
388 static void calculate_ids(JoystickImpl* device)
389 {
390     int i;
391     int axis = 0;
392     int button = 0;
393     int pov = 0;
394     int axis_base;
395     int pov_base;
396     int button_base;
397
398     /* Make two passes over the format. The first counts the number
399      * for each type and the second sets the id */
400     for (i = 0; i < device->user_df->dwNumObjs; i++) {
401         if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS)
402             axis++;
403         else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV)
404             pov++;
405         else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
406             button++;
407     }
408
409     axis_base = 0;
410     pov_base = axis;
411     button_base = axis + pov;
412
413     axis = 0;
414     button = 0;
415     pov = 0;
416
417     for (i = 0; i < device->user_df->dwNumObjs; i++) {
418         DWORD type = 0;
419         if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS) {
420             axis++;
421             type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
422                 DIDFT_MAKEINSTANCE(axis + axis_base);
423             TRACE("axis type = 0x%08lx\n", type);
424         } else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV) {
425             pov++;
426             type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
427                 DIDFT_MAKEINSTANCE(pov + pov_base);
428             TRACE("POV type = 0x%08lx\n", type);
429         } else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON) {
430             button++;
431             type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
432                 DIDFT_MAKEINSTANCE(button + button_base);
433             TRACE("button type = 0x%08lx\n", type);
434         }
435         device->user_df->rgodf[i].dwType = type;
436     }
437 }
438
439 static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, LPDIRECTINPUTDEVICEA* pdev)
440 {
441     DWORD i;
442     JoystickImpl* newDevice;
443     char name[MAX_PATH];
444     HRESULT hr;
445
446     newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
447     if (newDevice == 0) {
448         WARN("out of memory\n");
449         *pdev = 0;
450         return DIERR_OUTOFMEMORY;
451     }
452
453     sprintf(newDevice->dev, "%s%d", JOYDEV, rguid->Data3);
454
455     if ((newDevice->joyfd = open(newDevice->dev,O_RDONLY)) < 0) {
456         WARN("open(%s,O_RDONLY) failed: %s\n", newDevice->dev, strerror(errno));
457         HeapFree(GetProcessHeap(), 0, newDevice);
458         return DIERR_DEVICENOTREG;
459     }
460
461     /* get the device name */
462 #if defined(JSIOCGNAME)
463     if (ioctl(newDevice->joyfd,JSIOCGNAME(MAX_PATH),name) < 0) {
464         WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", newDevice->dev, strerror(errno));
465         strcpy(name, "Wine Joystick");
466     }
467 #else
468     strcpy(name, "Wine Joystick");
469 #endif
470
471     /* copy the device name */
472     newDevice->name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
473     strcpy(newDevice->name, name);
474
475 #ifdef JSIOCGAXES
476     if (ioctl(newDevice->joyfd,JSIOCGAXES,&newDevice->axes) < 0) {
477         WARN("ioctl(%s,JSIOCGAXES) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
478         newDevice->axes = 2;
479     }
480 #endif
481 #ifdef JSIOCGBUTTONS
482     if (ioctl(newDevice->joyfd,JSIOCGBUTTONS,&newDevice->buttons) < 0) {
483         WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
484         newDevice->buttons = 2;
485     }
486 #endif
487
488     newDevice->lpVtbl = jvt;
489     newDevice->ref = 1;
490     newDevice->dinput = dinput;
491     newDevice->acquired = FALSE;
492     newDevice->overflow = FALSE;
493     CopyMemory(&(newDevice->guid),rguid,sizeof(*rguid));
494
495     /* setup_dinput_options may change these */
496     newDevice->deadzone = 5000;
497     newDevice->devcaps.dwButtons = newDevice->buttons;
498     newDevice->devcaps.dwAxes = newDevice->axes;
499     newDevice->devcaps.dwPOVs = 0;
500
501     /* do any user specified configuration */
502     hr = setup_dinput_options(newDevice);
503     if (hr != DI_OK)
504         goto FAILED1;
505
506     if (newDevice->axis_map == 0) {
507         newDevice->axis_map = HeapAlloc(GetProcessHeap(), 0, newDevice->axes * sizeof(int));
508         if (newDevice->axis_map == 0)
509             goto FAILED;
510
511         for (i = 0; i < newDevice->axes; i++)
512             newDevice->axis_map[i] = i;
513     }
514
515     /* wine uses DIJOYSTATE2 as it's internal format so copy
516      * the already defined format c_dfDIJoystick2 */
517     newDevice->user_df = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwSize);
518     if (newDevice->user_df == 0)
519         goto FAILED;
520
521     CopyMemory(newDevice->user_df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
522
523     /* copy default objects */
524     newDevice->user_df->rgodf = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
525     if (newDevice->user_df->rgodf == 0)
526         goto FAILED;
527
528     CopyMemory(newDevice->user_df->rgodf,c_dfDIJoystick2.rgodf,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
529
530     /* create default properties */
531     newDevice->props = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*sizeof(ObjProps));
532     if (newDevice->props == 0)
533         goto FAILED;
534
535     /* initialize default properties */
536     for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
537         newDevice->props[i].lMin = 0;
538         newDevice->props[i].lMax = 0xffff;
539         newDevice->props[i].lDeadZone = newDevice->deadzone;    /* % * 1000 */
540         newDevice->props[i].lSaturation = 0;
541     }
542
543     /* create an offsets array */
544     newDevice->offsets = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,c_dfDIJoystick2.dwNumObjs*sizeof(int));
545     if (newDevice->offsets == 0)
546         goto FAILED;
547
548     /* create the default transform filter */
549     newDevice->transform = create_DataFormat(&c_dfDIJoystick2, newDevice->user_df, newDevice->offsets);
550
551     calculate_ids(newDevice);
552
553     IDirectInputDevice_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->dinput);
554     InitializeCriticalSection(&(newDevice->crit));
555     newDevice->crit.DebugInfo->Spare[0] = (DWORD_PTR)"DINPUT_Mouse";
556
557     newDevice->devcaps.dwSize = sizeof(newDevice->devcaps);
558     newDevice->devcaps.dwFlags = DIDC_ATTACHED;
559     if (newDevice->dinput->dwVersion >= 0x0800)
560         newDevice->devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
561     else
562         newDevice->devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
563     newDevice->devcaps.dwFFSamplePeriod = 0;
564     newDevice->devcaps.dwFFMinTimeResolution = 0;
565     newDevice->devcaps.dwFirmwareRevision = 0;
566     newDevice->devcaps.dwHardwareRevision = 0;
567     newDevice->devcaps.dwFFDriverVersion = 0;
568
569     if (TRACE_ON(dinput)) {
570         _dump_DIDATAFORMAT(newDevice->user_df);
571        for (i = 0; i < (newDevice->axes); i++)
572            TRACE("axis_map[%ld] = %d\n", i, newDevice->axis_map[i]);
573         _dump_DIDEVCAPS(&newDevice->devcaps);
574     }
575
576     *pdev = (LPDIRECTINPUTDEVICEA)newDevice;
577
578     return DI_OK;
579
580 FAILED:
581     hr = DIERR_OUTOFMEMORY;
582 FAILED1:
583     HeapFree(GetProcessHeap(),0,newDevice->axis_map);
584     HeapFree(GetProcessHeap(),0,newDevice->name);
585     HeapFree(GetProcessHeap(),0,newDevice->props);
586     HeapFree(GetProcessHeap(),0,newDevice->user_df->rgodf);
587     HeapFree(GetProcessHeap(),0,newDevice->user_df);
588     HeapFree(GetProcessHeap(),0,newDevice);
589     *pdev = 0;
590
591     return hr;
592 }
593
594 static BOOL IsJoystickGUID(REFGUID guid)
595 {
596     GUID wine_joystick = DInput_Wine_Joystick_GUID;
597     GUID dev_guid = *guid;
598
599     wine_joystick.Data3 = 0;
600     dev_guid.Data3 = 0;
601
602     return IsEqualGUID(&wine_joystick, &dev_guid);
603 }
604
605 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
606 {
607   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
608       (IsJoystickGUID(rguid))) {
609     if ((riid == NULL) ||
610         IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
611         IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
612         IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
613         IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
614       return alloc_device(rguid, &JoystickAvt, dinput, pdev);
615     } else {
616       WARN("no interface\n");
617       *pdev = 0;
618       return DIERR_NOINTERFACE;
619     }
620   }
621
622   WARN("invalid device GUID\n");
623   *pdev = 0;
624   return DIERR_DEVICENOTREG;
625 }
626
627 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
628 {
629   if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
630       (IsJoystickGUID(rguid))) {
631     if ((riid == NULL) ||
632         IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
633         IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
634         IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
635         IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
636       return alloc_device(rguid, &JoystickWvt, dinput, (LPDIRECTINPUTDEVICEA *)pdev);
637     } else {
638       WARN("no interface\n");
639       *pdev = 0;
640       return DIERR_NOINTERFACE;
641     }
642   }
643
644   WARN("invalid device GUID\n");
645   *pdev = 0;
646   return DIERR_DEVICENOTREG;
647 }
648
649 const struct dinput_device joystick_linux_device = {
650   "Wine Linux joystick driver",
651   joydev_enum_deviceA,
652   joydev_enum_deviceW,
653   joydev_create_deviceA,
654   joydev_create_deviceW
655 };
656
657 /******************************************************************************
658  *      Joystick
659  */
660 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
661 {
662     JoystickImpl *This = (JoystickImpl *)iface;
663     ULONG ref;
664
665     ref = InterlockedDecrement((&This->ref));
666     if (ref)
667         return ref;
668
669     /* Free the device name */
670     HeapFree(GetProcessHeap(),0,This->name);
671
672     /* Free the axis map */
673     HeapFree(GetProcessHeap(),0,This->axis_map);
674
675     /* Free the data queue */
676     HeapFree(GetProcessHeap(),0,This->data_queue);
677
678     /* Free the DataFormat */
679     HeapFree(GetProcessHeap(), 0, This->user_df->rgodf);
680     HeapFree(GetProcessHeap(), 0, This->user_df);
681
682     /* Free the properties */
683     HeapFree(GetProcessHeap(), 0, This->props);
684
685     /* Free the offsets array */
686     HeapFree(GetProcessHeap(),0,This->offsets);
687
688     /* release the data transform filter */
689     release_DataFormat(This->transform);
690
691     This->crit.DebugInfo->Spare[0] = 0;
692     DeleteCriticalSection(&(This->crit));
693     IDirectInputDevice_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
694
695     HeapFree(GetProcessHeap(),0,This);
696     return 0;
697 }
698
699 /******************************************************************************
700   *   SetDataFormat : the application can choose the format of the data
701   *   the device driver sends back with GetDeviceState.
702   */
703 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
704     LPDIRECTINPUTDEVICE8A iface,
705     LPCDIDATAFORMAT df)
706 {
707     JoystickImpl *This = (JoystickImpl *)iface;
708     unsigned int i;
709     LPDIDATAFORMAT new_df = 0;
710     LPDIOBJECTDATAFORMAT new_rgodf = 0;
711     ObjProps * new_props = 0;
712
713     TRACE("(%p,%p)\n",This,df);
714
715     if (df == NULL) {
716         WARN("invalid pointer\n");
717         return E_POINTER;
718     }
719
720     if (df->dwSize != sizeof(*df)) {
721         WARN("invalid argument\n");
722         return DIERR_INVALIDPARAM;
723     }
724
725     if (This->acquired) {
726         WARN("acquired\n");
727         return DIERR_ACQUIRED;
728     }
729
730     if (TRACE_ON(dinput))
731         _dump_DIDATAFORMAT(df);
732
733     /* Store the new data format */
734     new_df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
735     if (new_df == 0)
736         goto FAILED;
737
738     new_rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
739     if (new_rgodf == 0)
740         goto FAILED;
741
742     new_props = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*sizeof(ObjProps));
743     if (new_props == 0)
744         goto FAILED;
745
746     HeapFree(GetProcessHeap(),0,This->user_df);
747     HeapFree(GetProcessHeap(),0,This->user_df->rgodf);
748     HeapFree(GetProcessHeap(),0,This->props);
749     release_DataFormat(This->transform);
750
751     This->user_df = new_df;
752     CopyMemory(This->user_df, df, df->dwSize);
753     This->user_df->rgodf = new_rgodf;
754     CopyMemory(This->user_df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
755     This->props = new_props;
756     for (i = 0; i < df->dwNumObjs; i++) {
757         This->props[i].lMin = 0;
758         This->props[i].lMax = 0xffff;
759         This->props[i].lDeadZone = 1000;
760         This->props[i].lSaturation = 0;
761     }
762     This->transform = create_DataFormat(&c_dfDIJoystick2, This->user_df, This->offsets);
763
764     calculate_ids(This);
765
766     return DI_OK;
767
768 FAILED:
769     WARN("out of memory\n");
770     HeapFree(GetProcessHeap(),0,new_props);
771     HeapFree(GetProcessHeap(),0,new_rgodf);
772     HeapFree(GetProcessHeap(),0,new_df);
773     return DIERR_OUTOFMEMORY;
774 }
775
776 /******************************************************************************
777   *     Acquire : gets exclusive control of the joystick
778   */
779 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
780 {
781     JoystickImpl *This = (JoystickImpl *)iface;
782
783     TRACE("(%p)\n",This);
784
785     if (This->acquired) {
786         WARN("already acquired\n");
787         return S_FALSE;
788     }
789
790     /* open the joystick device */
791     if (This->joyfd==-1) {
792         TRACE("opening joystick device %s\n", This->dev);
793
794         This->joyfd=open(This->dev,O_RDONLY);
795         if (This->joyfd==-1) {
796             ERR("open(%s) failed: %s\n", This->dev, strerror(errno));
797             return DIERR_NOTFOUND;
798         }
799     }
800
801     This->acquired = TRUE;
802
803     return DI_OK;
804 }
805
806 /******************************************************************************
807   *     Unacquire : frees the joystick
808   */
809 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
810 {
811     JoystickImpl *This = (JoystickImpl *)iface;
812
813     TRACE("(%p)\n",This);
814
815     if (!This->acquired) {
816         WARN("not acquired\n");
817         return DIERR_NOTACQUIRED;
818     }
819
820     if (This->joyfd!=-1) {
821         TRACE("closing joystick device\n");
822         close(This->joyfd);
823         This->joyfd = -1;
824         This->acquired = FALSE;
825         return DI_OK;
826     }
827
828     This->acquired = FALSE;
829
830     return DI_NOEFFECT;
831 }
832
833 static LONG map_axis(JoystickImpl * This, short val, short index)
834 {
835     double    fval = val;
836     double    fmin = This->props[index].lMin;
837     double    fmax = This->props[index].lMax;
838     double    fret;
839
840     fret = (((fval + 32767.0) * (fmax - fmin)) / (32767.0*2.0)) + fmin;
841
842     if (fret >= 0.0)
843         fret += 0.5;
844     else
845         fret -= 0.5;
846
847     return fret;
848 }
849
850 /* convert wine format offset to user format object index */
851 static int offset_to_object(JoystickImpl *This, int offset)
852 {
853     int i;
854
855     for (i = 0; i < This->user_df->dwNumObjs; i++) {
856         if (This->user_df->rgodf[i].dwOfs == offset)
857             return i;
858     }
859
860     return -1;
861 }
862
863 static LONG calculate_pov(JoystickImpl *This, int index)
864 {
865     if (This->povs[index].lX < 16384) {
866         if (This->povs[index].lY < 16384)
867             This->js.rgdwPOV[index] = 31500;
868         else if (This->povs[index].lY > 49150)
869             This->js.rgdwPOV[index] = 22500;
870         else
871             This->js.rgdwPOV[index] = 27000;
872     } else if (This->povs[index].lX > 49150) {
873         if (This->povs[index].lY < 16384)
874             This->js.rgdwPOV[index] = 4500;
875         else if (This->povs[index].lY > 49150)
876             This->js.rgdwPOV[index] = 13500;
877         else
878             This->js.rgdwPOV[index] = 9000;
879     } else {
880         if (This->povs[index].lY < 16384)
881             This->js.rgdwPOV[index] = 0;
882         else if (This->povs[index].lY > 49150)
883             This->js.rgdwPOV[index] = 18000;
884         else
885             This->js.rgdwPOV[index] = -1;
886     }
887
888     return This->js.rgdwPOV[index];
889 }
890
891 static void joy_polldev(JoystickImpl *This) {
892     struct timeval tv;
893     fd_set      readfds;
894     struct      js_event jse;
895     TRACE("(%p)\n", This);
896
897     if (This->joyfd==-1) {
898         WARN("no device\n");
899         return;
900     }
901     while (1) {
902         memset(&tv,0,sizeof(tv));
903         FD_ZERO(&readfds);FD_SET(This->joyfd,&readfds);
904         if (1>select(This->joyfd+1,&readfds,NULL,NULL,&tv))
905             return;
906         /* we have one event, so we can read */
907         if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
908             return;
909         }
910         TRACE("js_event: type 0x%x, number %d, value %d\n",
911               jse.type,jse.number,jse.value);
912         if (jse.type & JS_EVENT_BUTTON) {
913             int offset = This->offsets[jse.number + 12];
914             int value = jse.value?0x80:0x00;
915
916             This->js.rgbButtons[jse.number] = value;
917             GEN_EVENT(offset,value,jse.time,(This->dinput->evsequence)++);
918         } else if (jse.type & JS_EVENT_AXIS) {
919             int number = This->axis_map[jse.number];    /* wine format object index */
920             if (number < 12) {
921                 int offset = This->offsets[number];
922                 int index = offset_to_object(This, offset);
923                 LONG value = map_axis(This, jse.value, index);
924
925                 /* FIXME do deadzone and saturation here */
926
927                 TRACE("changing axis %d => %d\n", jse.number, number);
928                 switch (number) {
929                 case 0:
930                     This->js.lX = value;
931                     break;
932                 case 1:
933                     This->js.lY = value;
934                     break;
935                 case 2:
936                     This->js.lZ = value;
937                     break;
938                 case 3:
939                     This->js.lRx = value;
940                     break;
941                 case 4:
942                     This->js.lRy = value;
943                     break;
944                 case 5:
945                     This->js.lRz = value;
946                     break;
947                 case 6:
948                     This->js.rglSlider[0] = value;
949                     break;
950                 case 7:
951                     This->js.rglSlider[1] = value;
952                     break;
953                 case 8:
954                     /* FIXME don't go off array */
955                     if (This->axis_map[jse.number + 1] == number)
956                         This->povs[0].lX = value;
957                     else if (This->axis_map[jse.number - 1] == number)
958                         This->povs[0].lY = value;
959                     value = calculate_pov(This, 0);
960                     break;
961                 case 9:
962                     if (This->axis_map[jse.number + 1] == number)
963                         This->povs[1].lX = value;
964                     else if (This->axis_map[jse.number - 1] == number)
965                         This->povs[1].lY = value;
966                     value = calculate_pov(This, 1);
967                     break;
968                 case 10:
969                     if (This->axis_map[jse.number + 1] == number)
970                         This->povs[2].lX = value;
971                     else if (This->axis_map[jse.number - 1] == number)
972                         This->povs[2].lY = value;
973                     value = calculate_pov(This, 2);
974                     break;
975                 case 11:
976                     if (This->axis_map[jse.number + 1] == number)
977                         This->povs[3].lX = value;
978                     else if (This->axis_map[jse.number - 1] == number)
979                         This->povs[3].lY = value;
980                     value = calculate_pov(This, 3);
981                     break;
982                 }
983
984                 GEN_EVENT(offset,value,jse.time,(This->dinput->evsequence)++);
985             } else
986                 WARN("axis %d not supported\n", number);
987         }
988     }
989 }
990
991 /******************************************************************************
992   *     GetDeviceState : returns the "state" of the joystick.
993   *
994   */
995 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
996     LPDIRECTINPUTDEVICE8A iface,
997     DWORD len,
998     LPVOID ptr)
999 {
1000     JoystickImpl *This = (JoystickImpl *)iface;
1001
1002     TRACE("(%p,0x%08lx,%p)\n",This,len,ptr);
1003
1004     if (!This->acquired) {
1005         WARN("not acquired\n");
1006         return DIERR_NOTACQUIRED;
1007     }
1008
1009     /* update joystick state */
1010     joy_polldev(This);
1011
1012     /* convert and copy data to user supplied buffer */
1013     fill_DataFormat(ptr, &This->js, This->transform);
1014
1015     return DI_OK;
1016 }
1017
1018 /******************************************************************************
1019   *     GetDeviceData : gets buffered input data.
1020   */
1021 static HRESULT WINAPI JoystickAImpl_GetDeviceData(
1022     LPDIRECTINPUTDEVICE8A iface,
1023     DWORD dodsize,
1024     LPDIDEVICEOBJECTDATA dod,
1025     LPDWORD entries,
1026     DWORD flags)
1027 {
1028     JoystickImpl *This = (JoystickImpl *)iface;
1029     DWORD len;
1030     int nqtail;
1031     HRESULT hr = DI_OK;
1032
1033     TRACE("(%p)->(dods=%ld,entries=%ld,fl=0x%08lx)\n",This,dodsize,*entries,flags);
1034
1035     if (!This->acquired) {
1036         WARN("not acquired\n");
1037         return DIERR_NOTACQUIRED;
1038     }
1039
1040     EnterCriticalSection(&(This->crit));
1041
1042     joy_polldev(This);
1043
1044     len = ((This->queue_head < This->queue_tail) ? This->queue_len : 0)
1045         + (This->queue_head - This->queue_tail);
1046     if (len > *entries)
1047         len = *entries;
1048
1049     if (dod == NULL) {
1050         if (len)
1051             TRACE("Application discarding %ld event(s).\n", len);
1052
1053         *entries = len;
1054         nqtail = This->queue_tail + len;
1055         while (nqtail >= This->queue_len)
1056             nqtail -= This->queue_len;
1057     } else {
1058         if (dodsize < sizeof(DIDEVICEOBJECTDATA_DX3)) {
1059             ERR("Wrong structure size !\n");
1060             LeaveCriticalSection(&(This->crit));
1061             return DIERR_INVALIDPARAM;
1062         }
1063
1064         if (len)
1065             TRACE("Application retrieving %ld event(s).\n", len);
1066
1067         *entries = 0;
1068         nqtail = This->queue_tail;
1069         while (len) {
1070             /* Copy the buffered data into the application queue */
1071             memcpy((char *)dod + *entries * dodsize, This->data_queue + nqtail, dodsize);
1072             /* Advance position */
1073             nqtail++;
1074             if (nqtail >= This->queue_len)
1075                 nqtail -= This->queue_len;
1076             (*entries)++;
1077             len--;
1078         }
1079     }
1080
1081     if (This->overflow) {
1082         hr = DI_BUFFEROVERFLOW;
1083         if (!(flags & DIGDD_PEEK)) {
1084             This->overflow = FALSE;
1085         }
1086     }
1087
1088     if (!(flags & DIGDD_PEEK))
1089         This->queue_tail = nqtail;
1090
1091     LeaveCriticalSection(&(This->crit));
1092
1093     return hr;
1094 }
1095
1096 static int find_property(JoystickImpl * This, LPCDIPROPHEADER ph)
1097 {
1098     int i;
1099     if (ph->dwHow == DIPH_BYOFFSET) {
1100         return offset_to_object(This, ph->dwObj);
1101     } else if (ph->dwHow == DIPH_BYID) {
1102         for (i = 0; i < This->user_df->dwNumObjs; i++) {
1103             if ((This->user_df->rgodf[i].dwType & 0x00ffffff) == (ph->dwObj & 0x00ffffff)) {
1104                 return i;
1105             }
1106         }
1107     }
1108
1109     return -1;
1110 }
1111
1112 /******************************************************************************
1113   *     SetProperty : change input device properties
1114   */
1115 static HRESULT WINAPI JoystickAImpl_SetProperty(
1116     LPDIRECTINPUTDEVICE8A iface,
1117     REFGUID rguid,
1118     LPCDIPROPHEADER ph)
1119 {
1120     JoystickImpl *This = (JoystickImpl *)iface;
1121     int i;
1122
1123     TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
1124
1125     if (ph == NULL) {
1126         WARN("invalid parameter: ph == NULL\n");
1127         return DIERR_INVALIDPARAM;
1128     }
1129
1130     if (TRACE_ON(dinput))
1131         _dump_DIPROPHEADER(ph);
1132
1133     if (!HIWORD(rguid)) {
1134         switch (LOWORD(rguid)) {
1135         case (DWORD) DIPROP_BUFFERSIZE: {
1136             LPCDIPROPDWORD      pd = (LPCDIPROPDWORD)ph;
1137             TRACE("buffersize = %ld\n",pd->dwData);
1138             if (This->data_queue)
1139                 This->data_queue = HeapReAlloc(GetProcessHeap(),0, This->data_queue, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
1140             else
1141                 This->data_queue = HeapAlloc(GetProcessHeap(),0, pd->dwData * sizeof(DIDEVICEOBJECTDATA));
1142             This->queue_head = 0;
1143             This->queue_tail = 0;
1144             This->queue_len  = pd->dwData;
1145             break;
1146         }
1147         case (DWORD)DIPROP_RANGE: {
1148             LPCDIPROPRANGE      pr = (LPCDIPROPRANGE)ph;
1149             if (ph->dwHow == DIPH_DEVICE) {
1150                 TRACE("proprange(%ld,%ld) all\n",pr->lMin,pr->lMax);
1151                 for (i = 0; i < This->user_df->dwNumObjs; i++) {
1152                     This->props[i].lMin = pr->lMin;
1153                     This->props[i].lMax = pr->lMax;
1154                 }
1155             } else {
1156                 int obj = find_property(This, ph);
1157                 TRACE("proprange(%ld,%ld) obj=%d\n",pr->lMin,pr->lMax,obj);
1158                 if (obj >= 0) {
1159                     This->props[obj].lMin = pr->lMin;
1160                     This->props[obj].lMax = pr->lMax;
1161                     return DI_OK;
1162                 }
1163             }
1164             break;
1165         }
1166         case (DWORD)DIPROP_DEADZONE: {
1167             LPCDIPROPDWORD      pd = (LPCDIPROPDWORD)ph;
1168             if (ph->dwHow == DIPH_DEVICE) {
1169                 TRACE("deadzone(%ld) all\n",pd->dwData);
1170                 for (i = 0; i < This->user_df->dwNumObjs; i++)
1171                     This->props[i].lDeadZone  = pd->dwData;
1172             } else {
1173                 int obj = find_property(This, ph);
1174                 TRACE("deadzone(%ld) obj=%d\n",pd->dwData,obj);
1175                 if (obj >= 0) {
1176                     This->props[obj].lDeadZone  = pd->dwData;
1177                     return DI_OK;
1178                 }
1179             }
1180             break;
1181         }
1182         case (DWORD)DIPROP_SATURATION: {
1183             LPCDIPROPDWORD      pd = (LPCDIPROPDWORD)ph;
1184             if (ph->dwHow == DIPH_DEVICE) {
1185                 TRACE("saturation(%ld) all\n",pd->dwData);
1186                 for (i = 0; i < This->user_df->dwNumObjs; i++)
1187                     This->props[i].lSaturation = pd->dwData;
1188             } else {
1189                 int obj = find_property(This, ph);
1190                 TRACE("saturation(%ld) obj=%d\n",pd->dwData,obj);
1191                 if (obj >= 0) {
1192                     This->props[obj].lSaturation = pd->dwData;
1193                     return DI_OK;
1194                 }
1195             }
1196             break;
1197         }
1198         default:
1199             FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
1200             break;
1201         }
1202     }
1203
1204     return DI_OK;
1205 }
1206
1207 /******************************************************************************
1208   *     SetEventNotification : specifies event to be sent on state change
1209   */
1210 static HRESULT WINAPI JoystickAImpl_SetEventNotification(
1211         LPDIRECTINPUTDEVICE8A iface, HANDLE hnd
1212 ) {
1213     JoystickImpl *This = (JoystickImpl *)iface;
1214
1215     TRACE("(this=%p,%p)\n",This,hnd);
1216     This->hEvent = hnd;
1217     return DI_OK;
1218 }
1219
1220 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
1221         LPDIRECTINPUTDEVICE8A iface,
1222         LPDIDEVCAPS lpDIDevCaps)
1223 {
1224     JoystickImpl *This = (JoystickImpl *)iface;
1225     int size;
1226
1227     TRACE("%p->(%p)\n",iface,lpDIDevCaps);
1228
1229     if (lpDIDevCaps == NULL) {
1230         WARN("invalid pointer\n");
1231         return E_POINTER;
1232     }
1233
1234     size = lpDIDevCaps->dwSize;
1235
1236     if (!(size == sizeof(DIDEVCAPS) || size == sizeof(DIDEVCAPS_DX3))) {
1237         WARN("invalid parameter\n");
1238         return DIERR_INVALIDPARAM;
1239     }
1240
1241     CopyMemory(lpDIDevCaps, &This->devcaps, size);
1242     lpDIDevCaps->dwSize = size;
1243
1244     if (TRACE_ON(dinput))
1245         _dump_DIDEVCAPS(lpDIDevCaps);
1246
1247     return DI_OK;
1248 }
1249
1250 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
1251 {
1252     JoystickImpl *This = (JoystickImpl *)iface;
1253
1254     TRACE("(%p)\n",This);
1255
1256     if (!This->acquired) {
1257         WARN("not acquired\n");
1258         return DIERR_NOTACQUIRED;
1259     }
1260
1261     joy_polldev(This);
1262     return DI_OK;
1263 }
1264
1265 /******************************************************************************
1266   *     EnumObjects : enumerate the different buttons and axis...
1267   */
1268 static HRESULT WINAPI JoystickAImpl_EnumObjects(
1269         LPDIRECTINPUTDEVICE8A iface,
1270         LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
1271         LPVOID lpvRef,
1272         DWORD dwFlags)
1273 {
1274   JoystickImpl *This = (JoystickImpl *)iface;
1275   DIDEVICEOBJECTINSTANCEA ddoi;
1276   BYTE i;
1277   int user_offset;
1278   int user_object;
1279
1280   TRACE("(this=%p,%p,%p,%08lx)\n", This, lpCallback, lpvRef, dwFlags);
1281   if (TRACE_ON(dinput)) {
1282     TRACE("  - flags = ");
1283     _dump_EnumObjects_flags(dwFlags);
1284     TRACE("\n");
1285   }
1286
1287   /* Only the fields till dwFFMaxForce are relevant */
1288   ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
1289
1290   /* For the joystick, do as is done in the GetCapabilities function */
1291   if ((dwFlags == DIDFT_ALL) ||
1292       (dwFlags & DIDFT_AXIS) ||
1293       (dwFlags & DIDFT_POV)) {
1294     int pov[4] = { 0, 0, 0, 0 };
1295     int axes = 0;
1296     int povs = 0;
1297
1298     for (i = 0; i < This->axes; i++) {
1299       int wine_obj = This->axis_map[i];
1300       BOOL skip = FALSE;
1301
1302       switch (wine_obj) {
1303       case 0:
1304         ddoi.guidType = GUID_XAxis;
1305         break;
1306       case 1:
1307         ddoi.guidType = GUID_YAxis;
1308         break;
1309       case 2:
1310         ddoi.guidType = GUID_ZAxis;
1311         break;
1312       case 3:
1313         ddoi.guidType = GUID_RxAxis;
1314         break;
1315       case 4:
1316         ddoi.guidType = GUID_RyAxis;
1317         break;
1318       case 5:
1319         ddoi.guidType = GUID_RzAxis;
1320         break;
1321       case 6:
1322         ddoi.guidType = GUID_Slider;
1323         break;
1324       case 7:
1325         ddoi.guidType = GUID_Slider;
1326         break;
1327       case 8:
1328         pov[0]++;
1329         ddoi.guidType = GUID_POV;
1330         break;
1331       case 9:
1332         pov[1]++;
1333         ddoi.guidType = GUID_POV;
1334         break;
1335       case 10:
1336         pov[2]++;
1337         ddoi.guidType = GUID_POV;
1338         break;
1339       case 11:
1340         pov[3]++;
1341         ddoi.guidType = GUID_POV;
1342         break;
1343       default:
1344         ddoi.guidType = GUID_Unknown;
1345       }
1346       if (wine_obj < 8) {
1347           user_offset = This->offsets[wine_obj];        /* get user offset from wine index */
1348           user_object = offset_to_object(This, user_offset);
1349
1350           ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
1351           ddoi.dwOfs =  This->user_df->rgodf[user_object].dwOfs;
1352           sprintf(ddoi.tszName, "Axis %d", axes);
1353           axes++;
1354       } else {
1355           if (pov[wine_obj - 8] < 2) {
1356               user_offset = This->offsets[wine_obj];    /* get user offset from wine index */
1357               user_object = offset_to_object(This, user_offset);
1358
1359               ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
1360               ddoi.dwOfs =  This->user_df->rgodf[user_object].dwOfs;
1361               sprintf(ddoi.tszName, "POV %d", povs);
1362               povs++;
1363           } else
1364               skip = TRUE;
1365       }
1366       if (!skip) {
1367           _dump_OBJECTINSTANCEA(&ddoi);
1368           if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
1369               return DI_OK;
1370       }
1371     }
1372   }
1373
1374   if ((dwFlags == DIDFT_ALL) ||
1375       (dwFlags & DIDFT_BUTTON)) {
1376
1377     /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
1378     ddoi.guidType = GUID_Button;
1379
1380     for (i = 0; i < This->buttons; i++) {
1381       user_offset = This->offsets[i + 12];      /* get user offset from wine index */
1382       user_object = offset_to_object(This, user_offset);
1383       ddoi.guidType = GUID_Button;
1384       ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
1385       ddoi.dwOfs =  This->user_df->rgodf[user_object].dwOfs;
1386       sprintf(ddoi.tszName, "Button %d", i);
1387       _dump_OBJECTINSTANCEA(&ddoi);
1388       if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
1389     }
1390   }
1391
1392   return DI_OK;
1393 }
1394
1395 /******************************************************************************
1396   *     EnumObjects : enumerate the different buttons and axis...
1397   */
1398 static HRESULT WINAPI JoystickWImpl_EnumObjects(
1399         LPDIRECTINPUTDEVICE8W iface,
1400         LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
1401         LPVOID lpvRef,
1402         DWORD dwFlags)
1403 {
1404   JoystickImpl *This = (JoystickImpl *)iface;
1405
1406   device_enumobjects_AtoWcb_data data;
1407
1408   data.lpCallBack = lpCallback;
1409   data.lpvRef = lpvRef;
1410
1411   return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
1412 }
1413
1414 /******************************************************************************
1415   *     GetProperty : get input device properties
1416   */
1417 static HRESULT WINAPI JoystickAImpl_GetProperty(
1418     LPDIRECTINPUTDEVICE8A iface,
1419     REFGUID rguid,
1420     LPDIPROPHEADER pdiph)
1421 {
1422     JoystickImpl *This = (JoystickImpl *)iface;
1423
1424     TRACE("(%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
1425
1426     if (TRACE_ON(dinput))
1427         _dump_DIPROPHEADER(pdiph);
1428
1429     if (!HIWORD(rguid)) {
1430         switch (LOWORD(rguid)) {
1431         case (DWORD) DIPROP_BUFFERSIZE: {
1432             LPDIPROPDWORD       pd = (LPDIPROPDWORD)pdiph;
1433             TRACE(" return buffersize = %d\n",This->queue_len);
1434             pd->dwData = This->queue_len;
1435             break;
1436         }
1437         case (DWORD) DIPROP_RANGE: {
1438             LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
1439             int obj = find_property(This, pdiph);
1440             /* The app is querying the current range of the axis
1441              * return the lMin and lMax values */
1442             if (obj >= 0) {
1443                 pr->lMin = This->props[obj].lMin;
1444                 pr->lMax = This->props[obj].lMax;
1445                 TRACE("range(%ld, %ld) obj=%d\n", pr->lMin, pr->lMax, obj);
1446                 return DI_OK;
1447             }
1448             break;
1449         }
1450         case (DWORD) DIPROP_DEADZONE: {
1451             LPDIPROPDWORD       pd = (LPDIPROPDWORD)pdiph;
1452             int obj = find_property(This, pdiph);
1453             if (obj >= 0) {
1454                 pd->dwData = This->props[obj].lDeadZone;
1455                 TRACE("deadzone(%ld) obj=%d\n", pd->dwData, obj);
1456                 return DI_OK;
1457             }
1458             break;
1459         }
1460         case (DWORD) DIPROP_SATURATION: {
1461             LPDIPROPDWORD       pd = (LPDIPROPDWORD)pdiph;
1462             int obj = find_property(This, pdiph);
1463             if (obj >= 0) {
1464                 pd->dwData = This->props[obj].lSaturation;
1465                 TRACE("saturation(%ld) obj=%d\n", pd->dwData, obj);
1466                 return DI_OK;
1467             }
1468             break;
1469         }
1470         default:
1471             FIXME("Unknown type %p (%s)\n",rguid,debugstr_guid(rguid));
1472             break;
1473         }
1474     }
1475
1476     return DI_OK;
1477 }
1478
1479 /******************************************************************************
1480   *     GetObjectInfo : get object info
1481   */
1482 HRESULT WINAPI JoystickAImpl_GetObjectInfo(
1483         LPDIRECTINPUTDEVICE8A iface,
1484         LPDIDEVICEOBJECTINSTANCEA pdidoi,
1485         DWORD dwObj,
1486         DWORD dwHow)
1487 {
1488     JoystickImpl *This = (JoystickImpl *)iface;
1489     DIDEVICEOBJECTINSTANCEA didoiA;
1490     unsigned int i;
1491
1492     TRACE("(%p,%p,%ld,0x%08lx(%s))\n",
1493           iface, pdidoi, dwObj, dwHow,
1494           dwHow == DIPH_BYOFFSET ? "DIPH_BYOFFSET" :
1495           dwHow == DIPH_BYID ? "DIPH_BYID" :
1496           dwHow == DIPH_BYUSAGE ? "DIPH_BYUSAGE" :
1497           "UNKNOWN");
1498
1499     if (pdidoi == NULL) {
1500         WARN("invalid parameter: pdidoi = NULL\n");
1501         return DIERR_INVALIDPARAM;
1502     }
1503
1504     if ((pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCEA)) &&
1505         (pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3A))) {
1506         WARN("invalid parameter: pdidoi->dwSize = %ld != %d or %d\n",
1507              pdidoi->dwSize, sizeof(DIDEVICEOBJECTINSTANCEA),
1508              sizeof(DIDEVICEOBJECTINSTANCE_DX3A));
1509         return DIERR_INVALIDPARAM;
1510     }
1511
1512     ZeroMemory(&didoiA, sizeof(didoiA));
1513     didoiA.dwSize = pdidoi->dwSize;
1514
1515     switch (dwHow) {
1516     case DIPH_BYOFFSET: {
1517         int axis = 0;
1518         int pov = 0;
1519         int button = 0;
1520         for (i = 0; i < This->user_df->dwNumObjs; i++) {
1521             if (This->user_df->rgodf[i].dwOfs == dwObj) {
1522                 if (This->user_df->rgodf[i].pguid)
1523                     didoiA.guidType = *This->user_df->rgodf[i].pguid;
1524                 else
1525                     didoiA.guidType = GUID_NULL;
1526
1527                 didoiA.dwOfs = dwObj;
1528                 didoiA.dwType = This->user_df->rgodf[i].dwType;
1529                 didoiA.dwFlags = This->user_df->rgodf[i].dwFlags;
1530
1531                 if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_AXIS)
1532                     sprintf(didoiA.tszName, "Axis %d", axis);
1533                 else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_POV)
1534                     sprintf(didoiA.tszName, "POV %d", pov);
1535                 else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
1536                     sprintf(didoiA.tszName, "Button %d", button);
1537
1538                 CopyMemory(pdidoi, &didoiA, pdidoi->dwSize);
1539                 return DI_OK;
1540             }
1541
1542             if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_AXIS)
1543                 axis++;
1544             else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_POV)
1545                 pov++;
1546             else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
1547                 button++;
1548         }
1549         break;
1550     }
1551     case DIPH_BYID:
1552         FIXME("dwHow = DIPH_BYID not implemented\n");
1553         break;
1554     case DIPH_BYUSAGE:
1555         FIXME("dwHow = DIPH_BYUSAGE not implemented\n");
1556         break;
1557     default:
1558         WARN("invalid parameter: dwHow = %08lx\n", dwHow);
1559         return DIERR_INVALIDPARAM;
1560     }
1561
1562     CopyMemory(pdidoi, &didoiA, pdidoi->dwSize);
1563
1564     return DI_OK;
1565 }
1566
1567 /******************************************************************************
1568   *     GetDeviceInfo : get information about a device's identity
1569   */
1570 HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
1571     LPDIRECTINPUTDEVICE8A iface,
1572     LPDIDEVICEINSTANCEA pdidi)
1573 {
1574     JoystickImpl *This = (JoystickImpl *)iface;
1575
1576     TRACE("(%p,%p)\n", iface, pdidi);
1577
1578     if (pdidi == NULL) {
1579         WARN("invalid pointer\n");
1580         return E_POINTER;
1581     }
1582
1583     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
1584         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) {
1585         WARN("invalid parameter: pdidi->dwSize = %ld != %d or %d\n",
1586              pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3A),
1587              sizeof(DIDEVICEINSTANCEA));
1588         return DIERR_INVALIDPARAM;
1589     }
1590
1591     /* Return joystick */
1592     pdidi->guidInstance = GUID_Joystick;
1593     pdidi->guidProduct = DInput_Wine_Joystick_GUID;
1594     /* we only support traditional joysticks for now */
1595     pdidi->dwDevType = This->devcaps.dwDevType;
1596     strcpy(pdidi->tszInstanceName, "Joystick");
1597     strcpy(pdidi->tszProductName, This->name);
1598     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3A)) {
1599         pdidi->guidFFDriver = GUID_NULL;
1600         pdidi->wUsagePage = 0;
1601         pdidi->wUsage = 0;
1602     }
1603
1604     return DI_OK;
1605 }
1606
1607 /******************************************************************************
1608   *     GetDeviceInfo : get information about a device's identity
1609   */
1610 HRESULT WINAPI JoystickWImpl_GetDeviceInfo(
1611     LPDIRECTINPUTDEVICE8W iface,
1612     LPDIDEVICEINSTANCEW pdidi)
1613 {
1614     JoystickImpl *This = (JoystickImpl *)iface;
1615
1616     TRACE("(%p,%p)\n", iface, pdidi);
1617
1618     if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
1619         (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) {
1620         WARN("invalid parameter: pdidi->dwSize = %ld != %d or %d\n",
1621              pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3W),
1622              sizeof(DIDEVICEINSTANCEW));
1623         return DIERR_INVALIDPARAM;
1624     }
1625
1626     /* Return joystick */
1627     pdidi->guidInstance = GUID_Joystick;
1628     pdidi->guidProduct = DInput_Wine_Joystick_GUID;
1629     /* we only support traditional joysticks for now */
1630     pdidi->dwDevType = This->devcaps.dwDevType;
1631     MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, pdidi->tszInstanceName, MAX_PATH);
1632     MultiByteToWideChar(CP_ACP, 0, This->name, -1, pdidi->tszProductName, MAX_PATH);
1633     if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3W)) {
1634         pdidi->guidFFDriver = GUID_NULL;
1635         pdidi->wUsagePage = 0;
1636         pdidi->wUsage = 0;
1637     }
1638
1639     return DI_OK;
1640 }
1641
1642 static const IDirectInputDevice8AVtbl JoystickAvt =
1643 {
1644         IDirectInputDevice2AImpl_QueryInterface,
1645         IDirectInputDevice2AImpl_AddRef,
1646         JoystickAImpl_Release,
1647         JoystickAImpl_GetCapabilities,
1648         JoystickAImpl_EnumObjects,
1649         JoystickAImpl_GetProperty,
1650         JoystickAImpl_SetProperty,
1651         JoystickAImpl_Acquire,
1652         JoystickAImpl_Unacquire,
1653         JoystickAImpl_GetDeviceState,
1654         JoystickAImpl_GetDeviceData,
1655         JoystickAImpl_SetDataFormat,
1656         JoystickAImpl_SetEventNotification,
1657         IDirectInputDevice2AImpl_SetCooperativeLevel,
1658         JoystickAImpl_GetObjectInfo,
1659         JoystickAImpl_GetDeviceInfo,
1660         IDirectInputDevice2AImpl_RunControlPanel,
1661         IDirectInputDevice2AImpl_Initialize,
1662         IDirectInputDevice2AImpl_CreateEffect,
1663         IDirectInputDevice2AImpl_EnumEffects,
1664         IDirectInputDevice2AImpl_GetEffectInfo,
1665         IDirectInputDevice2AImpl_GetForceFeedbackState,
1666         IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1667         IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1668         IDirectInputDevice2AImpl_Escape,
1669         JoystickAImpl_Poll,
1670         IDirectInputDevice2AImpl_SendDeviceData,
1671         IDirectInputDevice7AImpl_EnumEffectsInFile,
1672         IDirectInputDevice7AImpl_WriteEffectToFile,
1673         IDirectInputDevice8AImpl_BuildActionMap,
1674         IDirectInputDevice8AImpl_SetActionMap,
1675         IDirectInputDevice8AImpl_GetImageInfo
1676 };
1677
1678 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1679 # define XCAST(fun)     (typeof(SysJoystickWvt.fun))
1680 #else
1681 # define XCAST(fun)     (void*)
1682 #endif
1683
1684 static const IDirectInputDevice8WVtbl SysJoystickWvt =
1685 {
1686         IDirectInputDevice2WImpl_QueryInterface,
1687         XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1688         XCAST(Release)JoystickAImpl_Release,
1689         XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1690         JoystickWImpl_EnumObjects,
1691         XCAST(GetProperty)JoystickAImpl_GetProperty,
1692         XCAST(SetProperty)JoystickAImpl_SetProperty,
1693         XCAST(Acquire)JoystickAImpl_Acquire,
1694         XCAST(Unacquire)JoystickAImpl_Unacquire,
1695         XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1696         XCAST(GetDeviceData)JoystickAImpl_GetDeviceData,
1697         XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1698         XCAST(SetEventNotification)JoystickAImpl_SetEventNotification,
1699         XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1700         IDirectInputDevice2WImpl_GetObjectInfo,
1701         JoystickWImpl_GetDeviceInfo,
1702         XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1703         XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1704         XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
1705         IDirectInputDevice2WImpl_EnumEffects,
1706         IDirectInputDevice2WImpl_GetEffectInfo,
1707         XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
1708         XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1709         XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1710         XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1711         XCAST(Poll)JoystickAImpl_Poll,
1712         XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1713         IDirectInputDevice7WImpl_EnumEffectsInFile,
1714         IDirectInputDevice7WImpl_WriteEffectToFile,
1715         IDirectInputDevice8WImpl_BuildActionMap,
1716         IDirectInputDevice8WImpl_SetActionMap,
1717         IDirectInputDevice8WImpl_GetImageInfo
1718 };
1719 #undef XCAST
1720
1721 #else  /* HAVE_LINUX_22_JOYSTICK_API */
1722
1723 const struct dinput_device joystick_linux_device = {
1724   "Wine Linux joystick driver",
1725   NULL,
1726   NULL,
1727   NULL,
1728   NULL
1729 };
1730
1731 #endif  /* HAVE_LINUX_22_JOYSTICK_API */