msctf: Maintain context reference in ranges.
[wine.git] / dlls / dinput / joystick_linux.c
blob3215978c995ccd903b605ef8792d51b01ad6c934
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 #ifdef HAVE_UNISTD_H
36 # include <unistd.h>
37 #endif
38 #ifdef HAVE_SYS_TIME_H
39 # include <sys/time.h>
40 #endif
41 #include <fcntl.h>
42 #ifdef HAVE_SYS_IOCTL_H
43 # include <sys/ioctl.h>
44 #endif
45 #include <errno.h>
46 #ifdef HAVE_LINUX_IOCTL_H
47 # include <linux/ioctl.h>
48 #endif
49 #ifdef HAVE_LINUX_JOYSTICK_H
50 # include <linux/joystick.h>
51 # undef SW_MAX
52 #endif
53 #ifdef HAVE_SYS_POLL_H
54 # include <sys/poll.h>
55 #endif
57 #include "wine/debug.h"
58 #include "wine/unicode.h"
59 #include "windef.h"
60 #include "winbase.h"
61 #include "winerror.h"
62 #include "devguid.h"
63 #include "dinput.h"
65 #include "dinput_private.h"
66 #include "device_private.h"
67 #include "joystick_private.h"
69 #ifdef HAVE_LINUX_22_JOYSTICK_API
71 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
73 #define JOYDEV_NEW "/dev/input/js"
74 #define JOYDEV_OLD "/dev/js"
75 #define JOYDEVDRIVER " (js)"
77 struct JoyDev
79 char device[MAX_PATH];
80 char name[MAX_PATH];
81 GUID guid_product;
83 BYTE axis_count;
84 BYTE button_count;
85 int *dev_axes_map;
87 WORD vendor_id, product_id, bus_type;
89 BOOL is_joystick;
92 typedef struct JoystickImpl JoystickImpl;
93 static const IDirectInputDevice8WVtbl JoystickWvt;
94 struct JoystickImpl
96 struct JoystickGenericImpl generic;
98 struct JoyDev *joydev;
100 /* joystick private */
101 int joyfd;
102 POINTL povs[4];
105 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
107 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
108 JoystickGenericImpl, base), JoystickImpl, generic);
111 static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */
112 0x9e573ed9,
113 0x7734,
114 0x11d2,
115 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
118 #define MAX_JOYSTICKS 64
119 static INT joystick_devices_count = -1;
120 static struct JoyDev *joystick_devices;
122 static void joy_polldev( IDirectInputDevice8W *iface );
124 #define SYS_PATH_FORMAT "/sys/class/input/js%d/device/id/%s"
125 static BOOL read_sys_id_variable(int index, const char *property, WORD *value)
127 char sys_path[sizeof(SYS_PATH_FORMAT) + 16], id_str[5];
128 int sys_fd;
129 BOOL ret = FALSE;
131 sprintf(sys_path, SYS_PATH_FORMAT, index, property);
132 if ((sys_fd = open(sys_path, O_RDONLY)) != -1)
134 if (read(sys_fd, id_str, 4) == 4)
136 id_str[4] = '\0';
137 *value = strtol(id_str, NULL, 16);
138 ret = TRUE;
141 close(sys_fd);
143 return ret;
145 #undef SYS_PATH_FORMAT
147 static INT find_joystick_devices(void)
149 INT i;
151 if (joystick_devices_count != -1) return joystick_devices_count;
153 joystick_devices_count = 0;
154 for (i = 0; i < MAX_JOYSTICKS; i++)
156 int fd;
157 struct JoyDev joydev, *new_joydevs;
158 BYTE axes_map[ABS_MAX + 1];
159 SHORT btn_map[KEY_MAX - BTN_MISC + 1];
160 BOOL non_js = FALSE;
162 snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i);
163 if ((fd = open(joydev.device, O_RDONLY)) == -1)
165 snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_OLD, i);
166 if ((fd = open(joydev.device, O_RDONLY)) == -1) continue;
169 strcpy(joydev.name, "Wine Joystick");
170 #if defined(JSIOCGNAME)
171 if (ioctl(fd, JSIOCGNAME(sizeof(joydev.name) - sizeof(JOYDEVDRIVER)), joydev.name) < 0)
172 WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joydev.device, strerror(errno));
173 #endif
175 /* Append driver name */
176 strcat(joydev.name, JOYDEVDRIVER);
178 if (device_disabled_registry(joydev.name)) {
179 close(fd);
180 continue;
183 #ifdef JSIOCGAXES
184 if (ioctl(fd, JSIOCGAXES, &joydev.axis_count) < 0)
186 WARN("ioctl(%s,JSIOCGAXES) failed: %s, defaulting to 2\n", joydev.device, strerror(errno));
187 joydev.axis_count = 2;
189 #else
190 WARN("reading number of joystick axes unsupported in this platform, defaulting to 2\n");
191 joydev.axis_count = 2;
192 #endif
193 #ifdef JSIOCGBUTTONS
194 if (ioctl(fd, JSIOCGBUTTONS, &joydev.button_count) < 0)
196 WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defaulting to 2\n", joydev.device, strerror(errno));
197 joydev.button_count = 2;
199 #else
200 WARN("reading number of joystick buttons unsupported in this platform, defaulting to 2\n");
201 joydev.button_count = 2;
202 #endif
204 joydev.is_joystick = FALSE;
205 if (ioctl(fd, JSIOCGBTNMAP, btn_map) < 0)
207 WARN("ioctl(%s,JSIOCGBTNMAP) failed: %s\n", joydev.device, strerror(errno));
209 else
211 INT j;
212 /* in lieu of properly reporting HID usage, detect presence of
213 * "joystick buttons" and report those devices as joysticks instead of
214 * gamepads */
215 for (j = 0; !joydev.is_joystick && j < joydev.button_count; j++)
217 switch (btn_map[j])
219 case BTN_TRIGGER:
220 case BTN_THUMB:
221 case BTN_THUMB2:
222 case BTN_TOP:
223 case BTN_TOP2:
224 case BTN_PINKIE:
225 case BTN_BASE:
226 case BTN_BASE2:
227 case BTN_BASE3:
228 case BTN_BASE4:
229 case BTN_BASE5:
230 case BTN_BASE6:
231 case BTN_DEAD:
232 joydev.is_joystick = TRUE;
233 break;
234 case BTN_MOUSE:
235 case BTN_STYLUS:
236 non_js = TRUE;
237 break;
238 default:
239 break;
244 if(non_js)
246 TRACE("Non-joystick detected. Skipping\n");
247 close(fd);
248 continue;
251 if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0)
253 WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno));
254 joydev.dev_axes_map = NULL;
256 else
257 if ((joydev.dev_axes_map = HeapAlloc(GetProcessHeap(), 0, joydev.axis_count * sizeof(int))))
259 INT j, found_axes = 0;
261 /* Remap to DI numbers */
262 for (j = 0; j < joydev.axis_count; j++)
264 if (axes_map[j] < 8)
266 /* Axis match 1-to-1 */
267 joydev.dev_axes_map[j] = j;
268 found_axes++;
270 else if (axes_map[j] <= 10)
272 /* Axes 8 through 10 are Wheel, Gas and Brake,
273 * remap to 0, 1 and 2
275 joydev.dev_axes_map[j] = axes_map[j] - 8;
276 found_axes++;
278 else if (axes_map[j] == 16 ||
279 axes_map[j] == 17)
281 /* POV axis */
282 joydev.dev_axes_map[j] = 8;
283 found_axes++;
285 else
286 joydev.dev_axes_map[j] = -1;
289 /* If no axes were configured but there are axes assume a 1-to-1 (wii controller) */
290 if (joydev.axis_count && !found_axes)
292 int axes_limit = min(joydev.axis_count, 8); /* generic driver limit */
294 ERR("Incoherent joystick data, advertised %d axes, detected 0. Assuming 1-to-1.\n",
295 joydev.axis_count);
296 for (j = 0; j < axes_limit; j++)
297 joydev.dev_axes_map[j] = j;
299 joydev.axis_count = axes_limit;
303 /* Find vendor_id and product_id in sysfs */
304 joydev.vendor_id = 0;
305 joydev.product_id = 0;
307 read_sys_id_variable(i, "vendor", &joydev.vendor_id);
308 read_sys_id_variable(i, "product", &joydev.product_id);
309 read_sys_id_variable(i, "bustype", &joydev.bus_type);
311 if (joydev.vendor_id == 0 || joydev.product_id == 0)
313 joydev.guid_product = DInput_Wine_Joystick_GUID;
315 else
317 /* Concatenate product_id with vendor_id to mimic Windows behaviour */
318 joydev.guid_product = DInput_PIDVID_Product_GUID;
319 joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
322 close(fd);
324 if (!joystick_devices_count)
325 new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
326 else
327 new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joystick_devices,
328 (joystick_devices_count + 1) * sizeof(struct JoyDev));
329 if (!new_joydevs) continue;
331 TRACE("Found a joystick on %s: %s\n with %d axes and %d buttons\n", joydev.device,
332 joydev.name, joydev.axis_count, joydev.button_count);
334 joystick_devices = new_joydevs;
335 joystick_devices[joystick_devices_count++] = joydev;
338 return joystick_devices_count;
341 static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
343 DWORD dwSize = lpddi->dwSize;
345 TRACE("%d %p\n", dwSize, lpddi);
346 memset(lpddi, 0, dwSize);
348 /* Return joystick */
349 lpddi->dwSize = dwSize;
350 lpddi->guidInstance = DInput_Wine_Joystick_GUID;
351 lpddi->guidInstance.Data3 = id;
352 lpddi->guidProduct = joystick_devices[id].guid_product;
353 lpddi->dwDevType = get_device_type(version, joystick_devices[id].is_joystick);
355 /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */
356 if (joystick_devices[id].bus_type == BUS_USB &&
357 joystick_devices[id].vendor_id && joystick_devices[id].product_id)
359 lpddi->dwDevType |= DIDEVTYPE_HID;
360 lpddi->wUsagePage = 0x01; /* Desktop */
361 if (joystick_devices[id].is_joystick)
362 lpddi->wUsage = 0x04; /* Joystick */
363 else
364 lpddi->wUsage = 0x05; /* Game Pad */
367 MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
368 MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszProductName, MAX_PATH);
369 lpddi->guidFFDriver = GUID_NULL;
372 static HRESULT joydev_enum_device(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
374 int fd = -1;
376 if (id >= find_joystick_devices()) return E_FAIL;
378 if (dwFlags & DIEDFL_FORCEFEEDBACK) {
379 WARN("force feedback not supported\n");
380 return S_FALSE;
383 if ((dwDevType == 0) ||
384 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
385 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) {
386 /* check whether we have a joystick */
387 if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1)
389 WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno));
390 return S_FALSE;
392 fill_joystick_dideviceinstanceW( lpddi, version, id );
393 close(fd);
394 TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name);
395 return S_OK;
398 return S_FALSE;
401 static HRESULT alloc_device( REFGUID rguid, IDirectInputImpl *dinput, JoystickImpl **out, unsigned short index )
403 DWORD i;
404 JoystickImpl* newDevice;
405 HRESULT hr;
406 LPDIDATAFORMAT df = NULL;
407 int idx = 0;
408 DIDEVICEINSTANCEW ddi;
410 TRACE( "%s %p %p %hu\n", debugstr_guid( rguid ), dinput, out, index );
412 if (FAILED(hr = direct_input_device_alloc( sizeof(JoystickImpl), &JoystickWvt, rguid, dinput, (void **)&newDevice )))
413 return hr;
414 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
416 newDevice->joydev = &joystick_devices[index];
417 newDevice->joyfd = -1;
418 newDevice->generic.guidInstance = DInput_Wine_Joystick_GUID;
419 newDevice->generic.guidInstance.Data3 = index;
420 newDevice->generic.guidProduct = DInput_Wine_Joystick_GUID;
421 newDevice->generic.joy_polldev = joy_polldev;
422 newDevice->generic.name = newDevice->joydev->name;
423 newDevice->generic.device_axis_count = newDevice->joydev->axis_count;
424 newDevice->generic.devcaps.dwButtons = newDevice->joydev->button_count;
426 if (newDevice->generic.devcaps.dwButtons > 128)
428 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons);
429 newDevice->generic.devcaps.dwButtons = 128;
432 /* setup_dinput_options may change these */
433 newDevice->generic.deadzone = 0;
435 /* do any user specified configuration */
436 hr = setup_dinput_options(&newDevice->generic, newDevice->joydev->dev_axes_map);
437 if (hr != DI_OK)
438 goto FAILED1;
440 /* Create copy of default data format */
441 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED;
442 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
444 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons;
445 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED;
447 for (i = 0; i < newDevice->generic.device_axis_count; i++)
449 int wine_obj = newDevice->generic.axis_map[i];
451 if (wine_obj < 0) continue;
453 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
454 if (wine_obj < 8)
455 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
456 else
458 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
459 i++; /* POV takes 2 axes */
462 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++)
464 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize);
465 df->rgodf[idx ].pguid = &GUID_Button;
466 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON;
468 newDevice->generic.base.data_format.wine_df = df;
470 /* initialize default properties */
471 for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) {
472 newDevice->generic.props[i].lDevMin = -32767;
473 newDevice->generic.props[i].lDevMax = +32767;
474 newDevice->generic.props[i].lMin = 0;
475 newDevice->generic.props[i].lMax = 0xffff;
476 newDevice->generic.props[i].lDeadZone = newDevice->generic.deadzone; /* % * 1000 */
477 newDevice->generic.props[i].lSaturation = 0;
480 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
481 newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
483 ddi.dwSize = sizeof(ddi);
484 fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index);
485 newDevice->generic.devcaps.dwDevType = ddi.dwDevType;
487 newDevice->generic.devcaps.dwFFSamplePeriod = 0;
488 newDevice->generic.devcaps.dwFFMinTimeResolution = 0;
489 newDevice->generic.devcaps.dwFirmwareRevision = 0;
490 newDevice->generic.devcaps.dwHardwareRevision = 0;
491 newDevice->generic.devcaps.dwFFDriverVersion = 0;
493 if (TRACE_ON(dinput)) {
494 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df);
495 for (i = 0; i < (newDevice->generic.device_axis_count); i++)
496 TRACE("axis_map[%d] = %d\n", i, newDevice->generic.axis_map[i]);
497 _dump_DIDEVCAPS(&newDevice->generic.devcaps);
500 *out = newDevice;
501 return DI_OK;
503 FAILED:
504 hr = DIERR_OUTOFMEMORY;
505 FAILED1:
506 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
507 HeapFree(GetProcessHeap(), 0, df);
508 release_DataFormat(&newDevice->generic.base.data_format);
509 HeapFree(GetProcessHeap(),0,newDevice->generic.axis_map);
510 HeapFree(GetProcessHeap(),0,newDevice);
511 return hr;
514 /******************************************************************************
515 * get_joystick_index : Get the joystick index from a given GUID
517 static unsigned short get_joystick_index(REFGUID guid)
519 GUID wine_joystick = DInput_Wine_Joystick_GUID;
520 GUID dev_guid = *guid;
521 INT i;
523 wine_joystick.Data3 = 0;
524 dev_guid.Data3 = 0;
526 /* for the standard joystick GUID use index 0 */
527 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
529 /* for the wine joystick GUIDs use the index stored in Data3 */
530 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3;
532 for(i = 0; i < joystick_devices_count; i++)
533 if(IsEqualGUID(&joystick_devices[i].guid_product, guid)) return i;
535 return MAX_JOYSTICKS;
538 static HRESULT joydev_create_device( IDirectInputImpl *dinput, REFGUID rguid, IDirectInputDevice8W **out )
540 unsigned short index;
542 TRACE( "%p %s %p\n", dinput, debugstr_guid( rguid ), out );
543 find_joystick_devices();
544 *out = NULL;
546 if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS &&
547 joystick_devices_count && index < joystick_devices_count)
549 JoystickImpl *This;
550 HRESULT hr;
552 if (FAILED(hr = alloc_device( rguid, dinput, &This, index ))) return hr;
554 TRACE( "Created a Joystick device (%p)\n", This );
556 *out = &This->generic.base.IDirectInputDevice8W_iface;
557 return hr;
560 return DIERR_DEVICENOTREG;
563 #undef MAX_JOYSTICKS
565 const struct dinput_device joystick_linux_device = {
566 "Wine Linux joystick driver",
567 joydev_enum_device,
568 joydev_create_device
571 /******************************************************************************
572 * Acquire : gets exclusive control of the joystick
574 static HRESULT WINAPI JoystickLinuxWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
576 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
577 HRESULT res;
579 TRACE("(%p)\n",This);
581 res = IDirectInputDevice2WImpl_Acquire(iface);
582 if (res != DI_OK)
583 return res;
585 /* open the joystick device */
586 if (This->joyfd==-1) {
587 TRACE("opening joystick device %s\n", This->joydev->device);
589 This->joyfd = open(This->joydev->device, O_RDONLY);
590 if (This->joyfd==-1) {
591 ERR("open(%s) failed: %s\n", This->joydev->device, strerror(errno));
592 IDirectInputDevice2WImpl_Unacquire(iface);
593 return DIERR_NOTFOUND;
597 return DI_OK;
600 /******************************************************************************
601 * GetProperty : get input device properties
603 static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
605 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
607 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
608 _dump_DIPROPHEADER(pdiph);
610 if (!IS_DIPROP(rguid)) return DI_OK;
612 switch (LOWORD(rguid)) {
614 case (DWORD_PTR) DIPROP_VIDPID:
616 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
618 if (!This->joydev->product_id || !This->joydev->vendor_id)
619 return DIERR_UNSUPPORTED;
620 pd->dwData = MAKELONG(This->joydev->vendor_id, This->joydev->product_id);
621 TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData);
622 break;
624 case (DWORD_PTR) DIPROP_JOYSTICKID:
626 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
628 pd->dwData = get_joystick_index(&This->generic.base.guid);
629 TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
630 break;
633 case (DWORD_PTR) DIPROP_GUIDANDPATH:
635 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
636 'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0};
637 static const WCHAR miW[] = {'m','i',0};
638 static const WCHAR igW[] = {'i','g',0};
640 BOOL is_gamepad;
641 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
642 WORD vid = This->joydev->vendor_id;
643 WORD pid = This->joydev->product_id;
645 if (!pid || !vid)
646 return DIERR_UNSUPPORTED;
648 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
649 pd->guidClass = GUID_DEVCLASS_HIDCLASS;
650 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid));
652 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
653 break;
656 default:
657 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
660 return DI_OK;
663 /******************************************************************************
664 * GetDeviceInfo : get information about a device's identity
666 static HRESULT WINAPI JoystickLinuxWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, LPDIDEVICEINSTANCEW ddi)
668 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
670 TRACE("(%p) %p\n", This, ddi);
672 if (ddi == NULL) return E_POINTER;
673 if ((ddi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
674 (ddi->dwSize != sizeof(DIDEVICEINSTANCEW)))
675 return DIERR_INVALIDPARAM;
677 fill_joystick_dideviceinstanceW( ddi, This->generic.base.dinput->dwVersion,
678 get_joystick_index(&This->generic.base.guid) );
680 ddi->guidInstance = This->generic.base.guid;
682 return DI_OK;
685 /******************************************************************************
686 * Unacquire : frees the joystick
688 static HRESULT WINAPI JoystickLinuxWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
690 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
691 HRESULT res;
693 TRACE("(%p)\n",This);
695 res = IDirectInputDevice2WImpl_Unacquire(iface);
697 if (res != DI_OK)
698 return res;
700 if (This->joyfd!=-1) {
701 TRACE("closing joystick device\n");
702 close(This->joyfd);
703 This->joyfd = -1;
704 return DI_OK;
707 return DI_NOEFFECT;
710 static void joy_polldev( IDirectInputDevice8W *iface )
712 struct pollfd plfd;
713 struct js_event jse;
714 JoystickImpl *This = impl_from_IDirectInputDevice8W( iface );
716 TRACE("(%p)\n", This);
718 if (This->joyfd==-1) {
719 WARN("no device\n");
720 return;
722 while (1)
724 LONG value;
725 int inst_id = -1;
727 plfd.fd = This->joyfd;
728 plfd.events = POLLIN;
729 if (poll(&plfd,1,0) != 1)
730 return;
731 /* we have one event, so we can read */
732 if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) {
733 return;
735 TRACE("js_event: type 0x%x, number %d, value %d\n",
736 jse.type,jse.number,jse.value);
737 if (jse.type & JS_EVENT_BUTTON)
739 if (jse.number >= This->generic.devcaps.dwButtons) return;
741 inst_id = DIDFT_MAKEINSTANCE(jse.number) | DIDFT_PSHBUTTON;
742 This->generic.js.rgbButtons[jse.number] = value = jse.value ? 0x80 : 0x00;
744 else if (jse.type & JS_EVENT_AXIS)
746 int number = This->generic.axis_map[jse.number]; /* wine format object index */
748 if (number < 0) return;
749 inst_id = number < 8 ? DIDFT_MAKEINSTANCE(number) | DIDFT_ABSAXIS :
750 DIDFT_MAKEINSTANCE(number - 8) | DIDFT_POV;
751 value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], jse.value);
753 TRACE("changing axis %d => %d\n", jse.number, number);
754 switch (number)
756 case 0: This->generic.js.lX = value; break;
757 case 1: This->generic.js.lY = value; break;
758 case 2: This->generic.js.lZ = value; break;
759 case 3: This->generic.js.lRx = value; break;
760 case 4: This->generic.js.lRy = value; break;
761 case 5: This->generic.js.lRz = value; break;
762 case 6: This->generic.js.rglSlider[0] = value; break;
763 case 7: This->generic.js.rglSlider[1] = value; break;
764 case 8: case 9: case 10: case 11:
766 int idx = number - 8;
768 if (jse.number % 2)
769 This->povs[idx].y = jse.value;
770 else
771 This->povs[idx].x = jse.value;
773 This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
774 break;
776 default:
777 WARN("axis %d not supported\n", number);
780 if (inst_id >= 0)
781 queue_event(iface, inst_id, value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
785 static const IDirectInputDevice8WVtbl JoystickWvt =
787 IDirectInputDevice2WImpl_QueryInterface,
788 IDirectInputDevice2WImpl_AddRef,
789 JoystickWGenericImpl_Release,
790 JoystickWGenericImpl_GetCapabilities,
791 IDirectInputDevice2WImpl_EnumObjects,
792 JoystickLinuxWImpl_GetProperty,
793 JoystickWGenericImpl_SetProperty,
794 JoystickLinuxWImpl_Acquire,
795 JoystickLinuxWImpl_Unacquire,
796 JoystickWGenericImpl_GetDeviceState,
797 IDirectInputDevice2WImpl_GetDeviceData,
798 IDirectInputDevice2WImpl_SetDataFormat,
799 IDirectInputDevice2WImpl_SetEventNotification,
800 IDirectInputDevice2WImpl_SetCooperativeLevel,
801 JoystickWGenericImpl_GetObjectInfo,
802 JoystickLinuxWImpl_GetDeviceInfo,
803 IDirectInputDevice2WImpl_RunControlPanel,
804 IDirectInputDevice2WImpl_Initialize,
805 IDirectInputDevice2WImpl_CreateEffect,
806 IDirectInputDevice2WImpl_EnumEffects,
807 IDirectInputDevice2WImpl_GetEffectInfo,
808 IDirectInputDevice2WImpl_GetForceFeedbackState,
809 IDirectInputDevice2WImpl_SendForceFeedbackCommand,
810 IDirectInputDevice2WImpl_EnumCreatedEffectObjects,
811 IDirectInputDevice2WImpl_Escape,
812 JoystickWGenericImpl_Poll,
813 IDirectInputDevice2WImpl_SendDeviceData,
814 IDirectInputDevice7WImpl_EnumEffectsInFile,
815 IDirectInputDevice7WImpl_WriteEffectToFile,
816 JoystickWGenericImpl_BuildActionMap,
817 JoystickWGenericImpl_SetActionMap,
818 IDirectInputDevice8WImpl_GetImageInfo
821 #else /* HAVE_LINUX_22_JOYSTICK_API */
823 const struct dinput_device joystick_linux_device = {
824 "Wine Linux joystick driver",
825 NULL,
826 NULL,
829 #endif /* HAVE_LINUX_22_JOYSTICK_API */