1 /* DirectInput Joystick device 2 * 3 * Copyright 1998 Marcus Meissner 4 * Copyright 1998,1999 Lionel Ulmer 5 * Copyright 2000-2001 TransGaming Technologies Inc. 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 /* 23 * To Do: 24 * dead zone 25 * force feedback 26 */ 27 28 #include "config.h" 29 #include "wine/port.h" 30 31 #include <stdarg.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include <time.h> 35 #ifdef HAVE_UNISTD_H 36 # include <unistd.h> 37 #endif 38 #ifdef HAVE_SYS_TIME_H 39 # include <sys/time.h> 40 #endif 41 #include <fcntl.h> 42 #ifdef HAVE_SYS_IOCTL_H 43 # include <sys/ioctl.h> 44 #endif 45 #include <errno.h> 46 #ifdef HAVE_LINUX_IOCTL_H 47 # include <linux/ioctl.h> 48 #endif 49 #ifdef HAVE_LINUX_JOYSTICK_H 50 # include <linux/joystick.h> 51 # undef SW_MAX 52 #endif 53 #ifdef HAVE_SYS_POLL_H 54 # include <sys/poll.h> 55 #endif 56 57 #include "wine/debug.h" 58 #include "wine/unicode.h" 59 #include "windef.h" 60 #include "winbase.h" 61 #include "winerror.h" 62 #include "devguid.h" 63 #include "dinput.h" 64 65 #include "dinput_private.h" 66 #include "device_private.h" 67 #include "joystick_private.h" 68 69 #ifdef HAVE_LINUX_22_JOYSTICK_API 70 71 WINE_DEFAULT_DEBUG_CHANNEL(dinput); 72 73 #define JOYDEV_NEW "/dev/input/js" 74 #define JOYDEV_OLD "/dev/js" 75 #define JOYDEVDRIVER " (js)" 76 77 struct JoyDev 78 { 79 char device[MAX_PATH]; 80 char name[MAX_PATH]; 81 GUID guid_product; 82 83 BYTE axis_count; 84 BYTE button_count; 85 int *dev_axes_map; 86 87 WORD vendor_id, product_id, bus_type; 88 89 BOOL is_joystick; 90 }; 91 92 typedef struct JoystickImpl JoystickImpl; 93 static const IDirectInputDevice8AVtbl JoystickAvt; 94 static const IDirectInputDevice8WVtbl JoystickWvt; 95 struct JoystickImpl 96 { 97 struct JoystickGenericImpl generic; 98 99 struct JoyDev *joydev; 100 101 /* joystick private */ 102 int joyfd; 103 POINTL povs[4]; 104 }; 105 106 static inline JoystickImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface) 107 { 108 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface), 109 JoystickGenericImpl, base), JoystickImpl, generic); 110 } 111 static inline JoystickImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface) 112 { 113 return CONTAINING_RECORD(CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface), 114 JoystickGenericImpl, base), JoystickImpl, generic); 115 } 116 117 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickImpl *This) 118 { 119 return &This->generic.base.IDirectInputDevice8W_iface; 120 } 121 122 static const GUID DInput_Wine_Joystick_GUID = { /* 9e573ed9-7734-11d2-8d4a-23903fb6bdf7 */ 123 0x9e573ed9, 124 0x7734, 125 0x11d2, 126 {0x8d, 0x4a, 0x23, 0x90, 0x3f, 0xb6, 0xbd, 0xf7} 127 }; 128 129 #define MAX_JOYSTICKS 64 130 static INT joystick_devices_count = -1; 131 static struct JoyDev *joystick_devices; 132 133 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface); 134 135 #define SYS_PATH_FORMAT "/sys/class/input/js%d/device/id/%s" 136 static BOOL read_sys_id_variable(int index, const char *property, WORD *value) 137 { 138 char sys_path[sizeof(SYS_PATH_FORMAT) + 16], id_str[5]; 139 int sys_fd; 140 BOOL ret = FALSE; 141 142 sprintf(sys_path, SYS_PATH_FORMAT, index, property); 143 if ((sys_fd = open(sys_path, O_RDONLY)) != -1) 144 { 145 if (read(sys_fd, id_str, 4) == 4) 146 { 147 id_str[4] = '\0'; 148 *value = strtol(id_str, NULL, 16); 149 ret = TRUE; 150 } 151 152 close(sys_fd); 153 } 154 return ret; 155 } 156 #undef SYS_PATH_FORMAT 157 158 static INT find_joystick_devices(void) 159 { 160 INT i; 161 162 if (joystick_devices_count != -1) return joystick_devices_count; 163 164 joystick_devices_count = 0; 165 for (i = 0; i < MAX_JOYSTICKS; i++) 166 { 167 int fd; 168 struct JoyDev joydev, *new_joydevs; 169 BYTE axes_map[ABS_MAX + 1]; 170 SHORT btn_map[KEY_MAX - BTN_MISC + 1]; 171 BOOL is_stylus = FALSE; 172 173 snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_NEW, i); 174 if ((fd = open(joydev.device, O_RDONLY)) == -1) 175 { 176 snprintf(joydev.device, sizeof(joydev.device), "%s%d", JOYDEV_OLD, i); 177 if ((fd = open(joydev.device, O_RDONLY)) == -1) continue; 178 } 179 180 strcpy(joydev.name, "Wine Joystick"); 181 #if defined(JSIOCGNAME) 182 if (ioctl(fd, JSIOCGNAME(sizeof(joydev.name) - sizeof(JOYDEVDRIVER)), joydev.name) < 0) 183 WARN("ioctl(%s,JSIOCGNAME) failed: %s\n", joydev.device, strerror(errno)); 184 #endif 185 186 /* Append driver name */ 187 strcat(joydev.name, JOYDEVDRIVER); 188 189 if (device_disabled_registry(joydev.name)) { 190 close(fd); 191 continue; 192 } 193 194 #ifdef JSIOCGAXES 195 if (ioctl(fd, JSIOCGAXES, &joydev.axis_count) < 0) 196 { 197 WARN("ioctl(%s,JSIOCGAXES) failed: %s, defaulting to 2\n", joydev.device, strerror(errno)); 198 joydev.axis_count = 2; 199 } 200 #else 201 WARN("reading number of joystick axes unsupported in this platform, defaulting to 2\n"); 202 joydev.axis_count = 2; 203 #endif 204 #ifdef JSIOCGBUTTONS 205 if (ioctl(fd, JSIOCGBUTTONS, &joydev.button_count) < 0) 206 { 207 WARN("ioctl(%s,JSIOCGBUTTONS) failed: %s, defaulting to 2\n", joydev.device, strerror(errno)); 208 joydev.button_count = 2; 209 } 210 #else 211 WARN("reading number of joystick buttons unsupported in this platform, defaulting to 2\n"); 212 joydev.button_count = 2; 213 #endif 214 215 joydev.is_joystick = FALSE; 216 if (ioctl(fd, JSIOCGBTNMAP, btn_map) < 0) 217 { 218 WARN("ioctl(%s,JSIOCGBTNMAP) failed: %s\n", joydev.device, strerror(errno)); 219 } 220 else 221 { 222 INT j; 223 /* in lieu of properly reporting HID usage, detect presence of 224 * "joystick buttons" and report those devices as joysticks instead of 225 * gamepads */ 226 for (j = 0; !joydev.is_joystick && j < joydev.button_count; j++) 227 { 228 switch (btn_map[j]) 229 { 230 case BTN_TRIGGER: 231 case BTN_THUMB: 232 case BTN_THUMB2: 233 case BTN_TOP: 234 case BTN_TOP2: 235 case BTN_PINKIE: 236 case BTN_BASE: 237 case BTN_BASE2: 238 case BTN_BASE3: 239 case BTN_BASE4: 240 case BTN_BASE5: 241 case BTN_BASE6: 242 case BTN_DEAD: 243 joydev.is_joystick = TRUE; 244 break; 245 case BTN_STYLUS: 246 is_stylus = TRUE; 247 break; 248 default: 249 break; 250 } 251 } 252 } 253 254 if(is_stylus) 255 { 256 TRACE("Stylus detected. Skipping\n"); 257 close(fd); 258 continue; 259 } 260 261 if (ioctl(fd, JSIOCGAXMAP, axes_map) < 0) 262 { 263 WARN("ioctl(%s,JSIOCGAXMAP) failed: %s\n", joydev.device, strerror(errno)); 264 joydev.dev_axes_map = NULL; 265 } 266 else 267 if ((joydev.dev_axes_map = HeapAlloc(GetProcessHeap(), 0, joydev.axis_count * sizeof(int)))) 268 { 269 INT j, found_axes = 0; 270 271 /* Remap to DI numbers */ 272 for (j = 0; j < joydev.axis_count; j++) 273 { 274 if (axes_map[j] < 8) 275 { 276 /* Axis match 1-to-1 */ 277 joydev.dev_axes_map[j] = j; 278 found_axes++; 279 } 280 else if (axes_map[j] <= 10) 281 { 282 /* Axes 8 through 10 are Wheel, Gas and Brake, 283 * remap to 0, 1 and 2 284 */ 285 joydev.dev_axes_map[j] = axes_map[j] - 8; 286 found_axes++; 287 } 288 else if (axes_map[j] == 16 || 289 axes_map[j] == 17) 290 { 291 /* POV axis */ 292 joydev.dev_axes_map[j] = 8; 293 found_axes++; 294 } 295 else 296 joydev.dev_axes_map[j] = -1; 297 } 298 299 /* If no axes were configured but there are axes assume a 1-to-1 (wii controller) */ 300 if (joydev.axis_count && !found_axes) 301 { 302 int axes_limit = min(joydev.axis_count, 8); /* generic driver limit */ 303 304 ERR("Incoherent joystick data, advertised %d axes, detected 0. Assuming 1-to-1.\n", 305 joydev.axis_count); 306 for (j = 0; j < axes_limit; j++) 307 joydev.dev_axes_map[j] = j; 308 309 joydev.axis_count = axes_limit; 310 } 311 } 312 313 /* Find vendor_id and product_id in sysfs */ 314 joydev.vendor_id = 0; 315 joydev.product_id = 0; 316 317 read_sys_id_variable(i, "vendor", &joydev.vendor_id); 318 read_sys_id_variable(i, "product", &joydev.product_id); 319 read_sys_id_variable(i, "bustype", &joydev.bus_type); 320 321 if (joydev.vendor_id == 0 || joydev.product_id == 0) 322 { 323 joydev.guid_product = DInput_Wine_Joystick_GUID; 324 } 325 else 326 { 327 /* Concatenate product_id with vendor_id to mimic Windows behaviour */ 328 joydev.guid_product = DInput_PIDVID_Product_GUID; 329 joydev.guid_product.Data1 = MAKELONG(joydev.vendor_id, joydev.product_id); 330 } 331 332 close(fd); 333 334 if (!joystick_devices_count) 335 new_joydevs = HeapAlloc(GetProcessHeap(), 0, sizeof(struct JoyDev)); 336 else 337 new_joydevs = HeapReAlloc(GetProcessHeap(), 0, joystick_devices, 338 (joystick_devices_count + 1) * sizeof(struct JoyDev)); 339 if (!new_joydevs) continue; 340 341 TRACE("Found a joystick on %s: %s\n with %d axes and %d buttons\n", joydev.device, 342 joydev.name, joydev.axis_count, joydev.button_count); 343 344 joystick_devices = new_joydevs; 345 joystick_devices[joystick_devices_count++] = joydev; 346 } 347 348 return joystick_devices_count; 349 } 350 351 static void fill_joystick_dideviceinstanceW(LPDIDEVICEINSTANCEW lpddi, DWORD version, int id) 352 { 353 DWORD dwSize = lpddi->dwSize; 354 355 TRACE("%d %p\n", dwSize, lpddi); 356 memset(lpddi, 0, dwSize); 357 358 /* Return joystick */ 359 lpddi->dwSize = dwSize; 360 lpddi->guidInstance = DInput_Wine_Joystick_GUID; 361 lpddi->guidInstance.Data3 = id; 362 lpddi->guidProduct = joystick_devices[id].guid_product; 363 lpddi->dwDevType = get_device_type(version, joystick_devices[id].is_joystick); 364 365 /* Assume the joystick as HID if it is attached to USB bus and has a valid VID/PID */ 366 if (joystick_devices[id].bus_type == BUS_USB && 367 joystick_devices[id].vendor_id && joystick_devices[id].product_id) 368 { 369 lpddi->dwDevType |= DIDEVTYPE_HID; 370 lpddi->wUsagePage = 0x01; /* Desktop */ 371 if (joystick_devices[id].is_joystick) 372 lpddi->wUsage = 0x04; /* Joystick */ 373 else 374 lpddi->wUsage = 0x05; /* Game Pad */ 375 } 376 377 MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszInstanceName, MAX_PATH); 378 MultiByteToWideChar(CP_ACP, 0, joystick_devices[id].name, -1, lpddi->tszProductName, MAX_PATH); 379 lpddi->guidFFDriver = GUID_NULL; 380 } 381 382 static void fill_joystick_dideviceinstanceA(LPDIDEVICEINSTANCEA lpddi, DWORD version, int id) 383 { 384 DIDEVICEINSTANCEW lpddiW; 385 DWORD dwSize = lpddi->dwSize; 386 387 lpddiW.dwSize = sizeof(lpddiW); 388 fill_joystick_dideviceinstanceW(&lpddiW, version, id); 389 390 TRACE("%d %p\n", dwSize, lpddi); 391 memset(lpddi, 0, dwSize); 392 393 /* Convert W->A */ 394 lpddi->dwSize = dwSize; 395 lpddi->guidInstance = lpddiW.guidInstance; 396 lpddi->guidProduct = lpddiW.guidProduct; 397 lpddi->dwDevType = lpddiW.dwDevType; 398 strcpy(lpddi->tszInstanceName, joystick_devices[id].name); 399 strcpy(lpddi->tszProductName, joystick_devices[id].name); 400 lpddi->guidFFDriver = lpddiW.guidFFDriver; 401 lpddi->wUsagePage = lpddiW.wUsagePage; 402 lpddi->wUsage = lpddiW.wUsage; 403 } 404 405 static HRESULT joydev_enum_deviceA(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEA lpddi, DWORD version, int id) 406 { 407 int fd = -1; 408 409 if (id >= find_joystick_devices()) return E_FAIL; 410 411 if (dwFlags & DIEDFL_FORCEFEEDBACK) { 412 WARN("force feedback not supported\n"); 413 return S_FALSE; 414 } 415 416 if ((dwDevType == 0) || 417 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) || 418 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) { 419 /* check whether we have a joystick */ 420 if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1) 421 { 422 WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno)); 423 return S_FALSE; 424 } 425 fill_joystick_dideviceinstanceA( lpddi, version, id ); 426 close(fd); 427 TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name); 428 return S_OK; 429 } 430 431 return S_FALSE; 432 } 433 434 static HRESULT joydev_enum_deviceW(DWORD dwDevType, DWORD dwFlags, LPDIDEVICEINSTANCEW lpddi, DWORD version, int id) 435 { 436 int fd = -1; 437 438 if (id >= find_joystick_devices()) return E_FAIL; 439 440 if (dwFlags & DIEDFL_FORCEFEEDBACK) { 441 WARN("force feedback not supported\n"); 442 return S_FALSE; 443 } 444 445 if ((dwDevType == 0) || 446 ((dwDevType == DIDEVTYPE_JOYSTICK) && (version >= 0x0300 && version < 0x0800)) || 447 (((dwDevType == DI8DEVCLASS_GAMECTRL) || (dwDevType == DI8DEVTYPE_JOYSTICK)) && (version >= 0x0800))) { 448 /* check whether we have a joystick */ 449 if ((fd = open(joystick_devices[id].device, O_RDONLY)) == -1) 450 { 451 WARN("open(%s, O_RDONLY) failed: %s\n", joystick_devices[id].device, strerror(errno)); 452 return S_FALSE; 453 } 454 fill_joystick_dideviceinstanceW( lpddi, version, id ); 455 close(fd); 456 TRACE("Enumerating the linux Joystick device: %s (%s)\n", joystick_devices[id].device, joystick_devices[id].name); 457 return S_OK; 458 } 459 460 return S_FALSE; 461 } 462 463 static HRESULT alloc_device(REFGUID rguid, IDirectInputImpl *dinput, 464 JoystickImpl **pdev, unsigned short index) 465 { 466 DWORD i; 467 JoystickImpl* newDevice; 468 HRESULT hr; 469 LPDIDATAFORMAT df = NULL; 470 int idx = 0; 471 DIDEVICEINSTANCEW ddi; 472 473 TRACE("%s %p %p %hu\n", debugstr_guid(rguid), dinput, pdev, index); 474 475 newDevice = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(JoystickImpl)); 476 if (newDevice == 0) { 477 WARN("out of memory\n"); 478 *pdev = 0; 479 return DIERR_OUTOFMEMORY; 480 } 481 482 newDevice->joydev = &joystick_devices[index]; 483 newDevice->joyfd = -1; 484 newDevice->generic.guidInstance = DInput_Wine_Joystick_GUID; 485 newDevice->generic.guidInstance.Data3 = index; 486 newDevice->generic.guidProduct = DInput_Wine_Joystick_GUID; 487 newDevice->generic.joy_polldev = joy_polldev; 488 newDevice->generic.name = newDevice->joydev->name; 489 newDevice->generic.device_axis_count = newDevice->joydev->axis_count; 490 newDevice->generic.devcaps.dwButtons = newDevice->joydev->button_count; 491 492 if (newDevice->generic.devcaps.dwButtons > 128) 493 { 494 WARN("Can't support %d buttons. Clamping down to 128\n", newDevice->generic.devcaps.dwButtons); 495 newDevice->generic.devcaps.dwButtons = 128; 496 } 497 498 newDevice->generic.base.IDirectInputDevice8A_iface.lpVtbl = &JoystickAvt; 499 newDevice->generic.base.IDirectInputDevice8W_iface.lpVtbl = &JoystickWvt; 500 newDevice->generic.base.ref = 1; 501 newDevice->generic.base.dinput = dinput; 502 newDevice->generic.base.guid = *rguid; 503 InitializeCriticalSection(&newDevice->generic.base.crit); 504 newDevice->generic.base.crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit"); 505 506 /* setup_dinput_options may change these */ 507 newDevice->generic.deadzone = 0; 508 509 /* do any user specified configuration */ 510 hr = setup_dinput_options(&newDevice->generic, newDevice->joydev->dev_axes_map); 511 if (hr != DI_OK) 512 goto FAILED1; 513 514 /* Create copy of default data format */ 515 if (!(df = HeapAlloc(GetProcessHeap(), 0, c_dfDIJoystick2.dwSize))) goto FAILED; 516 memcpy(df, &c_dfDIJoystick2, c_dfDIJoystick2.dwSize); 517 518 df->dwNumObjs = newDevice->generic.devcaps.dwAxes + newDevice->generic.devcaps.dwPOVs + newDevice->generic.devcaps.dwButtons; 519 if (!(df->rgodf = HeapAlloc(GetProcessHeap(), 0, df->dwNumObjs * df->dwObjSize))) goto FAILED; 520 521 for (i = 0; i < newDevice->generic.device_axis_count; i++) 522 { 523 int wine_obj = newDevice->generic.axis_map[i]; 524 525 if (wine_obj < 0) continue; 526 527 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[wine_obj], df->dwObjSize); 528 if (wine_obj < 8) 529 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj) | DIDFT_ABSAXIS; 530 else 531 { 532 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(wine_obj - 8) | DIDFT_POV; 533 i++; /* POV takes 2 axes */ 534 } 535 } 536 for (i = 0; i < newDevice->generic.devcaps.dwButtons; i++) 537 { 538 memcpy(&df->rgodf[idx], &c_dfDIJoystick2.rgodf[i + 12], df->dwObjSize); 539 df->rgodf[idx ].pguid = &GUID_Button; 540 df->rgodf[idx++].dwType = DIDFT_MAKEINSTANCE(i) | DIDFT_PSHBUTTON; 541 } 542 newDevice->generic.base.data_format.wine_df = df; 543 544 /* initialize default properties */ 545 for (i = 0; i < c_dfDIJoystick2.dwNumObjs; i++) { 546 newDevice->generic.props[i].lDevMin = -32767; 547 newDevice->generic.props[i].lDevMax = +32767; 548 newDevice->generic.props[i].lMin = 0; 549 newDevice->generic.props[i].lMax = 0xffff; 550 newDevice->generic.props[i].lDeadZone = newDevice->generic.deadzone; /* % * 1000 */ 551 newDevice->generic.props[i].lSaturation = 0; 552 } 553 554 IDirectInput_AddRef(&newDevice->generic.base.dinput->IDirectInput7A_iface); 555 556 EnterCriticalSection(&dinput->crit); 557 list_add_tail(&dinput->devices_list, &newDevice->generic.base.entry); 558 LeaveCriticalSection(&dinput->crit); 559 560 newDevice->generic.devcaps.dwSize = sizeof(newDevice->generic.devcaps); 561 newDevice->generic.devcaps.dwFlags = DIDC_ATTACHED; 562 563 ddi.dwSize = sizeof(ddi); 564 fill_joystick_dideviceinstanceW(&ddi, newDevice->generic.base.dinput->dwVersion, index); 565 newDevice->generic.devcaps.dwDevType = ddi.dwDevType; 566 567 newDevice->generic.devcaps.dwFFSamplePeriod = 0; 568 newDevice->generic.devcaps.dwFFMinTimeResolution = 0; 569 newDevice->generic.devcaps.dwFirmwareRevision = 0; 570 newDevice->generic.devcaps.dwHardwareRevision = 0; 571 newDevice->generic.devcaps.dwFFDriverVersion = 0; 572 573 if (TRACE_ON(dinput)) { 574 _dump_DIDATAFORMAT(newDevice->generic.base.data_format.wine_df); 575 for (i = 0; i < (newDevice->generic.device_axis_count); i++) 576 TRACE("axis_map[%d] = %d\n", i, newDevice->generic.axis_map[i]); 577 _dump_DIDEVCAPS(&newDevice->generic.devcaps); 578 } 579 580 *pdev = newDevice; 581 582 return DI_OK; 583 584 FAILED: 585 hr = DIERR_OUTOFMEMORY; 586 FAILED1: 587 if (df) HeapFree(GetProcessHeap(), 0, df->rgodf); 588 HeapFree(GetProcessHeap(), 0, df); 589 release_DataFormat(&newDevice->generic.base.data_format); 590 HeapFree(GetProcessHeap(),0,newDevice->generic.axis_map); 591 HeapFree(GetProcessHeap(),0,newDevice); 592 *pdev = 0; 593 594 return hr; 595 } 596 597 /****************************************************************************** 598 * get_joystick_index : Get the joystick index from a given GUID 599 */ 600 static unsigned short get_joystick_index(REFGUID guid) 601 { 602 GUID wine_joystick = DInput_Wine_Joystick_GUID; 603 GUID dev_guid = *guid; 604 605 wine_joystick.Data3 = 0; 606 dev_guid.Data3 = 0; 607 608 /* for the standard joystick GUID use index 0 */ 609 if(IsEqualGUID(&GUID_Joystick,guid)) return 0; 610 611 /* for the wine joystick GUIDs use the index stored in Data3 */ 612 if(IsEqualGUID(&wine_joystick, &dev_guid)) return guid->Data3; 613 614 return MAX_JOYSTICKS; 615 } 616 617 static HRESULT joydev_create_device(IDirectInputImpl *dinput, REFGUID rguid, REFIID riid, LPVOID *pdev, int unicode) 618 { 619 unsigned short index; 620 621 TRACE("%p %s %s %p %i\n", dinput, debugstr_guid(rguid), debugstr_guid(riid), pdev, unicode); 622 find_joystick_devices(); 623 *pdev = NULL; 624 625 if ((index = get_joystick_index(rguid)) < MAX_JOYSTICKS && 626 joystick_devices_count && index < joystick_devices_count) 627 { 628 JoystickImpl *This; 629 HRESULT hr; 630 631 if (riid == NULL) 632 ;/* nothing */ 633 else if (IsEqualGUID(&IID_IDirectInputDeviceA, riid) || 634 IsEqualGUID(&IID_IDirectInputDevice2A, riid) || 635 IsEqualGUID(&IID_IDirectInputDevice7A, riid) || 636 IsEqualGUID(&IID_IDirectInputDevice8A, riid)) 637 { 638 unicode = 0; 639 } 640 else if (IsEqualGUID(&IID_IDirectInputDeviceW, riid) || 641 IsEqualGUID(&IID_IDirectInputDevice2W, riid) || 642 IsEqualGUID(&IID_IDirectInputDevice7W, riid) || 643 IsEqualGUID(&IID_IDirectInputDevice8W, riid)) 644 { 645 unicode = 1; 646 } 647 else 648 { 649 WARN("no interface\n"); 650 return DIERR_NOINTERFACE; 651 } 652 653 hr = alloc_device(rguid, dinput, &This, index); 654 if (!This) return hr; 655 656 if (unicode) 657 *pdev = &This->generic.base.IDirectInputDevice8W_iface; 658 else 659 *pdev = &This->generic.base.IDirectInputDevice8A_iface; 660 661 return hr; 662 } 663 664 return DIERR_DEVICENOTREG; 665 } 666 667 #undef MAX_JOYSTICKS 668 669 const struct dinput_device joystick_linux_device = { 670 "Wine Linux joystick driver", 671 joydev_enum_deviceA, 672 joydev_enum_deviceW, 673 joydev_create_device 674 }; 675 676 /****************************************************************************** 677 * Acquire : gets exclusive control of the joystick 678 */ 679 static HRESULT WINAPI JoystickLinuxWImpl_Acquire(LPDIRECTINPUTDEVICE8W iface) 680 { 681 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); 682 HRESULT res; 683 684 TRACE("(%p)\n",This); 685 686 res = IDirectInputDevice2WImpl_Acquire(iface); 687 if (res != DI_OK) 688 return res; 689 690 /* open the joystick device */ 691 if (This->joyfd==-1) { 692 TRACE("opening joystick device %s\n", This->joydev->device); 693 694 This->joyfd = open(This->joydev->device, O_RDONLY); 695 if (This->joyfd==-1) { 696 ERR("open(%s) failed: %s\n", This->joydev->device, strerror(errno)); 697 IDirectInputDevice2WImpl_Unacquire(iface); 698 return DIERR_NOTFOUND; 699 } 700 } 701 702 return DI_OK; 703 } 704 705 static HRESULT WINAPI JoystickLinuxAImpl_Acquire(LPDIRECTINPUTDEVICE8A iface) 706 { 707 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); 708 return JoystickLinuxWImpl_Acquire(IDirectInputDevice8W_from_impl(This)); 709 } 710 711 /****************************************************************************** 712 * GetProperty : get input device properties 713 */ 714 static HRESULT WINAPI JoystickLinuxWImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph) 715 { 716 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); 717 718 TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(rguid), pdiph); 719 _dump_DIPROPHEADER(pdiph); 720 721 if (!IS_DIPROP(rguid)) return DI_OK; 722 723 switch (LOWORD(rguid)) { 724 725 case (DWORD_PTR) DIPROP_VIDPID: 726 { 727 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; 728 729 if (!This->joydev->product_id || !This->joydev->vendor_id) 730 return DIERR_UNSUPPORTED; 731 pd->dwData = MAKELONG(This->joydev->vendor_id, This->joydev->product_id); 732 TRACE("DIPROP_VIDPID(%08x)\n", pd->dwData); 733 break; 734 } 735 case (DWORD_PTR) DIPROP_JOYSTICKID: 736 { 737 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; 738 739 pd->dwData = get_joystick_index(&This->generic.base.guid); 740 TRACE("DIPROP_JOYSTICKID(%d)\n", pd->dwData); 741 break; 742 } 743 744 case (DWORD_PTR) DIPROP_GUIDANDPATH: 745 { 746 static const WCHAR formatW[] = {'\\','\\','?','\\','h','i','d','#','v','i','d','_','%','0','4','x','&', 747 'p','i','d','_','%','0','4','x','&','%','s','_','%','h','u',0}; 748 static const WCHAR miW[] = {'m','i',0}; 749 static const WCHAR igW[] = {'i','g',0}; 750 751 BOOL is_gamepad; 752 LPDIPROPGUIDANDPATH pd = (LPDIPROPGUIDANDPATH)pdiph; 753 WORD vid = This->joydev->vendor_id; 754 WORD pid = This->joydev->product_id; 755 756 if (!pid || !vid) 757 return DIERR_UNSUPPORTED; 758 759 is_gamepad = is_xinput_device(&This->generic.devcaps, vid, pid); 760 pd->guidClass = GUID_DEVCLASS_HIDCLASS; 761 sprintfW(pd->wszPath, formatW, vid, pid, is_gamepad ? igW : miW, get_joystick_index(&This->generic.base.guid)); 762 763 TRACE("DIPROP_GUIDANDPATH(%s, %s): returning fake path\n", debugstr_guid(&pd->guidClass), debugstr_w(pd->wszPath)); 764 break; 765 } 766 767 default: 768 return JoystickWGenericImpl_GetProperty(iface, rguid, pdiph); 769 } 770 771 return DI_OK; 772 } 773 774 static HRESULT WINAPI JoystickLinuxAImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph) 775 { 776 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); 777 return JoystickLinuxWImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph); 778 } 779 780 /****************************************************************************** 781 * GetDeviceInfo : get information about a device's identity 782 */ 783 static HRESULT WINAPI JoystickLinuxAImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8A iface, LPDIDEVICEINSTANCEA ddi) 784 { 785 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); 786 787 TRACE("(%p) %p\n", This, ddi); 788 789 if (ddi == NULL) return E_POINTER; 790 if ((ddi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) && 791 (ddi->dwSize != sizeof(DIDEVICEINSTANCEA))) 792 return DIERR_INVALIDPARAM; 793 794 fill_joystick_dideviceinstanceA( ddi, This->generic.base.dinput->dwVersion, 795 get_joystick_index(&This->generic.base.guid) ); 796 return DI_OK; 797 } 798 799 static HRESULT WINAPI JoystickLinuxWImpl_GetDeviceInfo(LPDIRECTINPUTDEVICE8W iface, LPDIDEVICEINSTANCEW ddi) 800 { 801 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); 802 803 TRACE("(%p) %p\n", This, ddi); 804 805 if (ddi == NULL) return E_POINTER; 806 if ((ddi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) && 807 (ddi->dwSize != sizeof(DIDEVICEINSTANCEW))) 808 return DIERR_INVALIDPARAM; 809 810 fill_joystick_dideviceinstanceW( ddi, This->generic.base.dinput->dwVersion, 811 get_joystick_index(&This->generic.base.guid) ); 812 return DI_OK; 813 } 814 815 /****************************************************************************** 816 * Unacquire : frees the joystick 817 */ 818 static HRESULT WINAPI JoystickLinuxWImpl_Unacquire(LPDIRECTINPUTDEVICE8W iface) 819 { 820 JoystickImpl *This = impl_from_IDirectInputDevice8W(iface); 821 HRESULT res; 822 823 TRACE("(%p)\n",This); 824 825 res = IDirectInputDevice2WImpl_Unacquire(iface); 826 827 if (res != DI_OK) 828 return res; 829 830 if (This->joyfd!=-1) { 831 TRACE("closing joystick device\n"); 832 close(This->joyfd); 833 This->joyfd = -1; 834 return DI_OK; 835 } 836 837 return DI_NOEFFECT; 838 } 839 840 static HRESULT WINAPI JoystickLinuxAImpl_Unacquire(LPDIRECTINPUTDEVICE8A iface) 841 { 842 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); 843 return JoystickLinuxWImpl_Unacquire(IDirectInputDevice8W_from_impl(This)); 844 } 845 846 static void joy_polldev(LPDIRECTINPUTDEVICE8A iface) 847 { 848 struct pollfd plfd; 849 struct js_event jse; 850 JoystickImpl *This = impl_from_IDirectInputDevice8A(iface); 851 852 TRACE("(%p)\n", This); 853 854 if (This->joyfd==-1) { 855 WARN("no device\n"); 856 return; 857 } 858 while (1) 859 { 860 LONG value; 861 int inst_id = -1; 862 863 plfd.fd = This->joyfd; 864 plfd.events = POLLIN; 865 if (poll(&plfd,1,0) != 1) 866 return; 867 /* we have one event, so we can read */ 868 if (sizeof(jse)!=read(This->joyfd,&jse,sizeof(jse))) { 869 return; 870 } 871 TRACE("js_event: type 0x%x, number %d, value %d\n", 872 jse.type,jse.number,jse.value); 873 if (jse.type & JS_EVENT_BUTTON) 874 { 875 int button; 876 if (jse.number >= This->generic.devcaps.dwButtons) return; 877 878 button = This->generic.button_map[jse.number]; 879 880 inst_id = DIDFT_MAKEINSTANCE(button) | DIDFT_PSHBUTTON; 881 This->generic.js.rgbButtons[button] = value = jse.value ? 0x80 : 0x00; 882 } 883 else if (jse.type & JS_EVENT_AXIS) 884 { 885 int number = This->generic.axis_map[jse.number]; /* wine format object index */ 886 887 if (number < 0) return; 888 inst_id = number < 8 ? DIDFT_MAKEINSTANCE(number) | DIDFT_ABSAXIS : 889 DIDFT_MAKEINSTANCE(number - 8) | DIDFT_POV; 890 value = joystick_map_axis(&This->generic.props[id_to_object(This->generic.base.data_format.wine_df, inst_id)], jse.value); 891 892 TRACE("changing axis %d => %d\n", jse.number, number); 893 switch (number) 894 { 895 case 0: This->generic.js.lX = value; break; 896 case 1: This->generic.js.lY = value; break; 897 case 2: This->generic.js.lZ = value; break; 898 case 3: This->generic.js.lRx = value; break; 899 case 4: This->generic.js.lRy = value; break; 900 case 5: This->generic.js.lRz = value; break; 901 case 6: This->generic.js.rglSlider[0] = value; break; 902 case 7: This->generic.js.rglSlider[1] = value; break; 903 case 8: case 9: case 10: case 11: 904 { 905 int idx = number - 8; 906 907 if (jse.number % 2) 908 This->povs[idx].y = jse.value; 909 else 910 This->povs[idx].x = jse.value; 911 912 This->generic.js.rgdwPOV[idx] = value = joystick_map_pov(&This->povs[idx]); 913 break; 914 } 915 default: 916 WARN("axis %d not supported\n", number); 917 } 918 } 919 if (inst_id >= 0) 920 queue_event(iface, inst_id, value, GetCurrentTime(), This->generic.base.dinput->evsequence++); 921 } 922 } 923 924 static const IDirectInputDevice8AVtbl JoystickAvt = 925 { 926 IDirectInputDevice2AImpl_QueryInterface, 927 IDirectInputDevice2AImpl_AddRef, 928 IDirectInputDevice2AImpl_Release, 929 JoystickAGenericImpl_GetCapabilities, 930 IDirectInputDevice2AImpl_EnumObjects, 931 JoystickLinuxAImpl_GetProperty, 932 JoystickAGenericImpl_SetProperty, 933 JoystickLinuxAImpl_Acquire, 934 JoystickLinuxAImpl_Unacquire, 935 JoystickAGenericImpl_GetDeviceState, 936 IDirectInputDevice2AImpl_GetDeviceData, 937 IDirectInputDevice2AImpl_SetDataFormat, 938 IDirectInputDevice2AImpl_SetEventNotification, 939 IDirectInputDevice2AImpl_SetCooperativeLevel, 940 JoystickAGenericImpl_GetObjectInfo, 941 JoystickLinuxAImpl_GetDeviceInfo, 942 IDirectInputDevice2AImpl_RunControlPanel, 943 IDirectInputDevice2AImpl_Initialize, 944 IDirectInputDevice2AImpl_CreateEffect, 945 IDirectInputDevice2AImpl_EnumEffects, 946 IDirectInputDevice2AImpl_GetEffectInfo, 947 IDirectInputDevice2AImpl_GetForceFeedbackState, 948 IDirectInputDevice2AImpl_SendForceFeedbackCommand, 949 IDirectInputDevice2AImpl_EnumCreatedEffectObjects, 950 IDirectInputDevice2AImpl_Escape, 951 JoystickAGenericImpl_Poll, 952 IDirectInputDevice2AImpl_SendDeviceData, 953 IDirectInputDevice7AImpl_EnumEffectsInFile, 954 IDirectInputDevice7AImpl_WriteEffectToFile, 955 JoystickAGenericImpl_BuildActionMap, 956 JoystickAGenericImpl_SetActionMap, 957 IDirectInputDevice8AImpl_GetImageInfo 958 }; 959 960 static const IDirectInputDevice8WVtbl JoystickWvt = 961 { 962 IDirectInputDevice2WImpl_QueryInterface, 963 IDirectInputDevice2WImpl_AddRef, 964 IDirectInputDevice2WImpl_Release, 965 JoystickWGenericImpl_GetCapabilities, 966 IDirectInputDevice2WImpl_EnumObjects, 967 JoystickLinuxWImpl_GetProperty, 968 JoystickWGenericImpl_SetProperty, 969 JoystickLinuxWImpl_Acquire, 970 JoystickLinuxWImpl_Unacquire, 971 JoystickWGenericImpl_GetDeviceState, 972 IDirectInputDevice2WImpl_GetDeviceData, 973 IDirectInputDevice2WImpl_SetDataFormat, 974 IDirectInputDevice2WImpl_SetEventNotification, 975 IDirectInputDevice2WImpl_SetCooperativeLevel, 976 JoystickWGenericImpl_GetObjectInfo, 977 JoystickLinuxWImpl_GetDeviceInfo, 978 IDirectInputDevice2WImpl_RunControlPanel, 979 IDirectInputDevice2WImpl_Initialize, 980 IDirectInputDevice2WImpl_CreateEffect, 981 IDirectInputDevice2WImpl_EnumEffects, 982 IDirectInputDevice2WImpl_GetEffectInfo, 983 IDirectInputDevice2WImpl_GetForceFeedbackState, 984 IDirectInputDevice2WImpl_SendForceFeedbackCommand, 985 IDirectInputDevice2WImpl_EnumCreatedEffectObjects, 986 IDirectInputDevice2WImpl_Escape, 987 JoystickWGenericImpl_Poll, 988 IDirectInputDevice2WImpl_SendDeviceData, 989 IDirectInputDevice7WImpl_EnumEffectsInFile, 990 IDirectInputDevice7WImpl_WriteEffectToFile, 991 JoystickWGenericImpl_BuildActionMap, 992 JoystickWGenericImpl_SetActionMap, 993 IDirectInputDevice8WImpl_GetImageInfo 994 }; 995 996 #else /* HAVE_LINUX_22_JOYSTICK_API */ 997 998 const struct dinput_device joystick_linux_device = { 999 "Wine Linux joystick driver", 1000 NULL, 1001 NULL, 1002 NULL 1003 }; 1004 1005 #endif /* HAVE_LINUX_22_JOYSTICK_API */ 1006