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