dinput: Fix processing of custom format.
[wine/hacks.git] / dlls / dinput / joystick_linux.c
blob474678cc30c2bfb66e76dd9b17d98efef3d080ea
1 /* DirectInput Joystick device
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
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.
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.
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
23 * To Do:
24 * dead zone
25 * force feedback
28 #include "config.h"
29 #include "wine/port.h"
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 #include <errno.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 #endif
42 #include <sys/fcntl.h>
43 #ifdef HAVE_SYS_IOCTL_H
44 # include <sys/ioctl.h>
45 #endif
46 #include <errno.h>
47 #ifdef HAVE_SYS_ERRNO_H
48 # include <sys/errno.h>
49 #endif
50 #ifdef HAVE_LINUX_IOCTL_H
51 # include <linux/ioctl.h>
52 #endif
53 #ifdef HAVE_LINUX_JOYSTICK_H
54 # include <linux/joystick.h>
55 #endif
56 #ifdef HAVE_SYS_POLL_H
57 # include <sys/poll.h>
58 #endif
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"
68 #include "dinput_private.h"
69 #include "device_private.h"
71 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
73 #ifdef HAVE_LINUX_22_JOYSTICK_API
75 #define JOYDEV_NEW "/dev/input/js"
76 #define JOYDEV_OLD "/dev/js"
78 typedef struct {
79 LONG lMin;
80 LONG lMax;
81 LONG lDeadZone;
82 LONG lSaturation;
83 } ObjProps;
85 typedef struct {
86 LONG lX;
87 LONG lY;
88 } POV;
90 typedef struct JoystickImpl JoystickImpl;
91 static const IDirectInputDevice8AVtbl JoystickAvt;
92 static const IDirectInputDevice8WVtbl JoystickWvt;
93 struct JoystickImpl
95 struct IDirectInputDevice2AImpl base;
97 char dev[32];
99 /* The 'parent' DInput */
100 IDirectInputImpl *dinput;
102 /* joystick private */
103 int joyfd;
104 DIJOYSTATE2 js; /* wine data */
105 LPDIDATAFORMAT user_df; /* user defined format */
106 DataFormat *transform; /* wine to user format converter */
107 int *offsets; /* object offsets */
108 ObjProps *props;
109 char *name;
110 DIDEVCAPS devcaps;
111 LONG deadzone;
112 int *axis_map;
113 int axes;
114 int buttons;
115 POV povs[4];
118 static GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
119 0x9e573ed9,
120 0x7734,
121 0x11d2,
122 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
125 static void _dump_DIDEVCAPS(LPDIDEVCAPS lpDIDevCaps)
127 TRACE("dwSize: %d\n", lpDIDevCaps->dwSize);
128 TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags);
129 TRACE("dwDevType: %08x %s\n", lpDIDevCaps->dwDevType,
130 lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
131 lpDIDevCaps->dwDevType == DIDEVTYPE_DEVICE ? "DIDEVTYPE_DEVICE" :
132 lpDIDevCaps->dwDevType == DIDEVTYPE_MOUSE ? "DIDEVTYPE_MOUSE" :
133 lpDIDevCaps->dwDevType == DIDEVTYPE_KEYBOARD ? "DIDEVTYPE_KEYBOARD" :
134 lpDIDevCaps->dwDevType == DIDEVTYPE_JOYSTICK ? "DIDEVTYPE_JOYSTICK" :
135 lpDIDevCaps->dwDevType == DIDEVTYPE_HID ? "DIDEVTYPE_HID" : "UNKNOWN");
136 TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes);
137 TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons);
138 TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs);
139 if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) {
140 TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod);
141 TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution);
142 TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision);
143 TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision);
144 TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion);
148 static int joydev_get_device(char *dev, int id)
150 int ret;
151 sprintf(dev, "%s%d", JOYDEV_NEW, id);
152 if ((ret = open(dev, O_RDONLY)) < 0) {
153 sprintf(dev, "%s%d", JOYDEV_OLD, id);
154 if ((ret = open(dev, O_RDONLY)) < 0) {
155 return -1;
158 return ret;
161 static BOOL joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
163 int fd = -1;
164 char dev[32];
166 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
167 WARN("force feedback not supported\n");
168 return FALSE;
171 if ((dwDevType == 0) ||
172 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
173 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
174 /* check whether we have a joystick */
175 if ((fd = joydev_get_device(dev, id)) < 0) {
176 WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
177 return FALSE;
180 /* Return joystick */
181 lpddi->guidInstance = DInput_Wine_Joystick_GUID;
182 lpddi->guidInstance.Data3 = id;
183 lpddi->guidProduct = DInput_Wine_Joystick_GUID;
184 /* we only support traditional joysticks for now */
185 if (version >= 0x0800)
186 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
187 else
188 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
189 sprintf(lpddi->tszInstanceName, "Joystick %d", id);
190 #if defined(JSIOCGNAME)
191 if (ioctl(fd,JSIOCGNAME(sizeof(lpddi->tszProductName)),lpddi->tszProductName) < 0) {
192 WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
193 strcpy(lpddi->tszProductName, "Wine Joystick");
195 #else
196 strcpy(lpddi->tszProductName, "Wine Joystick");
197 #endif
199 lpddi->guidFFDriver = GUID_NULL;
200 close(fd);
201 TRACE("Enumerating the linux Joystick device: %s (%s)\n", dev, lpddi->tszProductName);
202 return TRUE;
205 return FALSE;
208 static BOOL joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
210 int fd = -1;
211 char name[MAX_PATH];
212 char dev[32];
213 char friendly[32];
215 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
216 WARN("force feedback not supported\n");
217 return FALSE;
220 if ((dwDevType == 0) ||
221 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
222 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
223 /* check whether we have a joystick */
224 if ((fd = joydev_get_device(dev, id)) < 0) {
225 WARN("open(%s,O_RDONLY) failed: %s\n", dev, strerror(errno));
226 return FALSE;
229 /* Return joystick */
230 lpddi->guidInstance = DInput_Wine_Joystick_GUID;
231 lpddi->guidInstance.Data3 = id;
232 lpddi->guidProduct = DInput_Wine_Joystick_GUID;
233 /* we only support traditional joysticks for now */
234 if (version >= 0x0800)
235 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
236 else
237 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
238 sprintf(friendly, "Joystick %d", id);
239 MultiByteToWideChar(CP_ACP, 0, friendly, -1, lpddi->tszInstanceName, MAX_PATH);
240 #if defined(JSIOCGNAME)
241 if (ioctl(fd,JSIOCGNAME(sizeof(name)),name) < 0) {
242 WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", dev, strerror(errno));
243 strcpy(name, "Wine Joystick");
245 #else
246 strcpy(name, "Wine Joystick");
247 #endif
248 MultiByteToWideChar(CP_ACP, 0, name, -1, lpddi->tszProductName, MAX_PATH);
249 lpddi->guidFFDriver = GUID_NULL;
250 close(fd);
251 TRACE("Enumerating the linux Joystick device: %s (%s)\n",dev,name);
252 return TRUE;
255 return FALSE;
259 * Get a config key from either the app-specific or the default config
262 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
263 char *buffer, DWORD size )
265 if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, (LPBYTE)buffer, &size ))
266 return 0;
268 if (defkey && !RegQueryValueExA( defkey, name, 0, NULL, (LPBYTE)buffer, &size ))
269 return 0;
271 return ERROR_FILE_NOT_FOUND;
275 * Setup the dinput options.
278 static HRESULT setup_dinput_options(JoystickImpl * device)
280 char buffer[MAX_PATH+16];
281 HKEY hkey, appkey = 0;
282 DWORD len;
284 buffer[MAX_PATH]='\0';
286 /* @@ Wine registry key: HKCU\Software\Wine\DirectInput */
287 if (RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\DirectInput", &hkey)) hkey = 0;
289 len = GetModuleFileNameA( 0, buffer, MAX_PATH );
290 if (len && len < MAX_PATH) {
291 HKEY tmpkey;
292 /* @@ Wine registry key: HKCU\Software\Wine\AppDefaults\app.exe\DirectInput */
293 if (!RegOpenKeyA( HKEY_CURRENT_USER, "Software\\Wine\\AppDefaults", &tmpkey ))
295 char *p, *appname = buffer;
296 if ((p = strrchr( appname, '/' ))) appname = p + 1;
297 if ((p = strrchr( appname, '\\' ))) appname = p + 1;
298 strcat( appname, "\\DirectInput" );
299 if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
300 RegCloseKey( tmpkey );
304 /* get options */
306 if (!get_config_key( hkey, appkey, "DefaultDeadZone", buffer, MAX_PATH )) {
307 device->deadzone = atoi(buffer);
308 TRACE("setting default deadzone to: \"%s\" %d\n", buffer, device->deadzone);
311 if (!get_config_key( hkey, appkey, device->name, buffer, MAX_PATH )) {
312 int tokens = 0;
313 int axis = 0;
314 int pov = 0;
315 const char *delim = ",";
316 char * ptr;
317 TRACE("\"%s\" = \"%s\"\n", device->name, buffer);
319 device->axis_map = HeapAlloc(GetProcessHeap(), 0, device->axes * sizeof(int));
320 if (device->axis_map == 0)
321 return DIERR_OUTOFMEMORY;
323 if ((ptr = strtok(buffer, delim)) != NULL) {
324 do {
325 if (strcmp(ptr, "X") == 0) {
326 device->axis_map[tokens] = 0;
327 axis++;
328 } else if (strcmp(ptr, "Y") == 0) {
329 device->axis_map[tokens] = 1;
330 axis++;
331 } else if (strcmp(ptr, "Z") == 0) {
332 device->axis_map[tokens] = 2;
333 axis++;
334 } else if (strcmp(ptr, "Rx") == 0) {
335 device->axis_map[tokens] = 3;
336 axis++;
337 } else if (strcmp(ptr, "Ry") == 0) {
338 device->axis_map[tokens] = 4;
339 axis++;
340 } else if (strcmp(ptr, "Rz") == 0) {
341 device->axis_map[tokens] = 5;
342 axis++;
343 } else if (strcmp(ptr, "Slider1") == 0) {
344 device->axis_map[tokens] = 6;
345 axis++;
346 } else if (strcmp(ptr, "Slider2") == 0) {
347 device->axis_map[tokens] = 7;
348 axis++;
349 } else if (strcmp(ptr, "POV1") == 0) {
350 device->axis_map[tokens++] = 8;
351 device->axis_map[tokens] = 8;
352 pov++;
353 } else if (strcmp(ptr, "POV2") == 0) {
354 device->axis_map[tokens++] = 9;
355 device->axis_map[tokens] = 9;
356 pov++;
357 } else if (strcmp(ptr, "POV3") == 0) {
358 device->axis_map[tokens++] = 10;
359 device->axis_map[tokens] = 10;
360 pov++;
361 } else if (strcmp(ptr, "POV4") == 0) {
362 device->axis_map[tokens++] = 11;
363 device->axis_map[tokens] = 11;
364 pov++;
365 } else {
366 ERR("invalid joystick axis type: %s\n", ptr);
367 device->axis_map[tokens] = tokens;
368 axis++;
371 tokens++;
372 } while ((ptr = strtok(NULL, delim)) != NULL);
374 if (tokens != device->devcaps.dwAxes) {
375 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", device->axes, axis, pov,tokens);
376 while (tokens < device->axes) {
377 device->axis_map[tokens] = tokens;
378 tokens++;
383 device->devcaps.dwAxes = axis;
384 device->devcaps.dwPOVs = pov;
387 if (appkey)
388 RegCloseKey( appkey );
390 if (hkey)
391 RegCloseKey( hkey );
393 return DI_OK;
396 static void calculate_ids(JoystickImpl* device)
398 int i;
399 int axis = 0;
400 int button = 0;
401 int pov = 0;
402 int axis_base;
403 int pov_base;
404 int button_base;
406 /* Make two passes over the format. The first counts the number
407 * for each type and the second sets the id */
408 for (i = 0; i < device->user_df->dwNumObjs; i++) {
409 if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS)
410 axis++;
411 else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV)
412 pov++;
413 else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
414 button++;
417 axis_base = 0;
418 pov_base = axis;
419 button_base = axis + pov;
421 axis = 0;
422 button = 0;
423 pov = 0;
425 for (i = 0; i < device->user_df->dwNumObjs; i++) {
426 DWORD type = 0;
427 if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_AXIS) {
428 axis++;
429 type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
430 DIDFT_MAKEINSTANCE(axis + axis_base);
431 TRACE("axis type = 0x%08x\n", type);
432 } else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_POV) {
433 pov++;
434 type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
435 DIDFT_MAKEINSTANCE(pov + pov_base);
436 TRACE("POV type = 0x%08x\n", type);
437 } else if (DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) & DIDFT_BUTTON) {
438 button++;
439 type = DIDFT_GETTYPE(device->user_df->rgodf[i].dwType) |
440 DIDFT_MAKEINSTANCE(button + button_base);
441 TRACE("button type = 0x%08x\n", type);
443 device->user_df->rgodf[i].dwType = type;
447 static HRESULT alloc_device(REFGUID rguid, const void *jvt, IDirectInputImpl *dinput, LPDIRECTINPUTDEVICEA* pdev)
449 DWORD i;
450 JoystickImpl* newDevice;
451 char name[MAX_PATH];
452 HRESULT hr;
454 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl));
455 if (newDevice == 0) {
456 WARN("out of memory\n");
457 *pdev = 0;
458 return DIERR_OUTOFMEMORY;
461 if ((newDevice->joyfd = joydev_get_device(newDevice->dev, rguid->Data3)) < 0) {
462 WARN("open(%s,O_RDONLY) failed: %s\n", newDevice->dev, strerror(errno));
463 HeapFree(GetProcessHeap(), 0, newDevice);
464 return DIERR_DEVICENOTREG;
467 /* get the device name */
468 #if defined(JSIOCGNAME)
469 if (ioctl(newDevice->joyfd,JSIOCGNAME(MAX_PATH),name) < 0) {
470 WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", newDevice->dev, strerror(errno));
471 strcpy(name, "Wine Joystick");
473 #else
474 strcpy(name, "Wine Joystick");
475 #endif
477 /* copy the device name */
478 newDevice->name = HeapAlloc(GetProcessHeap(),0,strlen(name) + 1);
479 strcpy(newDevice->name, name);
481 #ifdef JSIOCGAXES
482 if (ioctl(newDevice->joyfd,JSIOCGAXES,&newDevice->axes) < 0) {
483 WARN("ioctl(%s,JSIOCGAXES) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
484 newDevice->axes = 2;
486 #endif
487 #ifdef JSIOCGBUTTONS
488 if (ioctl(newDevice->joyfd,JSIOCGBUTTONS,&newDevice->buttons) < 0) {
489 WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defauting to 2\n", newDevice->dev, strerror(errno));
490 newDevice->buttons = 2;
492 #endif
494 newDevice->base.lpVtbl = jvt;
495 newDevice->base.ref = 1;
496 newDevice->dinput = dinput;
497 CopyMemory(&newDevice->base.guid, rguid, sizeof(*rguid));
498 InitializeCriticalSection(&newDevice->base.crit);
499 newDevice->base.crit.DebugInfo->Spare[0] = (DWORD_PTR)"DINPUT_joystick";
501 /* setup_dinput_options may change these */
502 newDevice->deadzone = 5000;
503 newDevice->devcaps.dwButtons = newDevice->buttons;
504 newDevice->devcaps.dwAxes = newDevice->axes;
505 newDevice->devcaps.dwPOVs = 0;
507 /* do any user specified configuration */
508 hr = setup_dinput_options(newDevice);
509 if (hr != DI_OK)
510 goto FAILED1;
512 if (newDevice->axis_map == 0) {
513 newDevice->axis_map = HeapAlloc(GetProcessHeap(), 0, newDevice->axes * sizeof(int));
514 if (newDevice->axis_map == 0)
515 goto FAILED;
517 for (i = 0; i < newDevice->axes; i++)
518 newDevice->axis_map[i] = i;
521 /* wine uses DIJOYSTATE2 as it's internal format so copy
522 * the already defined format c_dfDIJoystick2 */
523 newDevice->user_df = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwSize);
524 if (newDevice->user_df == 0)
525 goto FAILED;
527 CopyMemory(newDevice->user_df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
529 /* copy default objects */
530 newDevice->user_df->rgodf = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
531 if (newDevice->user_df->rgodf == 0)
532 goto FAILED;
534 CopyMemory(newDevice->user_df->rgodf,c_dfDIJoystick2.rgodf,c_dfDIJoystick2.dwNumObjs*c_dfDIJoystick2.dwObjSize);
536 /* create default properties */
537 newDevice->props = HeapAlloc(GetProcessHeap(),0,c_dfDIJoystick2.dwNumObjs*sizeof(ObjProps));
538 if (newDevice->props == 0)
539 goto FAILED;
541 /* initialize default properties */
542 for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
543 newDevice->props[i].lMin = 0;
544 newDevice->props[i].lMax = 0xffff;
545 newDevice->props[i].lDeadZone = newDevice->deadzone; /* % * 1000 */
546 newDevice->props[i].lSaturation = 0;
549 /* create an offsets array */
550 newDevice->offsets = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,c_dfDIJoystick2.dwNumObjs*sizeof(int));
551 if (newDevice->offsets == 0)
552 goto FAILED;
554 /* create the default transform filter */
555 newDevice->transform = create_DataFormat(&c_dfDIJoystick2, newDevice->user_df, newDevice->offsets);
557 calculate_ids(newDevice);
559 IDirectInputDevice_AddRef((LPDIRECTINPUTDEVICE8A)newDevice->dinput);
561 newDevice->devcaps.dwSize = sizeof(newDevice->devcaps);
562 newDevice->devcaps.dwFlags = DIDC_ATTACHED;
563 if (newDevice->dinput->dwVersion >= 0x0800)
564 newDevice->devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
565 else
566 newDevice->devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
567 newDevice->devcaps.dwFFSamplePeriod = 0;
568 newDevice->devcaps.dwFFMinTimeResolution = 0;
569 newDevice->devcaps.dwFirmwareRevision = 0;
570 newDevice->devcaps.dwHardwareRevision = 0;
571 newDevice->devcaps.dwFFDriverVersion = 0;
573 if (TRACE_ON(dinput)) {
574 _dump_DIDATAFORMAT(newDevice->user_df);
575 for (i = 0; i < (newDevice->axes); i++)
576 TRACE("axis_map[%d] = %d\n", i, newDevice->axis_map[i]);
577 _dump_DIDEVCAPS(&newDevice->devcaps);
580 *pdev = (LPDIRECTINPUTDEVICEA)newDevice;
582 return DI_OK;
584 FAILED:
585 hr = DIERR_OUTOFMEMORY;
586 FAILED1:
587 HeapFree(GetProcessHeap(),0,newDevice->axis_map);
588 HeapFree(GetProcessHeap(),0,newDevice->name);
589 HeapFree(GetProcessHeap(),0,newDevice->props);
590 HeapFree(GetProcessHeap(),0,newDevice->user_df->rgodf);
591 HeapFree(GetProcessHeap(),0,newDevice->user_df);
592 HeapFree(GetProcessHeap(),0,newDevice);
593 *pdev = 0;
595 return hr;
598 static BOOL IsJoystickGUID(REFGUID guid)
600 GUID wine_joystick = DInput_Wine_Joystick_GUID;
601 GUID dev_guid = *guid;
603 wine_joystick.Data3 = 0;
604 dev_guid.Data3 = 0;
606 return IsEqualGUID(&wine_joystick, &dev_guid);
609 static HRESULT joydev_create_deviceA(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEA* pdev)
611 if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
612 (IsJoystickGUID(rguid))) {
613 if ((riid == NULL) ||
614 IsEqualGUID(&IID_IDirectInputDeviceA,riid) ||
615 IsEqualGUID(&IID_IDirectInputDevice2A,riid) ||
616 IsEqualGUID(&IID_IDirectInputDevice7A,riid) ||
617 IsEqualGUID(&IID_IDirectInputDevice8A,riid)) {
618 return alloc_device(rguid, &JoystickAvt, dinput, pdev);
619 } else {
620 WARN("no interface\n");
621 *pdev = 0;
622 return DIERR_NOINTERFACE;
626 WARN("invalid device GUID\n");
627 *pdev = 0;
628 return DIERR_DEVICENOTREG;
631 static HRESULT joydev_create_deviceW(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPDIRECTINPUTDEVICEW* pdev)
633 if ((IsEqualGUID(&GUID_Joystick,rguid)) ||
634 (IsJoystickGUID(rguid))) {
635 if ((riid == NULL) ||
636 IsEqualGUID(&IID_IDirectInputDeviceW,riid) ||
637 IsEqualGUID(&IID_IDirectInputDevice2W,riid) ||
638 IsEqualGUID(&IID_IDirectInputDevice7W,riid) ||
639 IsEqualGUID(&IID_IDirectInputDevice8W,riid)) {
640 return alloc_device(rguid, &JoystickWvt, dinput, (LPDIRECTINPUTDEVICEA *)pdev);
641 } else {
642 WARN("no interface\n");
643 *pdev = 0;
644 return DIERR_NOINTERFACE;
648 WARN("invalid device GUID\n");
649 *pdev = 0;
650 return DIERR_DEVICENOTREG;
653 const struct dinput_device joystick_linux_device = {
654 "Wine Linux joystick driver",
655 joydev_enum_deviceA,
656 joydev_enum_deviceW,
657 joydev_create_deviceA,
658 joydev_create_deviceW
661 /******************************************************************************
662 * Joystick
664 static ULONG WINAPI JoystickAImpl_Release(LPDIRECTINPUTDEVICE8A iface)
666 JoystickImpl *This = (JoystickImpl *)iface;
667 ULONG ref;
669 ref = InterlockedDecrement(&This->base.ref);
670 if (ref)
671 return ref;
673 /* Free the device name */
674 HeapFree(GetProcessHeap(),0,This->name);
676 /* Free the axis map */
677 HeapFree(GetProcessHeap(),0,This->axis_map);
679 /* Free the data queue */
680 HeapFree(GetProcessHeap(), 0, This->base.data_queue);
682 /* Free the DataFormat */
683 HeapFree(GetProcessHeap(), 0, This->user_df->rgodf);
684 HeapFree(GetProcessHeap(), 0, This->user_df);
686 /* Free the properties */
687 HeapFree(GetProcessHeap(), 0, This->props);
689 /* Free the offsets array */
690 HeapFree(GetProcessHeap(),0,This->offsets);
692 /* release the data transform filter */
693 release_DataFormat(This->transform);
695 This->base.crit.DebugInfo->Spare[0] = 0;
696 DeleteCriticalSection(&This->base.crit);
697 IDirectInputDevice_Release((LPDIRECTINPUTDEVICE8A)This->dinput);
699 HeapFree(GetProcessHeap(),0,This);
700 return 0;
703 /******************************************************************************
704 * SetDataFormat : the application can choose the format of the data
705 * the device driver sends back with GetDeviceState.
707 static HRESULT WINAPI JoystickAImpl_SetDataFormat(
708 LPDIRECTINPUTDEVICE8A iface,
709 LPCDIDATAFORMAT df)
711 JoystickImpl *This = (JoystickImpl *)iface;
712 unsigned int i;
713 LPDIDATAFORMAT new_df = 0;
714 LPDIOBJECTDATAFORMAT new_rgodf = 0;
715 ObjProps * new_props = 0;
717 TRACE("(%p,%p)\n",This,df);
719 if (df == NULL) {
720 WARN("invalid pointer\n");
721 return E_POINTER;
724 if (df->dwSize != sizeof(*df)) {
725 WARN("invalid argument\n");
726 return DIERR_INVALIDPARAM;
729 if (This->base.acquired) {
730 WARN("acquired\n");
731 return DIERR_ACQUIRED;
734 if (TRACE_ON(dinput))
735 _dump_DIDATAFORMAT(df);
737 /* Store the new data format */
738 new_df = HeapAlloc(GetProcessHeap(),0,df->dwSize);
739 if (new_df == 0)
740 goto FAILED;
742 new_rgodf = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*df->dwObjSize);
743 if (new_rgodf == 0)
744 goto FAILED;
746 new_props = HeapAlloc(GetProcessHeap(),0,df->dwNumObjs*sizeof(ObjProps));
747 if (new_props == 0)
748 goto FAILED;
750 HeapFree(GetProcessHeap(),0,This->user_df);
751 HeapFree(GetProcessHeap(),0,This->user_df->rgodf);
752 HeapFree(GetProcessHeap(),0,This->props);
753 release_DataFormat(This->transform);
755 This->user_df = new_df;
756 CopyMemory(This->user_df, df, df->dwSize);
757 This->user_df->rgodf = new_rgodf;
758 CopyMemory(This->user_df->rgodf,df->rgodf,df->dwNumObjs*df->dwObjSize);
759 This->props = new_props;
760 for (i = 0; i < df->dwNumObjs; i++) {
761 This->props[i].lMin = 0;
762 This->props[i].lMax = 0xffff;
763 This->props[i].lDeadZone = 1000;
764 This->props[i].lSaturation = 0;
766 This->transform = create_DataFormat(&c_dfDIJoystick2, This->user_df, This->offsets);
768 calculate_ids(This);
770 return DI_OK;
772 FAILED:
773 WARN("out of memory\n");
774 HeapFree(GetProcessHeap(),0,new_props);
775 HeapFree(GetProcessHeap(),0,new_rgodf);
776 HeapFree(GetProcessHeap(),0,new_df);
777 return DIERR_OUTOFMEMORY;
780 /******************************************************************************
781 * Acquire : gets exclusive control of the joystick
783 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
785 JoystickImpl *This = (JoystickImpl *)iface;
787 TRACE("(%p)\n",This);
789 if (This->base.acquired) {
790 WARN("already acquired\n");
791 return S_FALSE;
794 /* open the joystick device */
795 if (This->joyfd==-1) {
796 TRACE("opening joystick device %s\n", This->dev);
798 This->joyfd=open(This->dev,O_RDONLY);
799 if (This->joyfd==-1) {
800 ERR("open(%s) failed: %s\n", This->dev, strerror(errno));
801 return DIERR_NOTFOUND;
805 This->base.acquired = 1;
807 return DI_OK;
810 /******************************************************************************
811 * Unacquire : frees the joystick
813 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
815 JoystickImpl *This = (JoystickImpl *)iface;
816 HRESULT res;
818 TRACE("(%p)\n",This);
820 if ((res = IDirectInputDevice2AImpl_Unacquire(iface)) != DI_OK) return res;
822 if (This->joyfd!=-1) {
823 TRACE("closing joystick device\n");
824 close(This->joyfd);
825 This->joyfd = -1;
826 return DI_OK;
829 return DI_NOEFFECT;
832 static LONG map_axis(JoystickImpl * This, short val, short index)
834 double fval = val;
835 double fmin = This->props[index].lMin;
836 double fmax = This->props[index].lMax;
837 double fret;
839 fret = (((fval + 32767.0) * (fmax - fmin)) / (32767.0*2.0)) + fmin;
841 if (fret >= 0.0)
842 fret += 0.5;
843 else
844 fret -= 0.5;
846 return fret;
849 /* convert wine format offset to user format object index */
850 static int offset_to_object(JoystickImpl *This, int offset)
852 int i;
854 for (i = 0; i < This->user_df->dwNumObjs; i++) {
855 if (This->user_df->rgodf[i].dwOfs == offset)
856 return i;
859 return -1;
862 static LONG calculate_pov(JoystickImpl *This, int index)
864 if (This->povs[index].lX < -16384) {
865 if (This->povs[index].lY < -16384)
866 This->js.rgdwPOV[index] = 31500;
867 else if (This->povs[index].lY > 16384)
868 This->js.rgdwPOV[index] = 22500;
869 else
870 This->js.rgdwPOV[index] = 27000;
871 } else if (This->povs[index].lX > 16384) {
872 if (This->povs[index].lY < -16384)
873 This->js.rgdwPOV[index] = 4500;
874 else if (This->povs[index].lY > 16384)
875 This->js.rgdwPOV[index] = 13500;
876 else
877 This->js.rgdwPOV[index] = 9000;
878 } else {
879 if (This->povs[index].lY < -16384)
880 This->js.rgdwPOV[index] = 0;
881 else if (This->povs[index].lY > 16384)
882 This->js.rgdwPOV[index] = 18000;
883 else
884 This->js.rgdwPOV[index] = -1;
887 return This->js.rgdwPOV[index];
890 static void joy_polldev(JoystickImpl *This) {
891 struct pollfd plfd;
892 struct js_event jse;
893 TRACE("(%p)\n", This);
895 if (This->joyfd==-1) {
896 WARN("no device\n");
897 return;
899 while (1) {
900 plfd.fd = This->joyfd;
901 plfd.events = POLLIN;
902 if (poll(&plfd,1,0) != 1)
903 return;
904 /* we have one event, so we can read */
905 if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
906 return;
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;
914 This->js.rgbButtons[jse.number] = value;
915 queue_event((LPDIRECTINPUTDEVICE8A)This, 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);
923 /* FIXME do deadzone and saturation here */
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 = jse.value;
955 else if (This->axis_map[jse.number - 1] == number)
956 This->povs[0].lY = jse.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 = jse.value;
962 else if (This->axis_map[jse.number - 1] == number)
963 This->povs[1].lY = jse.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 = jse.value;
969 else if (This->axis_map[jse.number - 1] == number)
970 This->povs[2].lY = jse.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 = jse.value;
976 else if (This->axis_map[jse.number - 1] == number)
977 This->povs[3].lY = jse.value;
978 value = calculate_pov(This, 3);
979 break;
982 queue_event((LPDIRECTINPUTDEVICE8A)This, offset, value, jse.time, This->dinput->evsequence++);
983 } else
984 WARN("axis %d not supported\n", number);
989 /******************************************************************************
990 * GetDeviceState : returns the "state" of the joystick.
993 static HRESULT WINAPI JoystickAImpl_GetDeviceState(
994 LPDIRECTINPUTDEVICE8A iface,
995 DWORD len,
996 LPVOID ptr)
998 JoystickImpl *This = (JoystickImpl *)iface;
1000 TRACE("(%p,0x%08x,%p)\n", This, len, ptr);
1002 if (!This->base.acquired) {
1003 WARN("not acquired\n");
1004 return DIERR_NOTACQUIRED;
1007 /* update joystick state */
1008 joy_polldev(This);
1010 /* convert and copy data to user supplied buffer */
1011 fill_DataFormat(ptr, &This->js, This->transform);
1013 return DI_OK;
1016 static int find_property(JoystickImpl * This, LPCDIPROPHEADER ph)
1018 int i;
1019 if (ph->dwHow == DIPH_BYOFFSET) {
1020 return offset_to_object(This, ph->dwObj);
1021 } else if (ph->dwHow == DIPH_BYID) {
1022 for (i = 0; i < This->user_df->dwNumObjs; i++) {
1023 if ((This->user_df->rgodf[i].dwType & 0x00ffffff) == (ph->dwObj & 0x00ffffff)) {
1024 return i;
1029 return -1;
1032 /******************************************************************************
1033 * SetProperty : change input device properties
1035 static HRESULT WINAPI JoystickAImpl_SetProperty(
1036 LPDIRECTINPUTDEVICE8A iface,
1037 REFGUID rguid,
1038 LPCDIPROPHEADER ph)
1040 JoystickImpl *This = (JoystickImpl *)iface;
1041 int i;
1043 TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
1045 if (ph == NULL) {
1046 WARN("invalid parameter: ph == NULL\n");
1047 return DIERR_INVALIDPARAM;
1050 if (TRACE_ON(dinput))
1051 _dump_DIPROPHEADER(ph);
1053 if (!HIWORD(rguid)) {
1054 switch (LOWORD(rguid)) {
1055 case (DWORD)DIPROP_RANGE: {
1056 LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph;
1057 if (ph->dwHow == DIPH_DEVICE) {
1058 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax);
1059 for (i = 0; i < This->user_df->dwNumObjs; i++) {
1060 This->props[i].lMin = pr->lMin;
1061 This->props[i].lMax = pr->lMax;
1063 } else {
1064 int obj = find_property(This, ph);
1065 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj);
1066 if (obj >= 0) {
1067 This->props[obj].lMin = pr->lMin;
1068 This->props[obj].lMax = pr->lMax;
1069 return DI_OK;
1072 break;
1074 case (DWORD)DIPROP_DEADZONE: {
1075 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
1076 if (ph->dwHow == DIPH_DEVICE) {
1077 TRACE("deadzone(%d) all\n", pd->dwData);
1078 for (i = 0; i < This->user_df->dwNumObjs; i++)
1079 This->props[i].lDeadZone = pd->dwData;
1080 } else {
1081 int obj = find_property(This, ph);
1082 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
1083 if (obj >= 0) {
1084 This->props[obj].lDeadZone = pd->dwData;
1085 return DI_OK;
1088 break;
1090 case (DWORD)DIPROP_SATURATION: {
1091 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
1092 if (ph->dwHow == DIPH_DEVICE) {
1093 TRACE("saturation(%d) all\n", pd->dwData);
1094 for (i = 0; i < This->user_df->dwNumObjs; i++)
1095 This->props[i].lSaturation = pd->dwData;
1096 } else {
1097 int obj = find_property(This, ph);
1098 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
1099 if (obj >= 0) {
1100 This->props[obj].lSaturation = pd->dwData;
1101 return DI_OK;
1104 break;
1106 default:
1107 return IDirectInputDevice2AImpl_SetProperty(iface, rguid, ph);
1111 return DI_OK;
1114 static HRESULT WINAPI JoystickAImpl_GetCapabilities(
1115 LPDIRECTINPUTDEVICE8A iface,
1116 LPDIDEVCAPS lpDIDevCaps)
1118 JoystickImpl *This = (JoystickImpl *)iface;
1119 int size;
1121 TRACE("%p->(%p)\n",iface,lpDIDevCaps);
1123 if (lpDIDevCaps == NULL) {
1124 WARN("invalid pointer\n");
1125 return E_POINTER;
1128 size = lpDIDevCaps->dwSize;
1130 if (!(size == sizeof(DIDEVCAPS) || size == sizeof(DIDEVCAPS_DX3))) {
1131 WARN("invalid parameter\n");
1132 return DIERR_INVALIDPARAM;
1135 CopyMemory(lpDIDevCaps, &This->devcaps, size);
1136 lpDIDevCaps->dwSize = size;
1138 if (TRACE_ON(dinput))
1139 _dump_DIDEVCAPS(lpDIDevCaps);
1141 return DI_OK;
1144 static HRESULT WINAPI JoystickAImpl_Poll(LPDIRECTINPUTDEVICE8A iface)
1146 JoystickImpl *This = (JoystickImpl *)iface;
1148 TRACE("(%p)\n",This);
1150 if (!This->base.acquired) {
1151 WARN("not acquired\n");
1152 return DIERR_NOTACQUIRED;
1155 joy_polldev(This);
1156 return DI_OK;
1159 /******************************************************************************
1160 * EnumObjects : enumerate the different buttons and axis...
1162 static HRESULT WINAPI JoystickAImpl_EnumObjects(
1163 LPDIRECTINPUTDEVICE8A iface,
1164 LPDIENUMDEVICEOBJECTSCALLBACKA lpCallback,
1165 LPVOID lpvRef,
1166 DWORD dwFlags)
1168 JoystickImpl *This = (JoystickImpl *)iface;
1169 DIDEVICEOBJECTINSTANCEA ddoi;
1170 BYTE i;
1171 int user_offset;
1172 int user_object;
1174 TRACE("(this=%p,%p,%p,%08x)\n", This, lpCallback, lpvRef, dwFlags);
1175 if (TRACE_ON(dinput)) {
1176 TRACE(" - flags = ");
1177 _dump_EnumObjects_flags(dwFlags);
1178 TRACE("\n");
1181 /* Only the fields till dwFFMaxForce are relevant */
1182 ddoi.dwSize = FIELD_OFFSET(DIDEVICEOBJECTINSTANCEA, dwFFMaxForce);
1184 /* For the joystick, do as is done in the GetCapabilities function */
1185 if ((dwFlags == DIDFT_ALL) ||
1186 (dwFlags & DIDFT_AXIS) ||
1187 (dwFlags & DIDFT_POV)) {
1188 int pov[4] = { 0, 0, 0, 0 };
1189 int axes = 0;
1190 int povs = 0;
1192 for (i = 0; i < This->axes; i++) {
1193 int wine_obj = This->axis_map[i];
1194 BOOL skip = FALSE;
1196 switch (wine_obj) {
1197 case 0:
1198 ddoi.guidType = GUID_XAxis;
1199 break;
1200 case 1:
1201 ddoi.guidType = GUID_YAxis;
1202 break;
1203 case 2:
1204 ddoi.guidType = GUID_ZAxis;
1205 break;
1206 case 3:
1207 ddoi.guidType = GUID_RxAxis;
1208 break;
1209 case 4:
1210 ddoi.guidType = GUID_RyAxis;
1211 break;
1212 case 5:
1213 ddoi.guidType = GUID_RzAxis;
1214 break;
1215 case 6:
1216 ddoi.guidType = GUID_Slider;
1217 break;
1218 case 7:
1219 ddoi.guidType = GUID_Slider;
1220 break;
1221 case 8:
1222 pov[0]++;
1223 ddoi.guidType = GUID_POV;
1224 break;
1225 case 9:
1226 pov[1]++;
1227 ddoi.guidType = GUID_POV;
1228 break;
1229 case 10:
1230 pov[2]++;
1231 ddoi.guidType = GUID_POV;
1232 break;
1233 case 11:
1234 pov[3]++;
1235 ddoi.guidType = GUID_POV;
1236 break;
1237 default:
1238 ddoi.guidType = GUID_Unknown;
1240 if (wine_obj < 8) {
1241 user_offset = This->offsets[wine_obj]; /* get user offset from wine index */
1242 user_object = offset_to_object(This, user_offset);
1244 ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
1245 ddoi.dwOfs = This->user_df->rgodf[user_object].dwOfs;
1246 sprintf(ddoi.tszName, "Axis %d", axes);
1247 axes++;
1248 } else {
1249 if (pov[wine_obj - 8] < 2) {
1250 user_offset = This->offsets[wine_obj]; /* get user offset from wine index */
1251 user_object = offset_to_object(This, user_offset);
1253 ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
1254 ddoi.dwOfs = This->user_df->rgodf[user_object].dwOfs;
1255 sprintf(ddoi.tszName, "POV %d", povs);
1256 povs++;
1257 } else
1258 skip = TRUE;
1260 if (!skip) {
1261 _dump_OBJECTINSTANCEA(&ddoi);
1262 if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE)
1263 return DI_OK;
1268 if ((dwFlags == DIDFT_ALL) ||
1269 (dwFlags & DIDFT_BUTTON)) {
1271 /* The DInput SDK says that GUID_Button is only for mouse buttons but well... */
1272 ddoi.guidType = GUID_Button;
1274 for (i = 0; i < This->buttons; i++) {
1275 user_offset = This->offsets[i + 12]; /* get user offset from wine index */
1276 user_object = offset_to_object(This, user_offset);
1277 ddoi.guidType = GUID_Button;
1278 ddoi.dwType = This->user_df->rgodf[user_object].dwType & 0x00ffffff;
1279 ddoi.dwOfs = This->user_df->rgodf[user_object].dwOfs;
1280 sprintf(ddoi.tszName, "Button %d", i);
1281 _dump_OBJECTINSTANCEA(&ddoi);
1282 if (lpCallback(&ddoi, lpvRef) != DIENUM_CONTINUE) return DI_OK;
1286 return DI_OK;
1289 /******************************************************************************
1290 * EnumObjects : enumerate the different buttons and axis...
1292 static HRESULT WINAPI JoystickWImpl_EnumObjects(
1293 LPDIRECTINPUTDEVICE8W iface,
1294 LPDIENUMDEVICEOBJECTSCALLBACKW lpCallback,
1295 LPVOID lpvRef,
1296 DWORD dwFlags)
1298 JoystickImpl *This = (JoystickImpl *)iface;
1300 device_enumobjects_AtoWcb_data data;
1302 data.lpCallBack = lpCallback;
1303 data.lpvRef = lpvRef;
1305 return JoystickAImpl_EnumObjects((LPDIRECTINPUTDEVICE8A) This, (LPDIENUMDEVICEOBJECTSCALLBACKA) DIEnumDevicesCallbackAtoW, (LPVOID) &data, dwFlags);
1308 /******************************************************************************
1309 * GetProperty : get input device properties
1311 static HRESULT WINAPI JoystickAImpl_GetProperty(
1312 LPDIRECTINPUTDEVICE8A iface,
1313 REFGUID rguid,
1314 LPDIPROPHEADER pdiph)
1316 JoystickImpl *This = (JoystickImpl *)iface;
1318 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
1320 if (TRACE_ON(dinput))
1321 _dump_DIPROPHEADER(pdiph);
1323 if (!HIWORD(rguid)) {
1324 switch (LOWORD(rguid)) {
1325 case (DWORD) DIPROP_RANGE: {
1326 LPDIPROPRANGE pr = (LPDIPROPRANGE) pdiph;
1327 int obj = find_property(This, pdiph);
1328 /* The app is querying the current range of the axis
1329 * return the lMin and lMax values */
1330 if (obj >= 0) {
1331 pr->lMin = This->props[obj].lMin;
1332 pr->lMax = This->props[obj].lMax;
1333 TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj);
1334 return DI_OK;
1336 break;
1338 case (DWORD) DIPROP_DEADZONE: {
1339 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1340 int obj = find_property(This, pdiph);
1341 if (obj >= 0) {
1342 pd->dwData = This->props[obj].lDeadZone;
1343 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj);
1344 return DI_OK;
1346 break;
1348 case (DWORD) DIPROP_SATURATION: {
1349 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1350 int obj = find_property(This, pdiph);
1351 if (obj >= 0) {
1352 pd->dwData = This->props[obj].lSaturation;
1353 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj);
1354 return DI_OK;
1356 break;
1358 default:
1359 return IDirectInputDevice2AImpl_GetProperty(iface, rguid, pdiph);
1363 return DI_OK;
1366 /******************************************************************************
1367 * GetObjectInfo : get object info
1369 HRESULT WINAPI JoystickAImpl_GetObjectInfo(
1370 LPDIRECTINPUTDEVICE8A iface,
1371 LPDIDEVICEOBJECTINSTANCEA pdidoi,
1372 DWORD dwObj,
1373 DWORD dwHow)
1375 JoystickImpl *This = (JoystickImpl *)iface;
1376 DIDEVICEOBJECTINSTANCEA didoiA;
1377 unsigned int i;
1379 TRACE("(%p,%p,%d,0x%08x(%s))\n",
1380 iface, pdidoi, dwObj, dwHow,
1381 dwHow == DIPH_BYOFFSET ? "DIPH_BYOFFSET" :
1382 dwHow == DIPH_BYID ? "DIPH_BYID" :
1383 dwHow == DIPH_BYUSAGE ? "DIPH_BYUSAGE" :
1384 "UNKNOWN");
1386 if (pdidoi == NULL) {
1387 WARN("invalid parameter: pdidoi = NULL\n");
1388 return DIERR_INVALIDPARAM;
1391 if ((pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCEA)) &&
1392 (pdidoi->dwSize != sizeof(DIDEVICEOBJECTINSTANCE_DX3A))) {
1393 WARN("invalid parameter: pdidoi->dwSize = %d != %d or %d\n",
1394 pdidoi->dwSize, sizeof(DIDEVICEOBJECTINSTANCEA),
1395 sizeof(DIDEVICEOBJECTINSTANCE_DX3A));
1396 return DIERR_INVALIDPARAM;
1399 ZeroMemory(&didoiA, sizeof(didoiA));
1400 didoiA.dwSize = pdidoi->dwSize;
1402 switch (dwHow) {
1403 case DIPH_BYOFFSET: {
1404 int axis = 0;
1405 int pov = 0;
1406 int button = 0;
1407 for (i = 0; i < This->user_df->dwNumObjs; i++) {
1408 if (This->user_df->rgodf[i].dwOfs == dwObj) {
1409 if (This->user_df->rgodf[i].pguid)
1410 didoiA.guidType = *This->user_df->rgodf[i].pguid;
1411 else
1412 didoiA.guidType = GUID_NULL;
1414 didoiA.dwOfs = dwObj;
1415 didoiA.dwType = This->user_df->rgodf[i].dwType;
1416 didoiA.dwFlags = This->user_df->rgodf[i].dwFlags;
1418 if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_AXIS)
1419 sprintf(didoiA.tszName, "Axis %d", axis);
1420 else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_POV)
1421 sprintf(didoiA.tszName, "POV %d", pov);
1422 else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
1423 sprintf(didoiA.tszName, "Button %d", button);
1425 CopyMemory(pdidoi, &didoiA, pdidoi->dwSize);
1426 return DI_OK;
1429 if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_AXIS)
1430 axis++;
1431 else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_POV)
1432 pov++;
1433 else if (DIDFT_GETTYPE(This->user_df->rgodf[i].dwType) & DIDFT_BUTTON)
1434 button++;
1436 break;
1438 case DIPH_BYID:
1439 FIXME("dwHow = DIPH_BYID not implemented\n");
1440 break;
1441 case DIPH_BYUSAGE:
1442 FIXME("dwHow = DIPH_BYUSAGE not implemented\n");
1443 break;
1444 default:
1445 WARN("invalid parameter: dwHow = %08x\n", dwHow);
1446 return DIERR_INVALIDPARAM;
1449 CopyMemory(pdidoi, &didoiA, pdidoi->dwSize);
1451 return DI_OK;
1454 /******************************************************************************
1455 * GetDeviceInfo : get information about a device's identity
1457 HRESULT WINAPI JoystickAImpl_GetDeviceInfo(
1458 LPDIRECTINPUTDEVICE8A iface,
1459 LPDIDEVICEINSTANCEA pdidi)
1461 JoystickImpl *This = (JoystickImpl *)iface;
1463 TRACE("(%p,%p)\n", iface, pdidi);
1465 if (pdidi == NULL) {
1466 WARN("invalid pointer\n");
1467 return E_POINTER;
1470 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
1471 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) {
1472 WARN("invalid parameter: pdidi->dwSize = %d != %d or %d\n",
1473 pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3A),
1474 sizeof(DIDEVICEINSTANCEA));
1475 return DIERR_INVALIDPARAM;
1478 /* Return joystick */
1479 pdidi->guidInstance = GUID_Joystick;
1480 pdidi->guidProduct = DInput_Wine_Joystick_GUID;
1481 /* we only support traditional joysticks for now */
1482 pdidi->dwDevType = This->devcaps.dwDevType;
1483 strcpy(pdidi->tszInstanceName, "Joystick");
1484 strcpy(pdidi->tszProductName, This->name);
1485 if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3A)) {
1486 pdidi->guidFFDriver = GUID_NULL;
1487 pdidi->wUsagePage = 0;
1488 pdidi->wUsage = 0;
1491 return DI_OK;
1494 /******************************************************************************
1495 * GetDeviceInfo : get information about a device's identity
1497 HRESULT WINAPI JoystickWImpl_GetDeviceInfo(
1498 LPDIRECTINPUTDEVICE8W iface,
1499 LPDIDEVICEINSTANCEW pdidi)
1501 JoystickImpl *This = (JoystickImpl *)iface;
1503 TRACE("(%p,%p)\n", iface, pdidi);
1505 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
1506 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) {
1507 WARN("invalid parameter: pdidi->dwSize = %d != %d or %d\n",
1508 pdidi->dwSize, sizeof(DIDEVICEINSTANCE_DX3W),
1509 sizeof(DIDEVICEINSTANCEW));
1510 return DIERR_INVALIDPARAM;
1513 /* Return joystick */
1514 pdidi->guidInstance = GUID_Joystick;
1515 pdidi->guidProduct = DInput_Wine_Joystick_GUID;
1516 /* we only support traditional joysticks for now */
1517 pdidi->dwDevType = This->devcaps.dwDevType;
1518 MultiByteToWideChar(CP_ACP, 0, "Joystick", -1, pdidi->tszInstanceName, MAX_PATH);
1519 MultiByteToWideChar(CP_ACP, 0, This->name, -1, pdidi->tszProductName, MAX_PATH);
1520 if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3W)) {
1521 pdidi->guidFFDriver = GUID_NULL;
1522 pdidi->wUsagePage = 0;
1523 pdidi->wUsage = 0;
1526 return DI_OK;
1529 static const IDirectInputDevice8AVtbl JoystickAvt =
1531 IDirectInputDevice2AImpl_QueryInterface,
1532 IDirectInputDevice2AImpl_AddRef,
1533 JoystickAImpl_Release,
1534 JoystickAImpl_GetCapabilities,
1535 JoystickAImpl_EnumObjects,
1536 JoystickAImpl_GetProperty,
1537 JoystickAImpl_SetProperty,
1538 JoystickAImpl_Acquire,
1539 JoystickAImpl_Unacquire,
1540 JoystickAImpl_GetDeviceState,
1541 IDirectInputDevice2AImpl_GetDeviceData,
1542 JoystickAImpl_SetDataFormat,
1543 IDirectInputDevice2AImpl_SetEventNotification,
1544 IDirectInputDevice2AImpl_SetCooperativeLevel,
1545 JoystickAImpl_GetObjectInfo,
1546 JoystickAImpl_GetDeviceInfo,
1547 IDirectInputDevice2AImpl_RunControlPanel,
1548 IDirectInputDevice2AImpl_Initialize,
1549 IDirectInputDevice2AImpl_CreateEffect,
1550 IDirectInputDevice2AImpl_EnumEffects,
1551 IDirectInputDevice2AImpl_GetEffectInfo,
1552 IDirectInputDevice2AImpl_GetForceFeedbackState,
1553 IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1554 IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1555 IDirectInputDevice2AImpl_Escape,
1556 JoystickAImpl_Poll,
1557 IDirectInputDevice2AImpl_SendDeviceData,
1558 IDirectInputDevice7AImpl_EnumEffectsInFile,
1559 IDirectInputDevice7AImpl_WriteEffectToFile,
1560 IDirectInputDevice8AImpl_BuildActionMap,
1561 IDirectInputDevice8AImpl_SetActionMap,
1562 IDirectInputDevice8AImpl_GetImageInfo
1565 #if !defined(__STRICT_ANSI__) && defined(__GNUC__)
1566 # define XCAST(fun) (typeof(SysJoystickWvt.fun))
1567 #else
1568 # define XCAST(fun) (void*)
1569 #endif
1571 static const IDirectInputDevice8WVtbl SysJoystickWvt =
1573 IDirectInputDevice2WImpl_QueryInterface,
1574 XCAST(AddRef)IDirectInputDevice2AImpl_AddRef,
1575 XCAST(Release)JoystickAImpl_Release,
1576 XCAST(GetCapabilities)JoystickAImpl_GetCapabilities,
1577 JoystickWImpl_EnumObjects,
1578 XCAST(GetProperty)JoystickAImpl_GetProperty,
1579 XCAST(SetProperty)JoystickAImpl_SetProperty,
1580 XCAST(Acquire)JoystickAImpl_Acquire,
1581 XCAST(Unacquire)JoystickAImpl_Unacquire,
1582 XCAST(GetDeviceState)JoystickAImpl_GetDeviceState,
1583 XCAST(GetDeviceData)IDirectInputDevice2AImpl_GetDeviceData,
1584 XCAST(SetDataFormat)JoystickAImpl_SetDataFormat,
1585 XCAST(SetEventNotification)IDirectInputDevice2AImpl_SetEventNotification,
1586 XCAST(SetCooperativeLevel)IDirectInputDevice2AImpl_SetCooperativeLevel,
1587 IDirectInputDevice2WImpl_GetObjectInfo,
1588 JoystickWImpl_GetDeviceInfo,
1589 XCAST(RunControlPanel)IDirectInputDevice2AImpl_RunControlPanel,
1590 XCAST(Initialize)IDirectInputDevice2AImpl_Initialize,
1591 XCAST(CreateEffect)IDirectInputDevice2AImpl_CreateEffect,
1592 IDirectInputDevice2WImpl_EnumEffects,
1593 IDirectInputDevice2WImpl_GetEffectInfo,
1594 XCAST(GetForceFeedbackState)IDirectInputDevice2AImpl_GetForceFeedbackState,
1595 XCAST(SendForceFeedbackCommand)IDirectInputDevice2AImpl_SendForceFeedbackCommand,
1596 XCAST(EnumCreatedEffectObjects)IDirectInputDevice2AImpl_EnumCreatedEffectObjects,
1597 XCAST(Escape)IDirectInputDevice2AImpl_Escape,
1598 XCAST(Poll)JoystickAImpl_Poll,
1599 XCAST(SendDeviceData)IDirectInputDevice2AImpl_SendDeviceData,
1600 IDirectInputDevice7WImpl_EnumEffectsInFile,
1601 IDirectInputDevice7WImpl_WriteEffectToFile,
1602 IDirectInputDevice8WImpl_BuildActionMap,
1603 IDirectInputDevice8WImpl_SetActionMap,
1604 IDirectInputDevice8WImpl_GetImageInfo
1606 #undef XCAST
1608 #else /* HAVE_LINUX_22_JOYSTICK_API */
1610 const struct dinput_device joystick_linux_device = {
1611 "Wine Linux joystick driver",
1612 NULL,
1613 NULL,
1614 NULL,
1615 NULL
1618 #endif /* HAVE_LINUX_22_JOYSTICK_API */