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 */ 1636