[BROWSEUI] SHExplorerParseCmdLine: Fix parsing of /root (#6752)
[reactos.git] / dll / directx / wine / dinput / joystick_linuxinput.c
1 /* DirectInput Joystick device
2 *
3 * Copyright 1998,2000 Marcus Meissner
4 * Copyright 1998,1999 Lionel Ulmer
5 * Copyright 2000-2001 TransGaming Technologies Inc.
6 * Copyright 2005 Daniel Remenak
7 *
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.
12 *
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.
17 *
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
21 */
22
23 #include "config.h"
24 #include "wine/port.h"
25
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
52
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 "devguid.h"
61 #include "dinput.h"
62
63 #include "dinput_private.h"
64 #include "device_private.h"
65 #include "joystick_private.h"
66
67 #ifdef HAS_PROPER_HEADER
68
69 WINE_DEFAULT_DEBUG_CHANNEL(dinput);
70
71 #define EVDEVPREFIX "/dev/input/event"
72 #define EVDEVDRIVER " (event)"
73
74 /* Wine joystick driver object instances */
75 #define WINE_JOYSTICK_MAX_AXES 8
76 #define WINE_JOYSTICK_MAX_POVS 4
77 #define WINE_JOYSTICK_MAX_BUTTONS 128
78
79 struct wine_input_absinfo {
80 LONG value;
81 LONG minimum;
82 LONG maximum;
83 LONG fuzz;
84 LONG flat;
85 };
86
87 enum wine_joystick_linuxinput_fd_state {
88 WINE_FD_STATE_CLOSED = 0, /* No device has been opened yet */
89 WINE_FD_STATE_OK, /* File descriptor is open and ready for reading */
90 WINE_FD_STATE_DISCONNECTED, /* Read error occurred; might be able to reopen later */
91 WINE_FD_STATE_INVALID, /* Device is no longer available at original pathname */
92 };
93
94 /* implemented in effect_linuxinput.c */
95 HRESULT linuxinput_create_effect(int* fd, REFGUID rguid, struct list *parent_list_entry, LPDIRECTINPUTEFFECT* peff);
96 HRESULT linuxinput_get_info_A(int fd, REFGUID rguid, LPDIEFFECTINFOA info);
97 HRESULT linuxinput_get_info_W(int fd, REFGUID rguid, LPDIEFFECTINFOW info);
98
99 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags);
100
101 typedef struct JoystickImpl JoystickImpl;
102 static const IDirectInputDevice8AVtbl JoystickAvt;
103 static const IDirectInputDevice8WVtbl JoystickWvt;
104
105 struct JoyDev {
106 char *device;
107 char *name;
108 GUID guid;
109 GUID guid_product;
110
111 BOOL has_ff, is_joystick;
112 int num_effects;
113
114 /* data returned by EVIOCGBIT for caps, EV_ABS, EV_KEY, and EV_FF */
115 BYTE evbits[(EV_MAX+7)/8];
116 BYTE absbits[(ABS_MAX+7)/8];
117 BYTE keybits[(KEY_MAX+7)/8];
118 BYTE ffbits[(FF_MAX+7)/8];
119
120 /* data returned by the EVIOCGABS() ioctl */
121 struct wine_input_absinfo axes[ABS_MAX];
122
123 WORD vendor_id, product_id, bus_type;
124 };
125
126 struct JoystickImpl
127 {
128 struct JoystickGenericImpl generic;
129 struct JoyDev *joydev;
130
131 /* joystick private */
132 int joyfd;
133 enum wine_joystick_linuxinput_fd_state joyfd_state;
134
135 int dev_axes_to_di[ABS_MAX];
136 POINTL povs[4];
137
138 /* LUT for KEY_ to offset in rgbButtons */
139 BYTE buttons[KEY_MAX];
140
141 /* Force feedback variables */
142 struct list ff_effects;
143 int ff_state;
144 int ff_autocenter;
145 int ff_gain;
146 };
147
148 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface)
149 {
150 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface),
151 JoystickGenericImpl, base), JoystickImpl, generic);
152 }
153 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface)
154 {
155 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface),
156 JoystickGenericImpl, base), JoystickImpl, generic);
157 }
158
159 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This)
160 {
161 return &This->generic.base.IDirectInputDevice8W_iface;
162 }
163
164 static void fake_current_js_state(JoystickImpl *ji);
165 static void find_joydevs(void);
166 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface);
167
168 /* This GUID is slightly different from the linux joystick one. Take note. */
169 static const GUID DInput_Wine_Joystick_Base_GUID = { /* 9e573eda-7734-11d2-8d4a-23903fb6bdf7 */
170 0x9e573eda,
171 0x7734,
172 0x11d2,
173 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7}
174 };
175
176 #define test_bit(arr,bit) (((BYTE*)(arr))[(bit)>>3]&(1<<((bit)&7)))
177
178 #define MAX_JOYDEV 64
179
180 static int have_joydevs = -1;
181 static struct JoyDev *joydevs = NULL;
182
183 static void find_joydevs(void)
184 {
185 int i;
186
187 if (InterlockedCompareExchange(&have_joydevs, 0, -1) != -1)
188 /* Someone beat us to it */
189 return;
190
191 for (i = 0; i < MAX_JOYDEV; i++)
192 {
193 char buf[MAX_PATH];
194 struct JoyDev joydev = {0};
195 int fd;
196 BOOL no_ff_check = FALSE;
197 int j;
198 struct JoyDev *new_joydevs;
199 struct input_id device_id = {0};
200
201 snprintf(buf, sizeof(buf), EVDEVPREFIX"%d", i);
202
203 if ((fd = open(buf, O_RDWR)) == -1)
204 {
205 fd = open(buf, O_RDONLY);
206 no_ff_check = TRUE;
207 }
208
209 if (fd == -1)
210 continue;
211
212 if (ioctl(fd, EVIOCGBIT(0, sizeof(joydev.evbits)), joydev.evbits) == -1)
213 {
214 WARN("ioctl(EVIOCGBIT, 0) failed: %d %s\n", errno, strerror(errno));
215 close(fd);
216 continue;
217 }
218 if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(joydev.absbits)), joydev.absbits) == -1)
219 {
220 WARN("ioctl(EVIOCGBIT, EV_ABS) failed: %d %s\n", errno, strerror(errno));
221 close(fd);
222 continue;
223 }
224 if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(joydev.keybits)), joydev.keybits) == -1)
225 {
226 WARN("ioctl(EVIOCGBIT, EV_KEY) failed: %d %s\n", errno, strerror(errno));
227 close(fd);
228 continue;
229 }
230
231 /* A true joystick has at least axis X and Y, and at least 1
232 * button. copied from linux/drivers/input/joydev.c */
233 if (((!test_bit(joydev.absbits, ABS_X) || !test_bit(joydev.absbits, ABS_Y)) &&
234 !test_bit(joydev.absbits, ABS_WHEEL) &&
235 !test_bit(joydev.absbits, ABS_GAS) &&
236 !test_bit(joydev.absbits, ABS_BRAKE)) ||
237 !(test_bit(joydev.keybits, BTN_TRIGGER) ||
238 test_bit(joydev.keybits, BTN_A) ||
239 test_bit(joydev.keybits, BTN_1) ||
240 test_bit(joydev.keybits, BTN_BASE) ||
241 test_bit(joydev.keybits, BTN_GEAR_UP) ||
242 test_bit(joydev.keybits, BTN_GEAR_DOWN)))
243 {
244 close(fd);
245 continue;
246 }
247
248 /* in lieu of properly reporting HID usage, detect presence of
249 * "joystick buttons" and report those devices as joysticks instead of
250 * gamepads */
251 joydev.is_joystick =
252 test_bit(joydev.keybits, BTN_TRIGGER) ||
253 test_bit(joydev.keybits, BTN_THUMB) ||
254 test_bit(joydev.keybits, BTN_THUMB2) ||
255 test_bit(joydev.keybits, BTN_TOP) ||
256 test_bit(joydev.keybits, BTN_TOP2) ||
257 test_bit(joydev.keybits, BTN_PINKIE) ||
258 test_bit(joydev.keybits, BTN_BASE) ||
259 test_bit(joydev.keybits, BTN_BASE2) ||
260 test_bit(joydev.keybits, BTN_BASE3) ||
261 test_bit(joydev.keybits, BTN_BASE4) ||
262 test_bit(joydev.keybits, BTN_BASE5) ||
263 test_bit(joydev.keybits, BTN_BASE6) ||
264 test_bit(joydev.keybits, BTN_GEAR_UP) ||
265 test_bit(joydev.keybits, BTN_GEAR_DOWN) ||
266 test_bit(joydev.keybits, BTN_DEAD);
267
268 if (!(joydev.device = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + 1)))
269 {
270 close(fd);
271 continue;
272 }
273 strcpy(joydev.device, buf);
274
275 buf[MAX_PATH - 1] = 0;
276 if (ioctl(fd, EVIOCGNAME(MAX_PATH - 1), buf) != -1 &&
277 (joydev.name = HeapAlloc(GetProcessHeap(), 0, strlen(buf) + strlen(EVDEVDRIVER) + 1)))
278 {
279 strcpy(joydev.name, buf);
280 /* Append driver name */
281 strcat(joydev.name, EVDEVDRIVER);
282 }
283 else
284 joydev.name = joydev.device;
285
286 if (device_disabled_registry(joydev.name)) {
287 close(fd);
288 HeapFree(GetProcessHeap(), 0, joydev.name);
289 if (joydev.name != joydev.device)
290 HeapFree(GetProcessHeap(), 0, joydev.device);
291 continue;
292 }
293
294 joydev.guid = DInput_Wine_Joystick_Base_GUID;
295 joydev.guid.Data3 += have_joydevs;
296
297 TRACE("Found a joystick on %s: %s (%s)\n",
298 joydev.device, joydev.name,
299 debugstr_guid(&joydev.guid)
300 );
301
302 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
303 if (!no_ff_check &&
304 test_bit(joydev.evbits, EV_FF) &&
305 ioctl(fd, EVIOCGBIT(EV_FF, sizeof(joydev.ffbits)), joydev.ffbits) != -1 &&
306 ioctl(fd, EVIOCGEFFECTS, &joydev.num_effects) != -1 &&
307 joydev.num_effects > 0)
308 {
309 TRACE(" ... with force feedback\n");
310 joydev.has_ff = TRUE;
311 }
312 #endif
313
314 for (j = 0; j < ABS_MAX;j ++)
315 {
316 if (!test_bit(joydev.absbits, j)) continue;
317 if (ioctl(fd, EVIOCGABS(j), &(joydev.axes[j])) != -1)
318 {
319 TRACE(" ... with axis %d: cur=%d, min=%d, max=%d, fuzz=%d, flat=%d\n",
320 j,
321 joydev.axes[j].value,
322 joydev.axes[j].minimum,
323 joydev.axes[j].maximum,
324 joydev.axes[j].fuzz,
325 joydev.axes[j].flat
326 );
327 }
328 }
329
330 if (ioctl(fd, EVIOCGID, &device_id) == -1)
331 {
332 WARN("ioctl(EVIOCGID) failed: %d %s\n", errno, strerror(errno));
333 joydev.guid_product = DInput_Wine_Joystick_Base_GUID;
334 }
335 else
336 {
337 joydev.vendor_id = device_id.vendor;
338 joydev.product_id = device_id.product;
339 joydev.bus_type = device_id.bustype;
340
341 /* Concatenate product_id with vendor_id to mimic Windows behaviour */
342 joydev.guid_product = DInput_PIDVID_Product_GUID;
343 joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id);
344 }
345
346 if (!have_joydevs)
347 new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev));
348 else
349 new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joydevs, (1 + have_joydevs) * sizeof(struct JoyDev));
350
351 if (!new_joydevs)
352 {
353 close(fd);
354 continue;
355 }
356 joydevs = new_joydevs;
357 joydevs[have_joydevs] = joydev;
358 have_joydevs++;
359
360 close(fd);
361 }
362 }
363
364 static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
365 {
366 DWORD dwSize = lpddi->dwSize;
367
368 TRACE("%d %p\n", dwSize, lpddi);
369 memset(lpddi, 0, dwSize);
370
371 lpddi->dwSize = dwSize;
372 lpddi->guidInstance = joydevs[id].guid;
373 lpddi->guidProduct = joydevs[id].guid_product;
374 lpddi->guidFFDriver = GUID_NULL;
375 lpddi->dwDevType = get_device_type(version, joydevs[id].is_joystick);
376
377 /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */
378 if (joydevs[id].bus_type == BUS_USB &&
379 joydevs[id].vendor_id && joydevs[id].product_id)
380 {
381 lpddi->dwDevType |= DIDEVTYPE_HID;
382 lpddi->wUsagePage = 0x01; /* Desktop */
383 if (joydevs[id].is_joystick)
384 lpddi->wUsage = 0x04; /* Joystick */
385 else
386 lpddi->wUsage = 0x05; /* Game Pad */
387 }
388
389 MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszInstanceName, MAX_PATH);
390 MultiByteToWideChar(CP_ACP, 0, joydevs[id].name, -1, lpddi->tszProductName, MAX_PATH);
391 }
392
393 static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
394 {
395 DIDEVICEINSTANCEW lpddiW;
396 DWORD dwSize = lpddi->dwSize;
397
398 lpddiW.dwSize = sizeof(lpddiW);
399 fill_joystick_dideviceinstanceW(&lpddiW, version, id);
400
401 TRACE("%d %p\n", dwSize, lpddi);
402 memset(lpddi, 0, dwSize);
403
404 /* Convert W->A */
405 lpddi->dwSize = dwSize;
406 lpddi->guidInstance = lpddiW.guidInstance;
407 lpddi->guidProduct = lpddiW.guidProduct;
408 lpddi->dwDevType = lpddiW.dwDevType;
409 lstrcpynA(lpddi->tszInstanceName, joydevs[id].name, MAX_PATH);
410 lstrcpynA(lpddi->tszProductName, joydevs[id].name, MAX_PATH);
411 lpddi->guidFFDriver = lpddiW.guidFFDriver;
412 lpddi->wUsagePage = lpddiW.wUsagePage;
413 lpddi->wUsage = lpddiW.wUsage;
414 }
415
416 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id)
417 {
418 find_joydevs();
419
420 if (id >= have_joydevs) {
421 return E_FAIL;
422 }
423
424 if (!((dwDevType == 0) ||
425 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
426 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
427 return S_FALSE;
428
429 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
430 if (dwFlags & DIEDFL_FORCEFEEDBACK)
431 return S_FALSE;
432 #endif
433
434 if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
435 fill_joystick_dideviceinstanceA(lpddi, version, id);
436 return S_OK;
437 }
438 return S_FALSE;
439 }
440
441 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id)
442 {
443 find_joydevs();
444
445 if (id >= have_joydevs) {
446 return E_FAIL;
447 }
448
449 if (!((dwDevType == 0) ||
450 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) ||
451 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))))
452 return S_FALSE;
453
454 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
455 if (dwFlags & DIEDFL_FORCEFEEDBACK)
456 return S_FALSE;
457 #endif
458
459 if (!(dwFlags & DIEDFL_FORCEFEEDBACK) || joydevs[id].has_ff) {
460 fill_joystick_dideviceinstanceW(lpddi, version, id);
461 return S_OK;
462 }
463 return S_FALSE;
464 }
465
466 static JoystickImpl *alloc_device(REFGUID rguid, IDirectInputImpl *dinput, unsigned short index)
467 {
468 JoystickImpl* newDevice;
469 LPDIDATAFORMAT df = NULL;
470 int i, idx = 0;
471 int default_axis_map[WINE_JOYSTICK_MAX_AXES + WINE_JOYSTICK_MAX_POVS*2];
472 DIDEVICEINSTANCEW ddi;
473
474 newDevice = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JoystickImpl));
475 if (!newDevice) return NULL;
476
477 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt;
478 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt;
479 newDevice->generic.base.ref = 1;
480 newDevice->generic.base.guid = *rguid;
481 newDevice->generic.base.dinput = dinput;
482 newDevice->generic.joy_polldev = joy_polldev;
483 newDevice->joyfd = -1;
484 newDevice->joyfd_state = WINE_FD_STATE_CLOSED;
485 newDevice->joydev = &joydevs[index];
486 newDevice->generic.name = newDevice->joydev->name;
487 list_init(&newDevice->ff_effects);
488 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
489 newDevice->ff_state = FF_STATUS_STOPPED;
490 #endif
491 /* There is no way in linux to query force feedback autocenter status.
492 Instead, track it with ff_autocenter, and assume it's initially
493 enabled. */
494 newDevice->ff_autocenter = 1;
495 newDevice->ff_gain = 0xFFFF;
496 InitializeCriticalSection(&newDevice->generic.base.crit);
497 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->base.crit");
498
499 /* Count number of available axes - supported Axis & POVs */
500 for (i = 0; i < ABS_MAX; i++)
501 {
502 if (idx < WINE_JOYSTICK_MAX_AXES &&
503 i < ABS_HAT0X &&
504 test_bit(newDevice->joydev->absbits, i))
505 {
506 newDevice->generic.device_axis_count++;
507 newDevice->dev_axes_to_di[i] = idx;
508 newDevice->generic.props[idx].lDevMin = newDevice->joydev->axes[i].minimum;
509 newDevice->generic.props[idx].lDevMax = newDevice->joydev->axes[i].maximum;
510 if (i >= 8 && i <= 10) /* If it's a wheel axis... */
511 default_axis_map[idx] = i - 8; /* ... remap to X/Y/Z */
512 else
513 default_axis_map[idx] = i;
514 idx++;
515 }
516 else
517 newDevice->dev_axes_to_di[i] = -1;
518 }
519
520 for (i = 0; i < WINE_JOYSTICK_MAX_POVS; i++)
521 {
522 if (test_bit(newDevice->joydev->absbits, ABS_HAT0X + i * 2) &&
523 test_bit(newDevice->joydev->absbits, ABS_HAT0Y + i * 2))
524 {
525 newDevice->generic.device_axis_count += 2;
526 newDevice->generic.props[idx ].lDevMin = newDevice->joydev->axes[ABS_HAT0X + i * 2].minimum;
527 newDevice->generic.props[idx ].lDevMax = newDevice->joydev->axes[ABS_HAT0X + i * 2].maximum;
528 newDevice->dev_axes_to_di[ABS_HAT0X + i * 2] = idx;
529 newDevice->generic.props[idx+1].lDevMin = newDevice->joydev->axes[ABS_HAT0Y + i * 2].minimum;
530 newDevice->generic.props[idx+1].lDevMax = newDevice->joydev->axes[ABS_HAT0Y + i * 2].maximum;
531 newDevice->dev_axes_to_di[ABS_HAT0Y + i * 2] = idx + 1;
532
533 default_axis_map[idx] = default_axis_map[idx + 1] = WINE_JOYSTICK_MAX_AXES + i;
534 idx += 2;
535 }
536 else
537 newDevice->dev_axes_to_di[ABS_HAT0X + i * 2] = newDevice->dev_axes_to_di[ABS_HAT0Y + i * 2] = -1;
538 }
539
540 /* do any user specified configuration */
541 if (setup_dinput_options(&newDevice->generic, default_axis_map) != DI_OK) goto failed;
542
543 /* Create copy of default data format */
544 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto failed;
545 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize);
546 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto failed;
547
548
549 /* Construct internal data format */
550
551 /* Supported Axis & POVs */
552 for (i = 0, idx = 0; i < newDevice->generic.device_axis_count; i++)
553 {
554 int wine_obj = newDevice->generic.axis_map[i];
555
556 if (wine_obj < 0) continue;
557
558 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize);
559 if (wine_obj < 8)
560 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS;
561 else
562 {
563 df->rgodf[idx].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV;
564 i++; /* POV takes 2 axes */
565 }
566
567 newDevice->generic.props[idx].lMin = 0;
568 newDevice->generic.props[idx].lMax = 0xffff;
569 newDevice->generic.props[idx].lSaturation = 0;
570 newDevice->generic.props[idx].lDeadZone = newDevice->generic.deadzone;
571
572 /* Linux supports force-feedback on X & Y axes only */
573 if (newDevice->joydev->has_ff && (i == 0 || i == 1))
574 df->rgodf[idx].dwFlags |= DIDOI_FFACTUATOR;
575
576 idx++;
577 }
578
579 /* Buttons can be anywhere, so check all */
580 for (i = 0; i < KEY_MAX && newDevice->generic.devcaps.dwButtons < WINE_JOYSTICK_MAX_BUTTONS; i++)
581 {
582 if (!test_bit(newDevice->joydev->keybits, i)) continue;
583
584 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[newDevice->generic.devcaps.dwButtons + 12], df->dwObjSize);
585 newDevice->buttons[i] = 0x80 | newDevice->generic.devcaps.dwButtons;
586 df->rgodf[idx ].pguid = &GUID_Button;
587 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(newDevice->generic.devcaps.dwButtons++) | DIDFT_PSHBUTTON;
588 }
589 df->dwNumObjs = idx;
590 newDevice->generic.base.data_format.wine_df = df;
591
592 fake_current_js_state(newDevice);
593
594 /* Fill the caps */
595 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps);
596 newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED;
597
598 ddi.dwSize = sizeof(ddi);
599 fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index);
600 newDevice->generic.devcaps.dwDevType = ddi.dwDevType;
601
602 if (newDevice->joydev->has_ff)
603 newDevice->generic.devcaps.dwFlags |= DIDC_FORCEFEEDBACK;
604
605 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface);
606
607 EnterCriticalSection(&dinput->crit);
608 list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry);
609 LeaveCriticalSection(&dinput->crit);
610
611 return newDevice;
612
613 failed:
614 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf);
615 HeapFree(GetProcessHeap(), 0, df);
616 HeapFree(GetProcessHeap(), 0, newDevice->generic.axis_map);
617 HeapFree(GetProcessHeap(), 0, newDevice);
618 return NULL;
619 }
620
621 /******************************************************************************
622 * get_joystick_index : Get the joystick index from a given GUID
623 */
624 static unsigned short get_joystick_index(REFGUID guid)
625 {
626 GUID wine_joystick = DInput_Wine_Joystick_Base_GUID;
627 GUID dev_guid = *guid;
628
629 wine_joystick.Data3 = 0;
630 dev_guid.Data3 = 0;
631
632 /* for the standard joystick GUID use index 0 */
633 if(IsEqualGUID(&GUID_Joystick,guid)) return 0;
634
635 /* for the wine joystick GUIDs use the index stored in Data3 */
636 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3 - DInput_Wine_Joystick_Base_GUID.Data3;
637
638 return MAX_JOYDEV;
639 }
640
641 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode)
642 {
643 unsigned short index;
644
645 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode);
646 find_joydevs();
647 *pdev = NULL;
648
649 if ((index = get_joystick_index(rguid)) < MAX_JOYDEV &&
650 have_joydevs && index < have_joydevs)
651 {
652 JoystickImpl *This;
653
654 if (riid == NULL)
655 ;/* nothing */
656 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) ||
657 IsEqualGUID(&IID_IDirectInputDevice2A, riid) ||
658 IsEqualGUID(&IID_IDirectInputDevice7A, riid) ||
659 IsEqualGUID(&IID_IDirectInputDevice8A, riid))
660 {
661 unicode = 0;
662 }
663 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) ||
664 IsEqualGUID(&IID_IDirectInputDevice2W, riid) ||
665 IsEqualGUID(&IID_IDirectInputDevice7W, riid) ||
666 IsEqualGUID(&IID_IDirectInputDevice8W, riid))
667 {
668 unicode = 1;
669 }
670 else
671 {
672 WARN("no interface\n");
673 return DIERR_NOINTERFACE;
674 }
675
676 This = alloc_device(rguid, dinput, index);
677 TRACE("Created a Joystick device (%p)\n", This);
678
679 if (!This) return DIERR_OUTOFMEMORY;
680
681 if (unicode)
682 *pdev = &This->generic.base.IDirectInputDevice8W_iface;
683 else
684 *pdev = &This->generic.base.IDirectInputDevice8A_iface;
685
686 return DI_OK;
687 }
688
689 return DIERR_DEVICENOTREG;
690 }
691
692 static int joydev_open_evdev(JoystickImpl *This)
693 {
694 int fd;
695
696 if ((fd = open(This->joydev->device, O_RDWR)) == -1)
697 {
698 if ((fd = open(This->joydev->device, O_RDONLY)) == -1)
699 {
700 /* Couldn't open the device at all */
701 }
702 else
703 {
704 /* Couldn't open in r/w but opened in read-only. */
705 WARN("Could not open %s in read-write mode. Force feedback will be disabled.\n", This->joydev->device);
706 }
707 }
708 else
709 {
710 struct input_event event;
711
712 event.type = EV_FF;
713 event.code = FF_GAIN;
714 event.value = This->ff_gain;
715 if (write(fd, &event, sizeof(event)) == -1)
716 ERR("Failed to set gain (%i): %d %s\n", This->ff_gain, errno, strerror(errno));
717 if (!This->ff_autocenter)
718 {
719 /* Disable autocenter. */
720 event.code = FF_AUTOCENTER;
721 event.value = 0;
722 if (write(fd, &event, sizeof(event)) == -1)
723 ERR("Failed disabling autocenter: %d %s\n", errno, strerror(errno));
724 }
725 }
726
727 return fd;
728 }
729
730
731 const struct dinput_device joystick_linuxinput_device = {
732 "Wine Linux-input joystick driver",
733 joydev_enum_deviceA,
734 joydev_enum_deviceW,
735 joydev_create_device
736 };
737
738 /******************************************************************************
739 * Acquire : gets exclusive control of the joystick
740 */
741 static HRESULT WINAPI JoystickWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface)
742 {
743 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
744 HRESULT res;
745
746 TRACE("(this=%p)\n",This);
747
748 if ((res = IDirectInputDevice2WImpl_Acquire(iface)) != DI_OK)
749 {
750 WARN("Failed to acquire: %x\n", res);
751 return res;
752 }
753
754 if ((This->joyfd = joydev_open_evdev(This)) == -1)
755 {
756 ERR("Failed to open device %s: %d %s\n", This->joydev->device, errno, strerror(errno));
757 IDirectInputDevice2WImpl_Unacquire(iface);
758 return DIERR_NOTFOUND;
759 }
760
761 This->joyfd_state = WINE_FD_STATE_OK;
762 return DI_OK;
763 }
764
765 static HRESULT WINAPI JoystickAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface)
766 {
767 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
768 return JoystickWImpl_Acquire(IDirectInputDevice8W_from_impl(This));
769 }
770
771 /******************************************************************************
772 * Unacquire : frees the joystick
773 */
774 static HRESULT WINAPI JoystickWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface)
775 {
776 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
777 HRESULT res;
778
779 TRACE("(this=%p)\n",This);
780 res = IDirectInputDevice2WImpl_Unacquire(iface);
781 if (res==DI_OK && This->joyfd!=-1) {
782 struct input_event event;
783
784 /* Stop and unload all effects */
785 JoystickWImpl_SendForceFeedbackCommand(iface, DISFFC_RESET);
786
787 /* Enable autocenter. */
788 event.type = EV_FF;
789 event.code = FF_AUTOCENTER;
790 /* TODO: Read autocenter strength before disabling it, and use it here
791 * instead of 0xFFFF (maximum strength).
792 */
793 event.value = 0xFFFF;
794 if (write(This->joyfd, &event, sizeof(event)) == -1)
795 ERR("Failed to set autocenter to %04x: %d %s\n", event.value, errno, strerror(errno));
796
797 close(This->joyfd);
798 This->joyfd = -1;
799 This->joyfd_state = WINE_FD_STATE_CLOSED;
800 }
801 return res;
802 }
803
804 static HRESULT WINAPI JoystickAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface)
805 {
806 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
807 return JoystickWImpl_Unacquire(IDirectInputDevice8W_from_impl(This));
808 }
809
810 /*
811 * set the current state of the js device as it would be with the middle
812 * values on the axes
813 */
814 #define CENTER_AXIS(a) \
815 (ji->dev_axes_to_di[a] == -1 ? 0 : joystick_map_axis( &ji->generic.props[ji->dev_axes_to_di[a]], \
816 ji->joydev->axes[a].value ))
817 static void fake_current_js_state(JoystickImpl *ji)
818 {
819 int i;
820
821 /* center the axes */
822 ji->generic.js.lX = CENTER_AXIS(ABS_X);
823 ji->generic.js.lY = CENTER_AXIS(ABS_Y);
824 ji->generic.js.lZ = CENTER_AXIS(ABS_Z);
825 ji->generic.js.lRx = CENTER_AXIS(ABS_RX);
826 ji->generic.js.lRy = CENTER_AXIS(ABS_RY);
827 ji->generic.js.lRz = CENTER_AXIS(ABS_RZ);
828 ji->generic.js.rglSlider[0] = CENTER_AXIS(ABS_THROTTLE);
829 ji->generic.js.rglSlider[1] = CENTER_AXIS(ABS_RUDDER);
830
831 /* POV center is -1 */
832 for (i = 0; i < 4; i++)
833 ji->generic.js.rgdwPOV[i] = -1;
834 }
835 #undef CENTER_AXIS
836
837 /* convert wine format offset to user format object index */
838 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface)
839 {
840 struct pollfd plfd;
841 struct input_event ie;
842 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
843
844 if (This->joyfd == -1)
845 {
846 int fd;
847 char namebuf[MAX_PATH + 8]; /* 8 == strlen(EVDEVDRIVER) */
848
849 if (This->joyfd_state != WINE_FD_STATE_DISCONNECTED)
850 return;
851 /* Try to reconnect to the device. */
852 fd = joydev_open_evdev(This);
853 if (fd == -1)
854 return;
855 namebuf[sizeof(namebuf) - strlen(EVDEVDRIVER) - 1] = 0;
856 if (ioctl(fd, EVIOCGNAME(sizeof(namebuf) - strlen(EVDEVDRIVER) - 1), namebuf) == -1)
857 {
858 /* Couldn't get the name; assume it's a different device. */
859 ERR("EVIOCGNAME(%s) failed: %d %s", This->joydev->device, errno, strerror(errno));
860 This->joyfd_state = WINE_FD_STATE_INVALID;
861 return;
862 }
863 strcat(namebuf, EVDEVDRIVER); /* Guaranteed to be safe. */
864 if (strcmp(namebuf, This->joydev->name) != 0)
865 {
866 ERR("Device %s changed from \"%s\" to \"%s\"! Can't reconnect.\n", This->joydev->device, This->joydev->name, namebuf);
867 This->joyfd_state = WINE_FD_STATE_INVALID;
868 return;
869 }
870 if (InterlockedCompareExchange(&This->joyfd, fd, -1) == -1)
871 {
872 This->joyfd_state = WINE_FD_STATE_OK;
873 TRACE("Reconnected to \"%s\" on %s", This->joydev->name, This->joydev->device);
874 }
875 else
876 {
877 /* Somebody beat us to it! Throw away our fd and use theirs. */
878 close(fd);
879 }
880 }
881
882 while (1)
883 {
884 LONG value = 0;
885 int inst_id = -1;
886 int result;
887
888 plfd.fd = This->joyfd;
889 plfd.events = POLLIN;
890
891 result = poll(&plfd,1,0);
892 if (result != 1)
893 {
894 if (result == -1)
895 {
896 ERR("poll failed: %d %s\n", errno, strerror(errno));
897 close(This->joyfd);
898 This->joyfd = -1;
899 This->joyfd_state = WINE_FD_STATE_DISCONNECTED;
900 }
901 return;
902 }
903
904 /* we have one event, so we can read */
905 result = read(This->joyfd,&ie,sizeof(ie));
906 if (result != sizeof(ie))
907 {
908 if (result == -1)
909 {
910 ERR("read failed: %d %s\n", errno, strerror(errno));
911 close(This->joyfd);
912 This->joyfd = -1;
913 This->joyfd_state = WINE_FD_STATE_DISCONNECTED;
914 }
915 return;
916 }
917
918 TRACE("input_event: type %d, code %d, value %d\n",ie.type,ie.code,ie.value);
919 switch (ie.type) {
920 case EV_KEY: /* button */
921 {
922 int btn = This->buttons[ie.code];
923
924 TRACE("(%p) %d -> %d\n", This, ie.code, btn);
925 if (btn & 0x80)
926 {
927 btn &= 0x7F;
928 btn = This->generic.button_map[btn];
929
930 inst_id = DIDFT_MAKEINSTANCE(btn) | DIDFT_PSHBUTTON;
931 This->generic.js.rgbButtons[btn] = value = ie.value ? 0x80 : 0x00;
932 }
933 break;
934 }
935 case EV_ABS:
936 {
937 int axis = This->dev_axes_to_di[ie.code];
938
939 /* User axis remapping */
940 if (axis < 0) break;
941 axis = This->generic.axis_map[axis];
942 if (axis < 0) break;
943
944 inst_id = axis < 8 ? DIDFT_MAKEINSTANCE(axis) | DIDFT_ABSAXIS :
945 DIDFT_MAKEINSTANCE(axis - 8) | DIDFT_POV;
946 value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], ie.value);
947
948 switch (axis) {
949 case 0: This->generic.js.lX = value; break;
950 case 1: This->generic.js.lY = value; break;
951 case 2: This->generic.js.lZ = value; break;
952 case 3: This->generic.js.lRx = value; break;
953 case 4: This->generic.js.lRy = value; break;
954 case 5: This->generic.js.lRz = value; break;
955 case 6: This->generic.js.rglSlider[0] = value; break;
956 case 7: This->generic.js.rglSlider[1] = value; break;
957 case 8: case 9: case 10: case 11:
958 {
959 int idx = axis - 8;
960
961 if (ie.code % 2)
962 This->povs[idx].y = ie.value;
963 else
964 This->povs[idx].x = ie.value;
965
966 This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]);
967 break;
968 }
969 default:
970 FIXME("unhandled joystick axis event (code %d, value %d)\n",ie.code,ie.value);
971 }
972 break;
973 }
974 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
975 case EV_FF_STATUS:
976 This->ff_state = ie.value;
977 break;
978 #endif
979 #ifdef EV_SYN
980 case EV_SYN:
981 /* there is nothing to do */
982 break;
983 #endif
984 #ifdef EV_MSC
985 case EV_MSC:
986 /* Ignore */
987 break;
988 #endif
989 default:
990 TRACE("skipping event\n");
991 break;
992 }
993 if (inst_id >= 0)
994 queue_event(iface, inst_id,
995 value, GetCurrentTime(), This->generic.base.dinput->evsequence++);
996 }
997 }
998
999 /******************************************************************************
1000 * SetProperty : change input device properties
1001 */
1002 static HRESULT WINAPI JoystickWImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIPROPHEADER ph)
1003 {
1004 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1005
1006 if (!ph) {
1007 WARN("invalid argument\n");
1008 return DIERR_INVALIDPARAM;
1009 }
1010
1011 TRACE("(this=%p,%s,%p)\n",This,debugstr_guid(rguid),ph);
1012 TRACE("ph.dwSize = %d, ph.dwHeaderSize =%d, ph.dwObj = %d, ph.dwHow= %d\n",
1013 ph->dwSize, ph->dwHeaderSize, ph->dwObj, ph->dwHow);
1014
1015 if (IS_DIPROP(rguid)) {
1016 switch (LOWORD(rguid)) {
1017 case (DWORD_PTR)DIPROP_AUTOCENTER: {
1018 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
1019
1020 TRACE("autocenter(%d)\n", pd->dwData);
1021 This->ff_autocenter = pd->dwData == DIPROPAUTOCENTER_ON;
1022
1023 break;
1024 }
1025 case (DWORD_PTR)DIPROP_FFGAIN: {
1026 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph;
1027
1028 TRACE("DIPROP_FFGAIN(%d)\n", pd->dwData);
1029 This->ff_gain = MulDiv(pd->dwData, 0xFFFF, 10000);
1030 if (This->generic.base.acquired) {
1031 /* Update immediately. */
1032 struct input_event event;
1033
1034 event.type = EV_FF;
1035 event.code = FF_GAIN;
1036 event.value = This->ff_gain;
1037 if (write(This->joyfd, &event, sizeof(event)) == -1)
1038 ERR("Failed to set gain (%i): %d %s\n", This->ff_gain, errno, strerror(errno));
1039 }
1040 break;
1041 }
1042 default:
1043 return JoystickWGenericImpl_SetProperty(iface, rguid, ph);
1044 }
1045 }
1046 return DI_OK;
1047 }
1048
1049 static HRESULT WINAPI JoystickAImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPCDIPROPHEADER ph)
1050 {
1051 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1052 return JoystickWImpl_SetProperty(IDirectInputDevice8W_from_impl(This), rguid, ph);
1053 }
1054
1055 /******************************************************************************
1056 * GetProperty : get input device properties
1057 */
1058 static HRESULT WINAPI JoystickWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1059 {
1060 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1061
1062 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph);
1063 _dump_DIPROPHEADER(pdiph);
1064
1065 if (!IS_DIPROP(rguid)) return DI_OK;
1066
1067 switch (LOWORD(rguid)) {
1068 case (DWORD_PTR) DIPROP_AUTOCENTER:
1069 {
1070 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1071
1072 pd->dwData = This->ff_autocenter ? DIPROPAUTOCENTER_ON : DIPROPAUTOCENTER_OFF;
1073 TRACE("autocenter(%d)\n", pd->dwData);
1074 break;
1075 }
1076 case (DWORD_PTR) DIPROP_FFGAIN:
1077 {
1078 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1079
1080 pd->dwData = MulDiv(This->ff_gain, 10000, 0xFFFF);
1081 TRACE("DIPROP_FFGAIN(%d)\n", pd->dwData);
1082 break;
1083 }
1084
1085 case (DWORD_PTR) DIPROP_VIDPID:
1086 {
1087 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1088
1089 if (!This->joydev->product_id || !This->joydev->vendor_id)
1090 return DIERR_UNSUPPORTED;
1091 pd->dwData = MAKELONG(This->joydev->vendor_id, This->joydev->product_id);
1092 TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData);
1093 break;
1094 }
1095
1096 case (DWORD_PTR) DIPROP_JOYSTICKID:
1097 {
1098 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph;
1099
1100 pd->dwData = get_joystick_index(&This->generic.base.guid);
1101 TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData);
1102 break;
1103 }
1104
1105 case (DWORD_PTR) DIPROP_GUIDANDPATH:
1106 {
1107 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&',
1108 'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0};
1109 static const WCHAR miW[] = {'m','i',0};
1110 static const WCHAR igW[] = {'i','g',0};
1111
1112 BOOL is_gamepad;
1113 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph;
1114 WORD vid = This->joydev->vendor_id;
1115 WORD pid = This->joydev->product_id;
1116
1117 if (!pid || !vid)
1118 return DIERR_UNSUPPORTED;
1119
1120 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid);
1121 pd->guidClass = GUID_DEVCLASS_HIDCLASS;
1122 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid));
1123
1124 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath));
1125 break;
1126 }
1127
1128 default:
1129 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph);
1130 }
1131
1132 return DI_OK;
1133 }
1134
1135 static HRESULT WINAPI JoystickAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph)
1136 {
1137 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1138 return JoystickWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph);
1139 }
1140
1141 /******************************************************************************
1142 * CreateEffect - Create a new FF effect with the specified params
1143 */
1144 static HRESULT WINAPI JoystickWImpl_CreateEffect(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid,
1145 LPCDIEFFECT lpeff, LPDIRECTINPUTEFFECT *ppdef,
1146 LPUNKNOWN pUnkOuter)
1147 {
1148 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1149 effect_list_item* new_effect = NULL;
1150 HRESULT retval = DI_OK;
1151 #endif
1152
1153 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1154 TRACE("(this=%p,%p,%p,%p,%p)\n", This, rguid, lpeff, ppdef, pUnkOuter);
1155
1156 *ppdef = NULL;
1157 if (!This->joydev->has_ff)
1158 {
1159 TRACE("No force feedback support\n");
1160 return DIERR_UNSUPPORTED;
1161 }
1162
1163 #ifndef HAVE_STRUCT_FF_EFFECT_DIRECTION
1164 TRACE("not available (compiled w/o force feedback support)\n");
1165 return DIERR_UNSUPPORTED;
1166 #else
1167
1168 if (!(new_effect = HeapAlloc(GetProcessHeap(), 0, sizeof(*new_effect))))
1169 return DIERR_OUTOFMEMORY;
1170
1171 retval = linuxinput_create_effect(&This->joyfd, rguid, &new_effect->entry, &new_effect->ref);
1172 if (retval != DI_OK)
1173 {
1174 HeapFree(GetProcessHeap(), 0, new_effect);
1175 return retval;
1176 }
1177
1178 if (lpeff != NULL)
1179 {
1180 retval = IDirectInputEffect_SetParameters(new_effect->ref, lpeff, 0);
1181
1182 if (retval != DI_OK && retval != DI_DOWNLOADSKIPPED)
1183 {
1184 HeapFree(GetProcessHeap(), 0, new_effect);
1185 return retval;
1186 }
1187 }
1188
1189 list_add_tail(&This->ff_effects, &new_effect->entry);
1190 *ppdef = new_effect->ref;
1191
1192 if (pUnkOuter != NULL)
1193 FIXME("Interface aggregation not implemented.\n");
1194
1195 return DI_OK;
1196
1197 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */
1198 }
1199
1200 static HRESULT WINAPI JoystickAImpl_CreateEffect(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid,
1201 LPCDIEFFECT lpeff, LPDIRECTINPUTEFFECT *ppdef,
1202 LPUNKNOWN pUnkOuter)
1203 {
1204 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1205 return JoystickWImpl_CreateEffect(IDirectInputDevice8W_from_impl(This), rguid, lpeff, ppdef, pUnkOuter);
1206 }
1207
1208 /*******************************************************************************
1209 * EnumEffects - Enumerate available FF effects
1210 */
1211 static HRESULT WINAPI JoystickAImpl_EnumEffects(LPDIRECTINPUTDEVICE8A iface,
1212 LPDIENUMEFFECTSCALLBACKA lpCallback,
1213 LPVOID pvRef,
1214 DWORD dwEffType)
1215 {
1216 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1217 DIEFFECTINFOA dei; /* feif */
1218 DWORD type = DIEFT_GETTYPE(dwEffType);
1219 JoystickImpl* This = impl_from_IDirectInputDevice8A(iface);
1220
1221 TRACE("(this=%p,%p,%d) type=%d\n", This, pvRef, dwEffType, type);
1222
1223 dei.dwSize = sizeof(DIEFFECTINFOA);
1224
1225 if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1226 && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1227 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1228 (*lpCallback)(&dei, pvRef);
1229 }
1230
1231 if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1232 && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1233 if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1234 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1235 (*lpCallback)(&dei, pvRef);
1236 }
1237 if (test_bit(This->joydev->ffbits, FF_SINE)) {
1238 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1239 (*lpCallback)(&dei, pvRef);
1240 }
1241 if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1242 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1243 (*lpCallback)(&dei, pvRef);
1244 }
1245 if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1246 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1247 (*lpCallback)(&dei, pvRef);
1248 }
1249 if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1250 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1251 (*lpCallback)(&dei, pvRef);
1252 }
1253 }
1254
1255 if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1256 && test_bit(This->joydev->ffbits, FF_RAMP)) {
1257 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1258 (*lpCallback)(&dei, pvRef);
1259 }
1260
1261 if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1262 if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1263 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1264 (*lpCallback)(&dei, pvRef);
1265 }
1266 if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1267 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1268 (*lpCallback)(&dei, pvRef);
1269 }
1270 if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1271 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1272 (*lpCallback)(&dei, pvRef);
1273 }
1274 if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1275 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1276 (*lpCallback)(&dei, pvRef);
1277 }
1278 }
1279
1280 #endif
1281
1282 return DI_OK;
1283 }
1284
1285 static HRESULT WINAPI JoystickWImpl_EnumEffects(LPDIRECTINPUTDEVICE8W iface,
1286 LPDIENUMEFFECTSCALLBACKW lpCallback,
1287 LPVOID pvRef,
1288 DWORD dwEffType)
1289 {
1290 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1291 /* seems silly to duplicate all this code but all the structures and functions
1292 * are actually different (A/W) */
1293 DIEFFECTINFOW dei; /* feif */
1294 DWORD type = DIEFT_GETTYPE(dwEffType);
1295 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1296 int xfd = This->joyfd;
1297
1298 TRACE("(this=%p,%p,%d) type=%d fd=%d\n", This, pvRef, dwEffType, type, xfd);
1299
1300 dei.dwSize = sizeof(DIEFFECTINFOW);
1301
1302 if ((type == DIEFT_ALL || type == DIEFT_CONSTANTFORCE)
1303 && test_bit(This->joydev->ffbits, FF_CONSTANT)) {
1304 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_ConstantForce);
1305 (*lpCallback)(&dei, pvRef);
1306 }
1307
1308 if ((type == DIEFT_ALL || type == DIEFT_PERIODIC)
1309 && test_bit(This->joydev->ffbits, FF_PERIODIC)) {
1310 if (test_bit(This->joydev->ffbits, FF_SQUARE)) {
1311 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Square);
1312 (*lpCallback)(&dei, pvRef);
1313 }
1314 if (test_bit(This->joydev->ffbits, FF_SINE)) {
1315 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Sine);
1316 (*lpCallback)(&dei, pvRef);
1317 }
1318 if (test_bit(This->joydev->ffbits, FF_TRIANGLE)) {
1319 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Triangle);
1320 (*lpCallback)(&dei, pvRef);
1321 }
1322 if (test_bit(This->joydev->ffbits, FF_SAW_UP)) {
1323 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothUp);
1324 (*lpCallback)(&dei, pvRef);
1325 }
1326 if (test_bit(This->joydev->ffbits, FF_SAW_DOWN)) {
1327 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_SawtoothDown);
1328 (*lpCallback)(&dei, pvRef);
1329 }
1330 }
1331
1332 if ((type == DIEFT_ALL || type == DIEFT_RAMPFORCE)
1333 && test_bit(This->joydev->ffbits, FF_RAMP)) {
1334 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_RampForce);
1335 (*lpCallback)(&dei, pvRef);
1336 }
1337
1338 if (type == DIEFT_ALL || type == DIEFT_CONDITION) {
1339 if (test_bit(This->joydev->ffbits, FF_SPRING)) {
1340 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Spring);
1341 (*lpCallback)(&dei, pvRef);
1342 }
1343 if (test_bit(This->joydev->ffbits, FF_DAMPER)) {
1344 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Damper);
1345 (*lpCallback)(&dei, pvRef);
1346 }
1347 if (test_bit(This->joydev->ffbits, FF_INERTIA)) {
1348 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Inertia);
1349 (*lpCallback)(&dei, pvRef);
1350 }
1351 if (test_bit(This->joydev->ffbits, FF_FRICTION)) {
1352 IDirectInputDevice8_GetEffectInfo(iface, &dei, &GUID_Friction);
1353 (*lpCallback)(&dei, pvRef);
1354 }
1355 }
1356
1357 /* return to unacquired state if that's where it was */
1358 if (xfd == -1)
1359 IDirectInputDevice8_Unacquire(iface);
1360 #endif
1361
1362 return DI_OK;
1363 }
1364
1365 /*******************************************************************************
1366 * GetEffectInfo - Get information about a particular effect
1367 */
1368 static HRESULT WINAPI JoystickAImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8A iface,
1369 LPDIEFFECTINFOA pdei,
1370 REFGUID guid)
1371 {
1372 JoystickImpl* This = impl_from_IDirectInputDevice8A(iface);
1373
1374 TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1375
1376 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1377 return linuxinput_get_info_A(This->joyfd, guid, pdei);
1378 #else
1379 return DI_OK;
1380 #endif
1381 }
1382
1383 static HRESULT WINAPI JoystickWImpl_GetEffectInfo(LPDIRECTINPUTDEVICE8W iface,
1384 LPDIEFFECTINFOW pdei,
1385 REFGUID guid)
1386 {
1387 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1388
1389 TRACE("(this=%p,%p,%s)\n", This, pdei, _dump_dinput_GUID(guid));
1390
1391 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1392 return linuxinput_get_info_W(This->joyfd, guid, pdei);
1393 #else
1394 return DI_OK;
1395 #endif
1396 }
1397
1398 /*******************************************************************************
1399 * GetForceFeedbackState - Get information about the device's FF state
1400 */
1401 static HRESULT WINAPI JoystickWImpl_GetForceFeedbackState(LPDIRECTINPUTDEVICE8W iface, LPDWORD pdwOut)
1402 {
1403 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1404
1405 TRACE("(this=%p,%p)\n", This, pdwOut);
1406
1407 (*pdwOut) = 0;
1408
1409 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1410 /* DIGFFS_STOPPED is the only mandatory flag to report */
1411 if (This->ff_state == FF_STATUS_STOPPED)
1412 (*pdwOut) |= DIGFFS_STOPPED;
1413 #endif
1414
1415 return DI_OK;
1416 }
1417
1418 static HRESULT WINAPI JoystickAImpl_GetForceFeedbackState(LPDIRECTINPUTDEVICE8A iface, LPDWORD pdwOut)
1419 {
1420 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1421 return JoystickWImpl_GetForceFeedbackState(IDirectInputDevice8W_from_impl(This), pdwOut);
1422 }
1423
1424 /*******************************************************************************
1425 * SendForceFeedbackCommand - Send a command to the device's FF system
1426 */
1427 static HRESULT WINAPI JoystickWImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8W iface, DWORD dwFlags)
1428 {
1429 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1430 TRACE("(this=%p,%d)\n", This, dwFlags);
1431
1432 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION
1433 switch (dwFlags)
1434 {
1435 case DISFFC_STOPALL:
1436 {
1437 effect_list_item *itr;
1438
1439 /* Stop all effects */
1440 LIST_FOR_EACH_ENTRY(itr, &This->ff_effects, effect_list_item, entry)
1441 IDirectInputEffect_Stop(itr->ref);
1442 break;
1443 }
1444
1445 case DISFFC_RESET:
1446 {
1447 effect_list_item *itr;
1448
1449 /* Stop and unload all effects. It is not true that effects are released */
1450 LIST_FOR_EACH_ENTRY(itr, &This->ff_effects, effect_list_item, entry)
1451 {
1452 IDirectInputEffect_Stop(itr->ref);
1453 IDirectInputEffect_Unload(itr->ref);
1454 }
1455 break;
1456 }
1457 case DISFFC_PAUSE:
1458 case DISFFC_CONTINUE:
1459 FIXME("No support for Pause or Continue in linux\n");
1460 break;
1461
1462 case DISFFC_SETACTUATORSOFF:
1463 case DISFFC_SETACTUATORSON:
1464 FIXME("No direct actuator control in linux\n");
1465 break;
1466
1467 default:
1468 WARN("Unknown Force Feedback Command %u!\n", dwFlags);
1469 return DIERR_INVALIDPARAM;
1470 }
1471 return DI_OK;
1472 #else
1473 return DIERR_UNSUPPORTED;
1474 #endif
1475 }
1476
1477 static HRESULT WINAPI JoystickAImpl_SendForceFeedbackCommand(LPDIRECTINPUTDEVICE8A iface, DWORD dwFlags)
1478 {
1479 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1480 return JoystickWImpl_SendForceFeedbackCommand(IDirectInputDevice8W_from_impl(This), dwFlags);
1481 }
1482
1483 /*******************************************************************************
1484 * EnumCreatedEffectObjects - Enumerate all the effects that have been
1485 * created for this device.
1486 */
1487 static HRESULT WINAPI JoystickWImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8W iface,
1488 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1489 LPVOID pvRef, DWORD dwFlags)
1490 {
1491 /* this function is safe to call on non-ff-enabled builds */
1492 JoystickImpl* This = impl_from_IDirectInputDevice8W(iface);
1493 effect_list_item *itr, *ptr;
1494
1495 TRACE("(this=%p,%p,%p,%d)\n", This, lpCallback, pvRef, dwFlags);
1496
1497 if (!lpCallback)
1498 return DIERR_INVALIDPARAM;
1499
1500 if (dwFlags != 0)
1501 FIXME("Flags specified, but no flags exist yet (DX9)!\n");
1502
1503 LIST_FOR_EACH_ENTRY_SAFE(itr, ptr, &This->ff_effects, effect_list_item, entry)
1504 (*lpCallback)(itr->ref, pvRef);
1505
1506 return DI_OK;
1507 }
1508
1509 static HRESULT WINAPI JoystickAImpl_EnumCreatedEffectObjects(LPDIRECTINPUTDEVICE8A iface,
1510 LPDIENUMCREATEDEFFECTOBJECTSCALLBACK lpCallback,
1511 LPVOID pvRef, DWORD dwFlags)
1512 {
1513 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1514 return JoystickWImpl_EnumCreatedEffectObjects(IDirectInputDevice8W_from_impl(This), lpCallback, pvRef, dwFlags);
1515 }
1516
1517 /******************************************************************************
1518 * GetDeviceInfo : get information about a device's identity
1519 */
1520 static HRESULT WINAPI JoystickAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface,
1521 LPDIDEVICEINSTANCEA pdidi)
1522 {
1523 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface);
1524
1525 TRACE("(%p) %p\n", This, pdidi);
1526
1527 if (pdidi == NULL) return E_POINTER;
1528 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) &&
1529 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA)))
1530 return DIERR_INVALIDPARAM;
1531
1532 fill_joystick_dideviceinstanceA(pdidi, This->generic.base.dinput->dwVersion,
1533 get_joystick_index(&This->generic.base.guid));
1534 return DI_OK;
1535 }
1536
1537 static HRESULT WINAPI JoystickWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface,
1538 LPDIDEVICEINSTANCEW pdidi)
1539 {
1540 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface);
1541
1542 TRACE("(%p) %p\n", This, pdidi);
1543
1544 if (pdidi == NULL) return E_POINTER;
1545 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) &&
1546 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW)))
1547 return DIERR_INVALIDPARAM;
1548
1549 fill_joystick_dideviceinstanceW(pdidi, This->generic.base.dinput->dwVersion,
1550 get_joystick_index(&This->generic.base.guid));
1551 return DI_OK;
1552 }
1553
1554 static const IDirectInputDevice8AVtbl JoystickAvt =
1555 {
1556 IDirectInputDevice2AImpl_QueryInterface,
1557 IDirectInputDevice2AImpl_AddRef,
1558 IDirectInputDevice2AImpl_Release,
1559 JoystickAGenericImpl_GetCapabilities,
1560 IDirectInputDevice2AImpl_EnumObjects,
1561 JoystickAImpl_GetProperty,
1562 JoystickAImpl_SetProperty,
1563 JoystickAImpl_Acquire,
1564 JoystickAImpl_Unacquire,
1565 JoystickAGenericImpl_GetDeviceState,
1566 IDirectInputDevice2AImpl_GetDeviceData,
1567 IDirectInputDevice2AImpl_SetDataFormat,
1568 IDirectInputDevice2AImpl_SetEventNotification,
1569 IDirectInputDevice2AImpl_SetCooperativeLevel,
1570 JoystickAGenericImpl_GetObjectInfo,
1571 JoystickAImpl_GetDeviceInfo,
1572 IDirectInputDevice2AImpl_RunControlPanel,
1573 IDirectInputDevice2AImpl_Initialize,
1574 JoystickAImpl_CreateEffect,
1575 JoystickAImpl_EnumEffects,
1576 JoystickAImpl_GetEffectInfo,
1577 JoystickAImpl_GetForceFeedbackState,
1578 JoystickAImpl_SendForceFeedbackCommand,
1579 JoystickAImpl_EnumCreatedEffectObjects,
1580 IDirectInputDevice2AImpl_Escape,
1581 JoystickAGenericImpl_Poll,
1582 IDirectInputDevice2AImpl_SendDeviceData,
1583 IDirectInputDevice7AImpl_EnumEffectsInFile,
1584 IDirectInputDevice7AImpl_WriteEffectToFile,
1585 JoystickAGenericImpl_BuildActionMap,
1586 JoystickAGenericImpl_SetActionMap,
1587 IDirectInputDevice8AImpl_GetImageInfo
1588 };
1589
1590 static const IDirectInputDevice8WVtbl JoystickWvt =
1591 {
1592 IDirectInputDevice2WImpl_QueryInterface,
1593 IDirectInputDevice2WImpl_AddRef,
1594 IDirectInputDevice2WImpl_Release,
1595 JoystickWGenericImpl_GetCapabilities,
1596 IDirectInputDevice2WImpl_EnumObjects,
1597 JoystickWImpl_GetProperty,
1598 JoystickWImpl_SetProperty,
1599 JoystickWImpl_Acquire,
1600 JoystickWImpl_Unacquire,
1601 JoystickWGenericImpl_GetDeviceState,
1602 IDirectInputDevice2WImpl_GetDeviceData,
1603 IDirectInputDevice2WImpl_SetDataFormat,
1604 IDirectInputDevice2WImpl_SetEventNotification,
1605 IDirectInputDevice2WImpl_SetCooperativeLevel,
1606 JoystickWGenericImpl_GetObjectInfo,
1607 JoystickWImpl_GetDeviceInfo,
1608 IDirectInputDevice2WImpl_RunControlPanel,
1609 IDirectInputDevice2WImpl_Initialize,
1610 JoystickWImpl_CreateEffect,
1611 JoystickWImpl_EnumEffects,
1612 JoystickWImpl_GetEffectInfo,
1613 JoystickWImpl_GetForceFeedbackState,
1614 JoystickWImpl_SendForceFeedbackCommand,
1615 JoystickWImpl_EnumCreatedEffectObjects,
1616 IDirectInputDevice2WImpl_Escape,
1617 JoystickWGenericImpl_Poll,
1618 IDirectInputDevice2WImpl_SendDeviceData,
1619 IDirectInputDevice7WImpl_EnumEffectsInFile,
1620 IDirectInputDevice7WImpl_WriteEffectToFile,
1621 JoystickWGenericImpl_BuildActionMap,
1622 JoystickWGenericImpl_SetActionMap,
1623 IDirectInputDevice8WImpl_GetImageInfo
1624 };
1625
1626 #else /* HAS_PROPER_HEADER */
1627
1628 const struct dinput_device joystick_linuxinput_device = {
1629 "Wine Linux-input joystick driver",
1630 NULL,
1631 NULL,
1632 NULL
1633 };
1634
1635 #endif /* HAS_PROPER_HEADER */