dinput: Fix product GUID generation in Linux joystick and event API.
[wine.git] / dlls / dinput / joystick_linuxinput.c
blobff02849e8603791d40a9a2e4d5087cd2518b6c66
1 /* DirectInput Joystick device
3 * Copyright 1998,2000 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2005 Daniel Remenak
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "config.h"
24 #include "wine/port.h"
26 #include <assert.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34 #ifdef HAVE_SYS_TIME_H
35 # include <sys/time.h>
36 #endif
37 #include <fcntl.h>
38 #ifdef HAVE_SYS_IOCTL_H
39 # include <sys/ioctl.h>
40 #endif
41 #include <errno.h>
42 #ifdef HAVE_LINUX_INPUT_H
43 # include <linux/input.h>
44 # undef SW_MAX
45 # if defined(EVIOCGBIT) && defined(EV_ABS) && defined(BTN_PINKIE)
46 # define HAS_PROPER_HEADER
47 # endif
48 #endif
49 #ifdef HAVE_SYS_POLL_H
50 # include <sys/poll.h>
51 #endif
53 #include "wine/debug.h"
54 #include "wine/unicode.h"
55 #include "wine/list.h"
56 #include "windef.h"
57 #include "winbase.h"
58 #include "winerror.h"
59 #include "winreg.h"
60 #include "dinput.h"
62 #include "dinput_private.h"
63 #include "device_private.h"
64 #include "joystick_private.h"
66 #ifdef HAS_PROPER_HEADER
68 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
70 #define EVDEVPREFIX "/dev/input/event"
71 #define EVDEVDRIVER " (event)"
73 /* Wine joystick driver object instances */
74 #define WINE_JOYSTICK_MAX_AXES 8
75 #define WINE_JOYSTICK_MAX_POVS 4
76 #define WINE_JOYSTICK_MAX_BUTTONS 128
78 struct wine_input_absinfo {
79 LONG value;
80 LONG minimum;
81 LONG maximum;
82 LONG fuzz;
83 LONG flat;
86 /* implemented in effect_linuxinput.c */
87 HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, struct list *parent_list_entry, LPDIRECTINPUTEFFECT* peff);
88 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
89 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
91 typedef struct JoystickImpl JoystickImpl;
92 static const IDirectInputDevice8AVtbl JoystickAvt;
93 static const IDirectInputDevice8WVtbl JoystickWvt;
95 struct JoyDev {
96 char *device;
97 char *name;
98 GUID guid;
99 GUID guid_product;
101 BOOL has_ff;
102 int num_effects;
104 /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
105 BYTE evbits[(EV_MAX+7)/8];
106 BYTE absbits[(ABS_MAX+7)/8];
107 BYTE keybits[(KEY_MAX+7)/8];
108 BYTE ffbits[(FF_MAX+7)/8];
110 /* data returned by the EVIOCGABS() ioctl */
111 struct wine_input_absinfo axes[ABS_MAX];
113 WORD vendor_id, product_id;
116 struct JoystickImpl
118 struct JoystickGenericImpl generic;
119 struct JoyDev *joydev;
121 /* joystick private */
122 int joyfd;
124 int dev_axes_to_di[ABS_MAX];
125 POINTL povs[4];
127 /* LUT for KEY_ to offset in rgbButtons */
128 BYTE buttons[KEY_MAX];
130 /* Force feedback variables */
131 struct list ff_effects;
132 int ff_state;
133 int ff_autocenter;
134 int ff_gain;
137 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
139 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
140 JoystickGenericImpl, base), JoystickImpl, generic);
142 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
144 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
145 JoystickGenericImpl, base), JoystickImpl, generic);
148 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
150 return &This->generic.base.IDirectInputDevice8W_iface;
153 static void fake_current_js_state(JoystickImpl *ji);
154 static void find_joydevs(void);
155 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface);
157 /* This GUID is slightly different from the linux joystick one. Take note. */
158 static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
159 0x9e573eda,
160 0x7734,
161 0x11d2,
162 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
166 * Construct the GUID in the same way of Windows doing this.
167 * Data1 is concatenation of productid and vendorid.
168 * Data2 and Data3 are NULL.
169 * Data4 seems to be a constant.
171 static const GUID DInput_Wine_Joystick_Constant_Part_GUID = {
172 0x000000000,
173 0x0000,
174 0x0000,
175 {0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44}
178 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
180 #define MAX_JOYDEV 64
182 static int have_joydevs = -1;
183 static struct JoyDev *joydevs = NULL;
185 static void find_joydevs(void)
187 int i;
189 if (InterlockedCompareExchange(&have_joydevs, 0, -1) != -1)
190 /* Someone beat us to it */
191 return;
193 for (i = 0; i < MAX_JOYDEV; i++)
195 char buf[MAX_PATH];
196 struct JoyDev joydev = {0};
197 int fd;
198 BOOL no_ff_check = FALSE;
199 int j;
200 struct JoyDev *new_joydevs;
201 struct input_id device_id = {0};
203 snprintf(buf, sizeof(buf), EVDEVPREFIX"%d", i);
205 if ((fd = open(buf, O_RDWR)) == -1)
207 fd = open(buf, O_RDONLY);
208 no_ff_check = TRUE;
211 if (fd == -1)
213 WARN("Failed to open \"%s\": %d %s\n", buf, errno, strerror(errno));
214 continue;
217 if (ioctl(fd, EVIOCGBIT(0, sizeof(joydev.evbits)), joydev.evbits) == -1)
219 WARN("ioctl(EVIOCGBIT, 0) failed: %d %s\n", errno, strerror(errno));
220 close(fd);
221 continue;
223 if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(joydev.absbits)), joydev.absbits) == -1)
225 WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno));
226 close(fd);
227 continue;
229 if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(joydev.keybits)), joydev.keybits) == -1)
231 WARN("ioctl(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno));
232 close(fd);
233 continue;
236 /* A true joystick has at least axis X and Y, and at least 1
237 * button. copied from linux/drivers/input/joydev.c */
238 if (!test_bit(joydev.absbits, ABS_X) || !test_bit(joydev.absbits, ABS_Y) ||
239 !(test_bit(joydev.keybits, BTN_TRIGGER) ||
240 test_bit(joydev.keybits, BTN_A) ||
241 test_bit(joydev.keybits, BTN_1)))
243 close(fd);
244 continue;
247 if (!(joydev.device = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + 1)))
249 close(fd);
250 continue;
252 strcpy(joydev.device, buf);
254 buf[MAX_PATH - 1] = 0;
255 if (ioctl(fd, EVIOCGNAME(MAX_PATH - 1), buf) != -1 &&
256 (joydev.name = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + strlen(EVDEVDRIVER) + 1)))
258 strcpy(joydev.name, buf);
259 /* Append driver name */
260 strcat(joydev.name, EVDEVDRIVER);
262 else
263 joydev.name = joydev.device;
265 if (device_disabled_registry(joydev.name)) {
266 close(fd);
267 HeapFree(GetProcessHeap(), 0, joydev.name);
268 if (joydev.name != joydev.device)
269 HeapFree(GetProcessHeap(), 0, joydev.device);
270 continue;
273 joydev.guid = DInput_Wine_Joystick_Base_GUID;
274 joydev.guid.Data3 += have_joydevs;
276 TRACE("Found a joystick on %s: %s (%s)\n",
277 joydev.device, joydev.name,
278 debugstr_guid(&joydev.guid)
281 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
282 if (!no_ff_check &&
283 test_bit(joydev.evbits, EV_FF) &&
284 ioctl(fd, EVIOCGBIT(EV_FF, sizeof(joydev.ffbits)), joydev.ffbits) != -1 &&
285 ioctl(fd, EVIOCGEFFECTS, &joydev.num_effects) != -1 &&
286 joydev.num_effects > 0)
288 TRACE(" ... with force feedback\n");
289 joydev.has_ff = TRUE;
291 #endif
293 for (j = 0; j < ABS_MAX;j ++)
295 if (!test_bit(joydev.absbits, j)) continue;
296 if (ioctl(fd, EVIOCGABS(j), &(joydev.axes[j])) != -1)
298 TRACE(" ... with axis %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
300 joydev.axes[j].value,
301 joydev.axes[j].minimum,
302 joydev.axes[j].maximum,
303 joydev.axes[j].fuzz,
304 joydev.axes[j].flat
309 if (ioctl(fd, EVIOCGID, &device_id) == -1)
311 WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno));
312 joydev.guid_product = DInput_Wine_Joystick_Base_GUID;
314 else
316 joydev.vendor_id = device_id.vendor;
317 joydev.product_id = device_id.product;
319 /* Concatenate product_id with vendor_id to mimic Windows behaviour */
320 joydev.guid_product = DInput_Wine_Joystick_Constant_Part_GUID;
321 joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
324 if (!have_joydevs)
325 new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
326 else
327 new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joydevs, (1 + have_joydevs) * sizeof(struct JoyDev));
329 if (!new_joydevs)
331 close(fd);
332 continue;
334 joydevs = new_joydevs;
335 joydevs[have_joydevs] = joydev;
336 have_joydevs++;
338 close(fd);
342 static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
344 DWORD dwSize = lpddi->dwSize;
346 TRACE("%d %p\n", dwSize, lpddi);
347 memset(lpddi, 0, dwSize);
349 lpddi->dwSize = dwSize;
350 lpddi->guidInstance = joydevs[id].guid;
351 lpddi->guidProduct = joydevs[id].guid_product;
352 lpddi->guidFFDriver = GUID_NULL;
354 if (version >= 0x0800)
355 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
356 else
357 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
359 strcpy(lpddi->tszInstanceName, joydevs[id].name);
360 strcpy(lpddi->tszProductName, joydevs[id].name);
363 static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
365 DWORD dwSize = lpddi->dwSize;
367 TRACE("%d %p\n", dwSize, lpddi);
368 memset(lpddi, 0, dwSize);
370 lpddi->dwSize = dwSize;
371 lpddi->guidInstance = joydevs[id].guid;
372 lpddi->guidProduct = joydevs[id].guid_product;
373 lpddi->guidFFDriver = GUID_NULL;
375 if (version >= 0x0800)
376 lpddi->dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
377 else
378 lpddi->dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
380 MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
381 MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszProductName, MAX_PATH);
384 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
386 find_joydevs();
388 if (id >= have_joydevs) {
389 return E_FAIL;
392 if (!((dwDevType == 0) ||
393 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
394 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
395 return S_FALSE;
397 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
398 if (dwFlags & DIEDFL_FORCEFEEDBACK)
399 return S_FALSE;
400 #endif
402 if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
403 fill_joystick_dideviceinstanceA(lpddi, version, id);
404 return S_OK;
406 return S_FALSE;
409 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
411 find_joydevs();
413 if (id >= have_joydevs) {
414 return E_FAIL;
417 if (!((dwDevType == 0) ||
418 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version > 0x0300 && version < 0x0800)) ||
419 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
420 return S_FALSE;
422 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
423 if (dwFlags & DIEDFL_FORCEFEEDBACK)
424 return S_FALSE;
425 #endif
427 if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
428 fill_joystick_dideviceinstanceW(lpddi, version, id);
429 return S_OK;
431 return S_FALSE;
434 static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsigned short index)
436 JoystickImpl* newDevice;
437 LPDIDATAFORMAT df = NULL;
438 int i, idx = 0;
439 int default_axis_map[WINE_JOYSTICK_MAX_AXES + WINE_JOYSTICK_MAX_POVS*2];
441 newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
442 if (!newDevice) return NULL;
444 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
445 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
446 newDevice->generic.base.ref = 1;
447 newDevice->generic.base.guid = *rguid;
448 newDevice->generic.base.dinput = dinput;
449 newDevice->generic.joy_polldev = joy_polldev;
450 newDevice->joyfd = -1;
451 newDevice->joydev = &joydevs[index];
452 newDevice->generic.name = newDevice->joydev->name;
453 list_init(&newDevice->ff_effects);
454 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
455 newDevice->ff_state = FF_STATUS_STOPPED;
456 #endif
457 /* There is no way in linux to query force feedback autocenter status.
458 Instead, track it with ff_autocenter, and assume it's initially
459 enabled. */
460 newDevice->ff_autocenter = 1;
461 newDevice->ff_gain = 0xFFFF;
462 InitializeCriticalSection(&newDevice->generic.base.crit);
463 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
465 /* Count number of available axes - supported Axis & POVs */
466 for (i = 0; i < ABS_MAX; i++)
468 if (i < WINE_JOYSTICK_MAX_AXES &&
469 test_bit(newDevice->joydev->absbits, i))
471 newDevice->generic.device_axis_count++;
472 newDevice->dev_axes_to_di[i] = idx;
473 newDevice->generic.props[idx].lDevMin = newDevice->joydev->axes[i].minimum;
474 newDevice->generic.props[idx].lDevMax = newDevice->joydev->axes[i].maximum;
475 default_axis_map[idx] = i;
476 idx++;
478 else
479 newDevice->dev_axes_to_di[i] = -1;
482 for (i = 0; i < WINE_JOYSTICK_MAX_POVS; i++)
484 if (test_bit(newDevice->joydev->absbits, ABS_HAT0X + i * 2) &&
485 test_bit(newDevice->joydev->absbits, ABS_HAT0Y + i * 2))
487 newDevice->generic.device_axis_count += 2;
488 newDevice->generic.props[idx ].lDevMin = newDevice->joydev->axes[ABS_HAT0X + i * 2].minimum;
489 newDevice->generic.props[idx ].lDevMax = newDevice->joydev->axes[ABS_HAT0X + i * 2].maximum;
490 newDevice->dev_axes_to_di[ABS_HAT0X + i * 2] = idx;
491 newDevice->generic.props[idx+1].lDevMin = newDevice->joydev->axes[ABS_HAT0Y + i * 2].minimum;
492 newDevice->generic.props[idx+1].lDevMax = newDevice->joydev->axes[ABS_HAT0Y + i * 2].maximum;
493 newDevice->dev_axes_to_di[ABS_HAT0Y + i * 2] = idx + 1;
495 default_axis_map[idx] = default_axis_map[idx + 1] = WINE_JOYSTICK_MAX_AXES + i;
496 idx += 2;
498 else
499 newDevice->dev_axes_to_di[ABS_HAT0X + i * 2] = newDevice->dev_axes_to_di[ABS_HAT0Y + i * 2] = -1;
502 /* do any user specified configuration */
503 if (setup_dinput_options(&newDevice->generic, default_axis_map) != DI_OK) goto failed;
505 /* Create copy of default data format */
506 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto failed;
507 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
508 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
511 /* Construct internal data format */
513 /* Supported Axis & POVs */
514 for (i = 0, idx = 0; i < newDevice->generic.device_axis_count; i++)
516 int wine_obj = newDevice->generic.axis_map[i];
518 if (wine_obj < 0) continue;
520 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
521 if (wine_obj < 8)
522 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
523 else
525 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
526 i++; /* POV takes 2 axes */
529 newDevice->generic.props[idx].lMin = 0;
530 newDevice->generic.props[idx].lMax = 0xffff;
531 newDevice->generic.props[idx].lSaturation = 0;
532 newDevice->generic.props[idx].lDeadZone = newDevice->generic.deadzone;
534 /* Linux supports force-feedback on X & Y axes only */
535 if (newDevice->joydev->has_ff && (i == 0 || i == 1))
536 df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
538 idx++;
541 /* Buttons can be anywhere, so check all */
542 for (i = 0; i < KEY_MAX && newDevice->generic.devcaps.dwButtons < WINE_JOYSTICK_MAX_BUTTONS; i++)
544 if (!test_bit(newDevice->joydev->keybits, i)) continue;
546 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[newDevice->generic.devcaps.dwButtons + 12], df->dwObjSize);
547 newDevice->buttons[i] = 0x80 | newDevice->generic.devcaps.dwButtons;
548 df->rgodf[idx ].pguid = &GUID_Button;
549 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->generic.devcaps.dwButtons++) | DIDFT_PSHBUTTON;
551 df->dwNumObjs = idx;
552 newDevice->generic.base.data_format.wine_df = df;
554 fake_current_js_state(newDevice);
556 /* Fill the caps */
557 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
558 newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
559 if (newDevice->generic.base.dinput->dwVersion >= 0x0800)
560 newDevice->generic.devcaps.dwDevType = DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8);
561 else
562 newDevice->generic.devcaps.dwDevType = DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8);
564 if (newDevice->joydev->has_ff)
565 newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
567 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
569 EnterCriticalSection(&dinput->crit);
570 list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
571 LeaveCriticalSection(&dinput->crit);
573 return newDevice;
575 failed:
576 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
577 HeapFree(GetProcessHeap(), 0, df);
578 HeapFree(GetProcessHeap(), 0, newDevice->generic.axis_map);
579 HeapFree(GetProcessHeap(), 0, newDevice);
580 return NULL;
583 /******************************************************************************
584 * get_joystick_index : Get the joystick index from a given GUID
586 static unsigned short get_joystick_index(REFGUID guid)
588 GUID wine_joystick = DInput_Wine_Joystick_Base_GUID;
589 GUID dev_guid = *guid;
591 wine_joystick.Data3 = 0;
592 dev_guid.Data3 = 0;
594 /* for the standard joystick GUID use index 0 */
595 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
597 /* for the wine joystick GUIDs use the index stored in Data3 */
598 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3 - DInput_Wine_Joystick_Base_GUID.Data3;
600 return MAX_JOYDEV;
603 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
605 unsigned short index;
607 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
608 find_joydevs();
609 *pdev = NULL;
611 if ((index = get_joystick_index(rguid)) < MAX_JOYDEV &&
612 have_joydevs && index < have_joydevs)
614 JoystickImpl *This;
616 if (riid == NULL)
617 ;/* nothing */
618 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
619 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
620 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
621 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
623 unicode = 0;
625 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
626 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
627 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
628 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
630 unicode = 1;
632 else
634 WARN("no interface\n");
635 return DIERR_NOINTERFACE;
638 This = alloc_device(rguid, dinput, index);
639 TRACE("Created a Joystick device (%p)\n", This);
641 if (!This) return DIERR_OUTOFMEMORY;
643 if (unicode)
644 *pdev = &This->generic.base.IDirectInputDevice8W_iface;
645 else
646 *pdev = &This->generic.base.IDirectInputDevice8A_iface;
648 return DI_OK;
651 return DIERR_DEVICENOTREG;
655 const struct dinput_device joystick_linuxinput_device = {
656 "Wine Linux-input joystick driver",
657 joydev_enum_deviceA,
658 joydev_enum_deviceW,
659 joydev_create_device
662 /******************************************************************************
663 * Acquire : gets exclusive control of the joystick
665 static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
667 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
668 HRESULT res;
670 TRACE("(this=%p)\n",This);
672 if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK)
674 WARN("Failed to acquire: %x\n", res);
675 return res;
678 if ((This->joyfd = open(This->joydev->device, O_RDWR)) == -1)
680 if ((This->joyfd = open(This->joydev->device, O_RDONLY)) == -1)
682 /* Couldn't open the device at all */
683 ERR("Failed to open device %s: %d %s\n", This->joydev->device, errno, strerror(errno));
684 IDirectInputDevice2WImpl_Unacquire(iface);
685 return DIERR_NOTFOUND;
687 else
689 /* Couldn't open in r/w but opened in read-only. */
690 WARN("Could not open %s in read-write mode. Force feedback will be disabled.\n", This->joydev->device);
693 else
695 struct input_event event;
697 event.type = EV_FF;
698 event.code = FF_GAIN;
699 event.value = This->ff_gain;
700 if (write(This->joyfd, &event, sizeof(event)) == -1)
701 ERR("Failed to set gain (%i): %d %s\n", This->ff_gain, errno, strerror(errno));
702 if (!This->ff_autocenter)
704 /* Disable autocenter. */
705 event.code = FF_AUTOCENTER;
706 event.value = 0;
707 if (write(This->joyfd, &event, sizeof(event)) == -1)
708 ERR("Failed disabling autocenter: %d %s\n", errno, strerror(errno));
712 return DI_OK;
715 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
717 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
718 return JoystickWImpl_Acquire(IDirectInputDevice8W_from_impl(This));
721 /******************************************************************************
722 * Unacquire : frees the joystick
724 static HRESULT WINAPI JoystickWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
726 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
727 HRESULT res;
729 TRACE("(this=%p)\n",This);
730 res = IDirectInputDevice2WImpl_Unacquire(iface);
731 if (res==DI_OK && This->joyfd!=-1) {
732 effect_list_item *itr;
733 struct input_event event;
735 /* For each known effect:
736 * - stop it
737 * - unload it
738 * But, unlike DISFFC_RESET, do not release the effect.
740 LIST_FOR_EACH_ENTRY(itr, &This->ff_effects, effect_list_item, entry) {
741 IDirectInputEffect_Stop(itr->ref);
742 IDirectInputEffect_Unload(itr->ref);
745 /* Enable autocenter. */
746 event.type = EV_FF;
747 event.code = FF_AUTOCENTER;
748 /* TODO: Read autocenter strength before disabling it, and use it here
749 * instead of 0xFFFF (maximum strength).
751 event.value = 0xFFFF;
752 if (write(This->joyfd, &event, sizeof(event)) == -1)
753 ERR("Failed to set autocenter to %04x: %d %s\n", event.value, errno, strerror(errno));
755 close(This->joyfd);
756 This->joyfd = -1;
758 return res;
761 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
763 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
764 return JoystickWImpl_Unacquire(IDirectInputDevice8W_from_impl(This));
768 * set the current state of the js device as it would be with the middle
769 * values on the axes
771 #define CENTER_AXIS(a) \
772 (ji->dev_axes_to_di[a] == -1 ? 0 : joystick_map_axis( &ji->generic.props[ji->dev_axes_to_di[a]], \
773 ji->joydev->axes[a].value ))
774 static void fake_current_js_state(JoystickImpl *ji)
776 int i;
778 /* center the axes */
779 ji->generic.js.lX = CENTER_AXIS(ABS_X);
780 ji->generic.js.lY = CENTER_AXIS(ABS_Y);
781 ji->generic.js.lZ = CENTER_AXIS(ABS_Z);
782 ji->generic.js.lRx = CENTER_AXIS(ABS_RX);
783 ji->generic.js.lRy = CENTER_AXIS(ABS_RY);
784 ji->generic.js.lRz = CENTER_AXIS(ABS_RZ);
785 ji->generic.js.rglSlider[0] = CENTER_AXIS(ABS_THROTTLE);
786 ji->generic.js.rglSlider[1] = CENTER_AXIS(ABS_RUDDER);
788 /* POV center is -1 */
789 for (i = 0; i < 4; i++)
790 ji->generic.js.rgdwPOV[i] = -1;
792 #undef CENTER_AXIS
794 /* convert wine format offset to user format object index */
795 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
797 struct pollfd plfd;
798 struct input_event ie;
799 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
801 if (This->joyfd==-1)
802 return;
804 while (1)
806 LONG value = 0;
807 int inst_id = -1;
809 plfd.fd = This->joyfd;
810 plfd.events = POLLIN;
812 if (poll(&plfd,1,0) != 1)
813 return;
815 /* we have one event, so we can read */
816 if (sizeof(ie)!=read(This->joyfd,&ie,sizeof(ie)))
817 return;
819 TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
820 switch (ie.type) {
821 case EV_KEY: /* button */
823 int btn = This->buttons[ie.code];
825 TRACE("(%p) %d -> %d\n", This, ie.code, btn);
826 if (btn & 0x80)
828 btn &= 0x7F;
829 inst_id = DIDFT_MAKEINSTANCE(btn) | DIDFT_PSHBUTTON;
830 This->generic.js.rgbButtons[btn] = value = ie.value ? 0x80 : 0x00;
832 break;
834 case EV_ABS:
836 int axis = This->dev_axes_to_di[ie.code];
838 /* User axis remapping */
839 if (axis < 0) break;
840 axis = This->generic.axis_map[axis];
841 if (axis < 0) break;
843 inst_id = axis < 8 ? DIDFT_MAKEINSTANCE(axis) | DIDFT_ABSAXIS :
844 DIDFT_MAKEINSTANCE(axis - 8) | DIDFT_POV;
845 value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], ie.value);
847 switch (axis) {
848 case 0: This->generic.js.lX = value; break;
849 case 1: This->generic.js.lY = value; break;
850 case 2: This->generic.js.lZ = value; break;
851 case 3: This->generic.js.lRx = value; break;
852 case 4: This->generic.js.lRy = value; break;
853 case 5: This->generic.js.lRz = value; break;
854 case 6: This->generic.js.rglSlider[0] = value; break;
855 case 7: This->generic.js.rglSlider[1] = value; break;
856 case 8: case 9: case 10: case 11:
858 int idx = axis - 8;
860 if (ie.code % 2)
861 This->povs[idx].y = ie.value;
862 else
863 This->povs[idx].x = ie.value;
865 This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
866 break;
868 default:
869 FIXME("unhandled joystick axis event (code %d, value %d)\n",ie.code,ie.value);
871 break;
873 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
874 case EV_FF_STATUS:
875 This->ff_state = ie.value;
876 break;
877 #endif
878 #ifdef EV_SYN
879 case EV_SYN:
880 /* there is nothing to do */
881 break;
882 #endif
883 #ifdef EV_MSC
884 case EV_MSC:
885 /* Ignore */
886 break;
887 #endif
888 default:
889 FIXME("joystick cannot handle type %d event (code %d)\n",ie.type,ie.code);
890 break;
892 if (inst_id >= 0)
893 queue_event(iface, inst_id,
894 value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
898 /******************************************************************************
899 * SetProperty : change input device properties
901 static HRESULT WINAPI JoystickWImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIPROPHEADER ph)
903 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
905 if (!ph) {
906 WARN("invalid argument\n");
907 return DIERR_INVALIDPARAM;
910 TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
911 TRACE("ph.dwSize = %d, ph.dwHeaderSize =%d, ph.dwObj = %d, ph.dwHow= %d\n",
912 ph->dwSize, ph->dwHeaderSize, ph->dwObj, ph->dwHow);
914 if (IS_DIPROP(rguid)) {
915 switch (LOWORD(rguid)) {
916 case (DWORD_PTR)DIPROP_CALIBRATIONMODE: {
917 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
918 FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData);
919 break;
921 case (DWORD_PTR)DIPROP_AUTOCENTER: {
922 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
924 TRACE("autocenter(%d)\n", pd->dwData);
925 This->ff_autocenter = pd->dwData == DIPROPAUTOCENTER_ON;
927 break;
929 case (DWORD_PTR)DIPROP_FFGAIN: {
930 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
932 TRACE("DIPROP_FFGAIN(%d)\n", pd->dwData);
933 This->ff_gain = MulDiv(pd->dwData, 0xFFFF, 10000);
934 if (This->generic.base.acquired) {
935 /* Update immediately. */
936 struct input_event event;
938 event.type = EV_FF;
939 event.code = FF_GAIN;
940 event.value = This->ff_gain;
941 if (write(This->joyfd, &event, sizeof(event)) == -1)
942 ERR("Failed to set gain (%i): %d %s\n", This->ff_gain, errno, strerror(errno));
944 break;
946 default:
947 return JoystickWGenericImpl_SetProperty(iface, rguid, ph);
950 return DI_OK;
953 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPCDIPROPHEADER ph)
955 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
956 return JoystickWImpl_SetProperty(IDirectInputDevice8W_from_impl(This), rguid, ph);
959 /******************************************************************************
960 * GetProperty : get input device properties
962 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
964 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
966 TRACE("(this=%p,%s,%p)\n", iface, debugstr_guid(rguid), pdiph);
967 _dump_DIPROPHEADER(pdiph);
969 if (!IS_DIPROP(rguid)) return DI_OK;
971 switch (LOWORD(rguid)) {
972 case (DWORD_PTR) DIPROP_AUTOCENTER:
974 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
976 pd->dwData = This->ff_autocenter ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF;
977 TRACE("autocenter(%d)\n", pd->dwData);
978 break;
980 case (DWORD_PTR) DIPROP_FFGAIN:
982 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
984 pd->dwData = MulDiv(This->ff_gain, 10000, 0xFFFF);
985 TRACE("DIPROP_FFGAIN(%d)\n", pd->dwData);
986 break;
989 case (DWORD_PTR) DIPROP_VIDPID:
991 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
993 if (!This->joydev->product_id || !This->joydev->vendor_id)
994 return DIERR_UNSUPPORTED;
995 pd->dwData = MAKELONG(This->joydev->vendor_id, This->joydev->product_id);
996 TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData);
997 break;
1000 case (DWORD_PTR) DIPROP_JOYSTICKID:
1002 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1004 pd->dwData = get_joystick_index(&This->generic.base.guid);
1005 TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
1006 break;
1009 default:
1010 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1013 return DI_OK;
1016 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1018 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1019 return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
1022 /******************************************************************************
1023 * CreateEffect - Create a new FF effect with the specified params
1025 static HRESULT WINAPI JoystickWImpl_CreateEffect(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid,
1026 LPCDIEFFECT lpeff, LPDIRECTINPUTEFFECT *ppdef,
1027 LPUNKNOWN pUnkOuter)
1029 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1030 effect_list_item* new_effect = NULL;
1031 HRESULT retval = DI_OK;
1032 #endif
1034 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1035 TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
1037 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
1038 TRACE("not available (compiled w/o ff support)\n");
1039 *ppdef = NULL;
1040 return DI_OK;
1041 #else
1043 if (!(new_effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_effect))))
1044 return DIERR_OUTOFMEMORY;
1046 retval = linuxinput_create_effect(&This->joyfd, rguid, &new_effect->entry, &new_effect->ref);
1047 if (retval != DI_OK)
1049 HeapFree(GetProcessHeap(), 0, new_effect);
1050 return retval;
1053 if (lpeff != NULL)
1055 retval = IDirectInputEffect_SetParameters(new_effect->ref, lpeff, 0);
1057 if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
1059 HeapFree(GetProcessHeap(), 0, new_effect);
1060 return retval;
1064 list_add_tail(&This->ff_effects, &new_effect->entry);
1065 *ppdef = new_effect->ref;
1067 if (pUnkOuter != NULL)
1068 FIXME("Interface aggregation not implemented.\n");
1070 return DI_OK;
1072 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
1075 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid,
1076 LPCDIEFFECT lpeff, LPDIRECTINPUTEFFECT *ppdef,
1077 LPUNKNOWN pUnkOuter)
1079 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1080 return JoystickWImpl_CreateEffect(IDirectInputDevice8W_from_impl(This), rguid, lpeff, ppdef, pUnkOuter);
1083 /*******************************************************************************
1084 * EnumEffects - Enumerate available FF effects
1086 static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
1087 LPDIENUMEFFECTSCALLBACKA lpCallback,
1088 LPVOID pvRef,
1089 DWORD dwEffType)
1091 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1092 DIEFFECTINFOA dei; /* feif */
1093 DWORD type = DIEFT_GETTYPE(dwEffType);
1094 JoystickImpl* This = impl_from_IDirectInputDevice8A(iface);
1096 TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
1098 dei.dwSize = sizeof(DIEFFECTINFOA);
1100 if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1101 && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1102 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1103 (*lpCallback)(&dei, pvRef);
1106 if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1107 && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1108 if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1109 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1110 (*lpCallback)(&dei, pvRef);
1112 if (test_bit(This->joydev->ffbits, FF_SINE)) {
1113 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1114 (*lpCallback)(&dei, pvRef);
1116 if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1117 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1118 (*lpCallback)(&dei, pvRef);
1120 if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1121 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1122 (*lpCallback)(&dei, pvRef);
1124 if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1125 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1126 (*lpCallback)(&dei, pvRef);
1130 if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1131 && test_bit(This->joydev->ffbits, FF_RAMP)) {
1132 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1133 (*lpCallback)(&dei, pvRef);
1136 if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1137 if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1138 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1139 (*lpCallback)(&dei, pvRef);
1141 if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1142 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1143 (*lpCallback)(&dei, pvRef);
1145 if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1146 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1147 (*lpCallback)(&dei, pvRef);
1149 if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1150 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1151 (*lpCallback)(&dei, pvRef);
1155 #endif
1157 return DI_OK;
1160 static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
1161 LPDIENUMEFFECTSCALLBACKW lpCallback,
1162 LPVOID pvRef,
1163 DWORD dwEffType)
1165 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1166 /* seems silly to duplicate all this code but all the structures and functions
1167 * are actually different (A/W) */
1168 DIEFFECTINFOW dei; /* feif */
1169 DWORD type = DIEFT_GETTYPE(dwEffType);
1170 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1171 int xfd = This->joyfd;
1173 TRACE("(this=%p,%p,%d) type=%d fd=%d\n", This, pvRef, dwEffType, type, xfd);
1175 dei.dwSize = sizeof(DIEFFECTINFOW);
1177 if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1178 && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1179 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1180 (*lpCallback)(&dei, pvRef);
1183 if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1184 && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1185 if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1186 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1187 (*lpCallback)(&dei, pvRef);
1189 if (test_bit(This->joydev->ffbits, FF_SINE)) {
1190 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1191 (*lpCallback)(&dei, pvRef);
1193 if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1194 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1195 (*lpCallback)(&dei, pvRef);
1197 if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1198 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1199 (*lpCallback)(&dei, pvRef);
1201 if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1202 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1203 (*lpCallback)(&dei, pvRef);
1207 if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1208 && test_bit(This->joydev->ffbits, FF_RAMP)) {
1209 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1210 (*lpCallback)(&dei, pvRef);
1213 if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1214 if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1215 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1216 (*lpCallback)(&dei, pvRef);
1218 if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1219 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1220 (*lpCallback)(&dei, pvRef);
1222 if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1223 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1224 (*lpCallback)(&dei, pvRef);
1226 if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1227 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1228 (*lpCallback)(&dei, pvRef);
1232 /* return to unacquired state if that's where it was */
1233 if (xfd == -1)
1234 IDirectInputDevice8_Unacquire(iface);
1235 #endif
1237 return DI_OK;
1240 /*******************************************************************************
1241 * GetEffectInfo - Get information about a particular effect
1243 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
1244 LPDIEFFECTINFOA pdei,
1245 REFGUID guid)
1247 JoystickImpl* This = impl_from_IDirectInputDevice8A(iface);
1249 TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1251 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1252 return linuxinput_get_info_A(This->joyfd, guid, pdei);
1253 #else
1254 return DI_OK;
1255 #endif
1258 static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
1259 LPDIEFFECTINFOW pdei,
1260 REFGUID guid)
1262 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1264 TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1266 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1267 return linuxinput_get_info_W(This->joyfd, guid, pdei);
1268 #else
1269 return DI_OK;
1270 #endif
1273 /*******************************************************************************
1274 * GetForceFeedbackState - Get information about the device's FF state
1276 static HRESULT WINAPI JoystickWImpl_GetForceFeedbackState(LPDIRECTINPUTDEVICE8W iface, LPDWORD pdwOut)
1278 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1280 TRACE("(this=%p,%p)\n", This, pdwOut);
1282 (*pdwOut) = 0;
1284 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1285 /* DIGFFS_STOPPED is the only mandatory flag to report */
1286 if (This->ff_state == FF_STATUS_STOPPED)
1287 (*pdwOut) |= DIGFFS_STOPPED;
1288 #endif
1290 return DI_OK;
1293 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(LPDIRECTINPUTDEVICE8A iface, LPDWORD pdwOut)
1295 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1296 return JoystickWImpl_GetForceFeedbackState(IDirectInputDevice8W_from_impl(This), pdwOut);
1299 /*******************************************************************************
1300 * SendForceFeedbackCommand - Send a command to the device's FF system
1302 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags)
1304 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1305 TRACE("(this=%p,%d)\n", This, dwFlags);
1307 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1308 switch (dwFlags)
1310 case DISFFC_STOPALL:
1312 /* Stop all effects */
1313 effect_list_item *itr;
1315 LIST_FOR_EACH_ENTRY(itr, &This->ff_effects, effect_list_item, entry)
1316 IDirectInputEffect_Stop(itr->ref);
1317 break;
1320 case DISFFC_RESET:
1322 effect_list_item *itr, *ptr;
1324 /* Stop, unload, release and free all effects */
1325 /* This returns the device to its "bare" state */
1326 LIST_FOR_EACH_ENTRY_SAFE(itr, ptr, &This->ff_effects, effect_list_item, entry)
1327 IDirectInputEffect_Release(itr->ref);
1328 break;
1330 case DISFFC_PAUSE:
1331 case DISFFC_CONTINUE:
1332 FIXME("No support for Pause or Continue in linux\n");
1333 break;
1335 case DISFFC_SETACTUATORSOFF:
1336 case DISFFC_SETACTUATORSON:
1337 FIXME("No direct actuator control in linux\n");
1338 break;
1340 default:
1341 FIXME("Unknown Force Feedback Command!\n");
1342 return DIERR_INVALIDPARAM;
1344 return DI_OK;
1345 #else
1346 return DIERR_UNSUPPORTED;
1347 #endif
1350 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8A iface, DWORD dwFlags)
1352 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1353 return JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W_from_impl(This), dwFlags);
1356 /*******************************************************************************
1357 * EnumCreatedEffectObjects - Enumerate all the effects that have been
1358 * created for this device.
1360 static HRESULT WINAPI JoystickWImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8W iface,
1361 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1362 LPVOID pvRef, DWORD dwFlags)
1364 /* this function is safe to call on non-ff-enabled builds */
1365 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1366 effect_list_item *itr, *ptr;
1368 TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags);
1370 if (!lpCallback)
1371 return DIERR_INVALIDPARAM;
1373 if (dwFlags != 0)
1374 FIXME("Flags specified, but no flags exist yet (DX9)!\n");
1376 LIST_FOR_EACH_ENTRY_SAFE(itr, ptr, &This->ff_effects, effect_list_item, entry)
1377 (*lpCallback)(itr->ref, pvRef);
1379 return DI_OK;
1382 static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8A iface,
1383 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1384 LPVOID pvRef, DWORD dwFlags)
1386 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1387 return JoystickWImpl_EnumCreatedEffectObjects(IDirectInputDevice8W_from_impl(This), lpCallback, pvRef, dwFlags);
1390 /******************************************************************************
1391 * GetDeviceInfo : get information about a device's identity
1393 static HRESULT WINAPI JoystickAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface,
1394 LPDIDEVICEINSTANCEA pdidi)
1396 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1398 TRACE("(%p) %p\n", This, pdidi);
1400 if (pdidi == NULL) return E_POINTER;
1401 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
1402 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)))
1403 return DIERR_INVALIDPARAM;
1405 fill_joystick_dideviceinstanceA(pdidi, This->generic.base.dinput->dwVersion,
1406 get_joystick_index(&This->generic.base.guid));
1407 return DI_OK;
1410 static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface,
1411 LPDIDEVICEINSTANCEW pdidi)
1413 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1415 TRACE("(%p) %p\n", This, pdidi);
1417 if (pdidi == NULL) return E_POINTER;
1418 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
1419 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW)))
1420 return DIERR_INVALIDPARAM;
1422 fill_joystick_dideviceinstanceW(pdidi, This->generic.base.dinput->dwVersion,
1423 get_joystick_index(&This->generic.base.guid));
1424 return DI_OK;
1427 static const IDirectInputDevice8AVtbl JoystickAvt =
1429 IDirectInputDevice2AImpl_QueryInterface,
1430 IDirectInputDevice2AImpl_AddRef,
1431 IDirectInputDevice2AImpl_Release,
1432 JoystickAGenericImpl_GetCapabilities,
1433 IDirectInputDevice2AImpl_EnumObjects,
1434 JoystickAImpl_GetProperty,
1435 JoystickAImpl_SetProperty,
1436 JoystickAImpl_Acquire,
1437 JoystickAImpl_Unacquire,
1438 JoystickAGenericImpl_GetDeviceState,
1439 IDirectInputDevice2AImpl_GetDeviceData,
1440 IDirectInputDevice2AImpl_SetDataFormat,
1441 IDirectInputDevice2AImpl_SetEventNotification,
1442 IDirectInputDevice2AImpl_SetCooperativeLevel,
1443 JoystickAGenericImpl_GetObjectInfo,
1444 JoystickAImpl_GetDeviceInfo,
1445 IDirectInputDevice2AImpl_RunControlPanel,
1446 IDirectInputDevice2AImpl_Initialize,
1447 JoystickAImpl_CreateEffect,
1448 JoystickAImpl_EnumEffects,
1449 JoystickAImpl_GetEffectInfo,
1450 JoystickAImpl_GetForceFeedbackState,
1451 JoystickAImpl_SendForceFeedbackCommand,
1452 JoystickAImpl_EnumCreatedEffectObjects,
1453 IDirectInputDevice2AImpl_Escape,
1454 JoystickAGenericImpl_Poll,
1455 IDirectInputDevice2AImpl_SendDeviceData,
1456 IDirectInputDevice7AImpl_EnumEffectsInFile,
1457 IDirectInputDevice7AImpl_WriteEffectToFile,
1458 JoystickAGenericImpl_BuildActionMap,
1459 JoystickAGenericImpl_SetActionMap,
1460 IDirectInputDevice8AImpl_GetImageInfo
1463 static const IDirectInputDevice8WVtbl JoystickWvt =
1465 IDirectInputDevice2WImpl_QueryInterface,
1466 IDirectInputDevice2WImpl_AddRef,
1467 IDirectInputDevice2WImpl_Release,
1468 JoystickWGenericImpl_GetCapabilities,
1469 IDirectInputDevice2WImpl_EnumObjects,
1470 JoystickWImpl_GetProperty,
1471 JoystickWImpl_SetProperty,
1472 JoystickWImpl_Acquire,
1473 JoystickWImpl_Unacquire,
1474 JoystickWGenericImpl_GetDeviceState,
1475 IDirectInputDevice2WImpl_GetDeviceData,
1476 IDirectInputDevice2WImpl_SetDataFormat,
1477 IDirectInputDevice2WImpl_SetEventNotification,
1478 IDirectInputDevice2WImpl_SetCooperativeLevel,
1479 JoystickWGenericImpl_GetObjectInfo,
1480 JoystickWImpl_GetDeviceInfo,
1481 IDirectInputDevice2WImpl_RunControlPanel,
1482 IDirectInputDevice2WImpl_Initialize,
1483 JoystickWImpl_CreateEffect,
1484 JoystickWImpl_EnumEffects,
1485 JoystickWImpl_GetEffectInfo,
1486 JoystickWImpl_GetForceFeedbackState,
1487 JoystickWImpl_SendForceFeedbackCommand,
1488 JoystickWImpl_EnumCreatedEffectObjects,
1489 IDirectInputDevice2WImpl_Escape,
1490 JoystickWGenericImpl_Poll,
1491 IDirectInputDevice2WImpl_SendDeviceData,
1492 IDirectInputDevice7WImpl_EnumEffectsInFile,
1493 IDirectInputDevice7WImpl_WriteEffectToFile,
1494 JoystickWGenericImpl_BuildActionMap,
1495 JoystickWGenericImpl_SetActionMap,
1496 IDirectInputDevice8WImpl_GetImageInfo
1499 #else /* HAS_PROPER_HEADER */
1501 const struct dinput_device joystick_linuxinput_device = {
1502 "Wine Linux-input joystick driver",
1503 NULL,
1504 NULL,
1505 NULL
1508 #endif /* HAS_PROPER_HEADER */