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