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