1 /* DirectInput Generic Joystick device 2 * 3 * Copyright 1998 Marcus Meissner 4 * Copyright 1998,1999 Lionel Ulmer 5 * Copyright 2000-2001 TransGaming Technologies Inc. 6 * Copyright 2009 Aric Stewart, CodeWeavers 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 /* 24 * To Do: 25 * dead zone 26 * force feedback 27 */ 28 29 #include <stdio.h> 30 31 #include "device_private.h" 32 #include "joystick_private.h" 33 #include "wine/debug.h" 34 #include "wine/heap.h" 35 #include "winreg.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(dinput); 38 39 #define VID_MICROSOFT 0x045e 40 41 static const WORD PID_XBOX_CONTROLLERS[] = { 42 0x0202, /* Xbox Controller */ 43 0x0285, /* Xbox Controller S */ 44 0x0289, /* Xbox Controller S */ 45 0x028e, /* Xbox360 Controller */ 46 0x028f, /* Xbox360 Wireless Controller */ 47 0x02d1, /* Xbox One Controller */ 48 0x02dd, /* Xbox One Controller (Covert Forces/Firmware 2015) */ 49 0x02e0, /* Xbox One X Controller */ 50 0x02e3, /* Xbox One Elite Controller */ 51 0x02e6, /* Wireless XBox Controller Dongle */ 52 0x02ea, /* Xbox One S Controller */ 53 0x02fd, /* Xbox One S Controller (Firmware 2017) */ 54 0x0719, /* Xbox 360 Wireless Adapter */ 55 }; 56 57 /* Windows uses this GUID for guidProduct on non-keyboard/mouse devices. 58 * Data1 contains the device VID (low word) and PID (high word). 59 * Data4 ends with the ASCII bytes "PIDVID". 60 */ 61 const GUID DInput_PIDVID_Product_GUID = { /* device_pidvid-0000-0000-0000-504944564944 */ 62 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x50, 0x49, 0x44, 0x56, 0x49, 0x44} 63 }; 64 65 static inline JoystickGenericImpl *impl_from_IDirectInputDevice8A(IDirectInputDevice8A *iface) 66 { 67 return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8A_iface), JoystickGenericImpl, base); 68 } 69 static inline JoystickGenericImpl *impl_from_IDirectInputDevice8W(IDirectInputDevice8W *iface) 70 { 71 return CONTAINING_RECORD(CONTAINING_RECORD(iface, IDirectInputDeviceImpl, IDirectInputDevice8W_iface), JoystickGenericImpl, base); 72 } 73 static inline IDirectInputDevice8A *IDirectInputDevice8A_from_impl(JoystickGenericImpl *This) 74 { 75 return &This->base.IDirectInputDevice8A_iface; 76 } 77 static inline IDirectInputDevice8W *IDirectInputDevice8W_from_impl(JoystickGenericImpl *This) 78 { 79 return &This->base.IDirectInputDevice8W_iface; 80 } 81 82 DWORD typeFromGUID(REFGUID guid) 83 { 84 if (IsEqualGUID(guid, &GUID_ConstantForce)) { 85 return DIEFT_CONSTANTFORCE; 86 } else if (IsEqualGUID(guid, &GUID_Square) 87 || IsEqualGUID(guid, &GUID_Sine) 88 || IsEqualGUID(guid, &GUID_Triangle) 89 || IsEqualGUID(guid, &GUID_SawtoothUp) 90 || IsEqualGUID(guid, &GUID_SawtoothDown)) { 91 return DIEFT_PERIODIC; 92 } else if (IsEqualGUID(guid, &GUID_RampForce)) { 93 return DIEFT_RAMPFORCE; 94 } else if (IsEqualGUID(guid, &GUID_Spring) 95 || IsEqualGUID(guid, &GUID_Damper) 96 || IsEqualGUID(guid, &GUID_Inertia) 97 || IsEqualGUID(guid, &GUID_Friction)) { 98 return DIEFT_CONDITION; 99 } else if (IsEqualGUID(guid, &GUID_CustomForce)) { 100 return DIEFT_CUSTOMFORCE; 101 } else { 102 WARN("GUID (%s) is not a known force type\n", _dump_dinput_GUID(guid)); 103 return 0; 104 } 105 } 106 107 DWORD get_device_type(DWORD version, BOOL is_joystick) 108 { 109 if (is_joystick) 110 return version >= 0x0800 ? DI8DEVTYPE_JOYSTICK | (DI8DEVTYPEJOYSTICK_STANDARD << 8) : 111 DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_TRADITIONAL << 8); 112 113 return version >= 0x0800 ? DI8DEVTYPE_GAMEPAD | (DI8DEVTYPEJOYSTICK_STANDARD << 8) : 114 DIDEVTYPE_JOYSTICK | (DIDEVTYPEJOYSTICK_GAMEPAD << 8); 115 } 116 117 static void _dump_DIEFFECT_flags(DWORD dwFlags) 118 { 119 if (TRACE_ON(dinput)) { 120 unsigned int i; 121 static const struct { 122 DWORD mask; 123 const char *name; 124 } flags[] = { 125 #define FE(x) { x, #x} 126 FE(DIEFF_CARTESIAN), 127 FE(DIEFF_OBJECTIDS), 128 FE(DIEFF_OBJECTOFFSETS), 129 FE(DIEFF_POLAR), 130 FE(DIEFF_SPHERICAL) 131 #undef FE 132 }; 133 for (i = 0; i < ARRAY_SIZE(flags); i++) 134 if (flags[i].mask & dwFlags) 135 TRACE("%s ", flags[i].name); 136 TRACE("\n"); 137 } 138 } 139 140 static void _dump_DIENVELOPE(LPCDIENVELOPE env) 141 { 142 if (env->dwSize != sizeof(DIENVELOPE)) { 143 WARN("Non-standard DIENVELOPE structure size %d.\n", env->dwSize); 144 } 145 TRACE("Envelope has attack (level: %d time: %d), fade (level: %d time: %d)\n", 146 env->dwAttackLevel, env->dwAttackTime, env->dwFadeLevel, env->dwFadeTime); 147 } 148 149 static void _dump_DICONSTANTFORCE(LPCDICONSTANTFORCE frc) 150 { 151 TRACE("Constant force has magnitude %d\n", frc->lMagnitude); 152 } 153 154 static void _dump_DIPERIODIC(LPCDIPERIODIC frc) 155 { 156 TRACE("Periodic force has magnitude %d, offset %d, phase %d, period %d\n", 157 frc->dwMagnitude, frc->lOffset, frc->dwPhase, frc->dwPeriod); 158 } 159 160 static void _dump_DIRAMPFORCE(LPCDIRAMPFORCE frc) 161 { 162 TRACE("Ramp force has start %d, end %d\n", 163 frc->lStart, frc->lEnd); 164 } 165 166 static void _dump_DICONDITION(LPCDICONDITION frc) 167 { 168 TRACE("Condition has offset %d, pos/neg coefficients %d and %d, pos/neg saturations %d and %d, deadband %d\n", 169 frc->lOffset, frc->lPositiveCoefficient, frc->lNegativeCoefficient, 170 frc->dwPositiveSaturation, frc->dwNegativeSaturation, frc->lDeadBand); 171 } 172 173 static void _dump_DICUSTOMFORCE(LPCDICUSTOMFORCE frc) 174 { 175 unsigned int i; 176 TRACE("Custom force uses %d channels, sample period %d. Has %d samples at %p.\n", 177 frc->cChannels, frc->dwSamplePeriod, frc->cSamples, frc->rglForceData); 178 if (frc->cSamples % frc->cChannels != 0) 179 WARN("Custom force has a non-integral samples-per-channel count!\n"); 180 if (TRACE_ON(dinput)) { 181 TRACE("Custom force data (time aligned, axes in order):\n"); 182 for (i = 1; i <= frc->cSamples; ++i) { 183 TRACE("%d ", frc->rglForceData[i]); 184 if (i % frc->cChannels == 0) 185 TRACE("\n"); 186 } 187 } 188 } 189 190 void dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid, DWORD dwFlags) 191 { 192 DWORD type = typeFromGUID(guid); 193 unsigned int i; 194 195 TRACE("Dumping DIEFFECT structure:\n"); 196 TRACE(" - dwSize: %d\n", eff->dwSize); 197 if ((eff->dwSize != sizeof(DIEFFECT)) && (eff->dwSize != sizeof(DIEFFECT_DX5))) { 198 WARN("Non-standard DIEFFECT structure size %d\n", eff->dwSize); 199 } 200 TRACE(" - dwFlags: %d\n", eff->dwFlags); 201 TRACE(" "); 202 _dump_DIEFFECT_flags(eff->dwFlags); 203 TRACE(" - dwDuration: %d\n", eff->dwDuration); 204 TRACE(" - dwGain: %d\n", eff->dwGain); 205 206 if (eff->dwGain > 10000) 207 WARN("dwGain is out of range (>10,000)\n"); 208 209 TRACE(" - dwTriggerButton: %d\n", eff->dwTriggerButton); 210 TRACE(" - dwTriggerRepeatInterval: %d\n", eff->dwTriggerRepeatInterval); 211 TRACE(" - rglDirection: %p\n", eff->rglDirection); 212 TRACE(" - cbTypeSpecificParams: %d\n", eff->cbTypeSpecificParams); 213 TRACE(" - lpvTypeSpecificParams: %p\n", eff->lpvTypeSpecificParams); 214 215 /* Only trace some members if dwFlags indicates they have data */ 216 if (dwFlags & DIEP_AXES) { 217 TRACE(" - cAxes: %d\n", eff->cAxes); 218 TRACE(" - rgdwAxes: %p\n", eff->rgdwAxes); 219 220 if (TRACE_ON(dinput) && eff->rgdwAxes) { 221 TRACE(" "); 222 for (i = 0; i < eff->cAxes; ++i) 223 TRACE("%d ", eff->rgdwAxes[i]); 224 TRACE("\n"); 225 } 226 } 227 228 if (dwFlags & DIEP_ENVELOPE) { 229 TRACE(" - lpEnvelope: %p\n", eff->lpEnvelope); 230 if (eff->lpEnvelope != NULL) 231 _dump_DIENVELOPE(eff->lpEnvelope); 232 } 233 234 if (eff->dwSize > sizeof(DIEFFECT_DX5)) 235 TRACE(" - dwStartDelay: %d\n", eff->dwStartDelay); 236 237 if (type == DIEFT_CONSTANTFORCE) { 238 if (eff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) { 239 WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n"); 240 } else { 241 _dump_DICONSTANTFORCE(eff->lpvTypeSpecificParams); 242 } 243 } else if (type == DIEFT_PERIODIC) { 244 if (eff->cbTypeSpecificParams != sizeof(DIPERIODIC)) { 245 WARN("Effect claims to be a periodic force but the type-specific params are the wrong size!\n"); 246 } else { 247 _dump_DIPERIODIC(eff->lpvTypeSpecificParams); 248 } 249 } else if (type == DIEFT_RAMPFORCE) { 250 if (eff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) { 251 WARN("Effect claims to be a ramp force but the type-specific params are the wrong size!\n"); 252 } else { 253 _dump_DIRAMPFORCE(eff->lpvTypeSpecificParams); 254 } 255 } else if (type == DIEFT_CONDITION) { 256 if (eff->cbTypeSpecificParams == sizeof(DICONDITION)) { 257 _dump_DICONDITION(eff->lpvTypeSpecificParams); 258 } else if (eff->cbTypeSpecificParams == 2 * sizeof(DICONDITION)) { 259 DICONDITION *condition = eff->lpvTypeSpecificParams; 260 _dump_DICONDITION(&condition[0]); 261 _dump_DICONDITION(&condition[1]); 262 } else { 263 WARN("Effect claims to be a condition but the type-specific params are the wrong size!\n"); 264 } 265 } else if (type == DIEFT_CUSTOMFORCE) { 266 if (eff->cbTypeSpecificParams != sizeof(DICUSTOMFORCE)) { 267 WARN("Effect claims to be a custom force but the type-specific params are the wrong size!\n"); 268 } else { 269 _dump_DICUSTOMFORCE(eff->lpvTypeSpecificParams); 270 } 271 } 272 } 273 274 BOOL device_disabled_registry(const char* name) 275 { 276 static const char disabled_str[] = "disabled"; 277 static const char joystick_key[] = "Joysticks"; 278 char buffer[MAX_PATH]; 279 HKEY hkey, appkey, temp; 280 BOOL do_disable = FALSE; 281 282 get_app_key(&hkey, &appkey); 283 284 /* Joystick settings are in the 'joysticks' subkey */ 285 if (appkey) 286 { 287 if (RegOpenKeyA(appkey, joystick_key, &temp)) temp = 0; 288 RegCloseKey(appkey); 289 appkey = temp; 290 } 291 if (hkey) 292 { 293 if (RegOpenKeyA(hkey, joystick_key, &temp)) temp = 0; 294 RegCloseKey(hkey); 295 hkey = temp; 296 } 297 298 /* Look for the "controllername"="disabled" key */ 299 if (!get_config_key(hkey, appkey, name, buffer, sizeof(buffer))) 300 if (!strcmp(disabled_str, buffer)) 301 { 302 TRACE("Disabling joystick '%s' based on registry key.\n", name); 303 do_disable = TRUE; 304 } 305 306 if (appkey) RegCloseKey(appkey); 307 if (hkey) RegCloseKey(hkey); 308 309 return do_disable; 310 } 311 312 BOOL is_xinput_device(const DIDEVCAPS *devcaps, WORD vid, WORD pid) 313 { 314 int i; 315 316 if (vid == VID_MICROSOFT) 317 { 318 for (i = 0; i < ARRAY_SIZE(PID_XBOX_CONTROLLERS); i++) 319 if (pid == PID_XBOX_CONTROLLERS[i]) return TRUE; 320 } 321 322 return (devcaps->dwAxes == 6 && devcaps->dwButtons >= 14); 323 } 324 325 static void remap_init(JoystickGenericImpl *This, int obj, ObjProps *remap_props) 326 { 327 /* Configure as if nothing changed so the helper functions can only change 328 * what they need, thus reducing code duplication. */ 329 remap_props->lDevMin = remap_props->lMin = This->props[obj].lMin; 330 remap_props->lDevMax = remap_props->lMax = This->props[obj].lMax; 331 332 remap_props->lDeadZone = This->props[obj].lDeadZone; 333 remap_props->lSaturation = This->props[obj].lSaturation; 334 } 335 336 static void remap_apply(JoystickGenericImpl *This, int obj, ObjProps *remap_props) 337 { 338 /* Many games poll the joystick immediately after setting the range 339 * for calibration purposes, so the old values need to be remapped 340 * to the new range before it does so */ 341 switch (This->base.data_format.wine_df->rgodf[obj].dwOfs){ 342 case DIJOFS_X : This->js.lX = joystick_map_axis(remap_props, This->js.lX); break; 343 case DIJOFS_Y : This->js.lY = joystick_map_axis(remap_props, This->js.lY); break; 344 case DIJOFS_Z : This->js.lZ = joystick_map_axis(remap_props, This->js.lZ); break; 345 case DIJOFS_RX : This->js.lRx = joystick_map_axis(remap_props, This->js.lRx); break; 346 case DIJOFS_RY : This->js.lRy = joystick_map_axis(remap_props, This->js.lRy); break; 347 case DIJOFS_RZ : This->js.lRz = joystick_map_axis(remap_props, This->js.lRz); break; 348 case DIJOFS_SLIDER(0): This->js.rglSlider[0] = joystick_map_axis(remap_props, This->js.rglSlider[0]); break; 349 case DIJOFS_SLIDER(1): This->js.rglSlider[1] = joystick_map_axis(remap_props, This->js.rglSlider[1]); break; 350 default: break; 351 } 352 } 353 354 static void remap_range(JoystickGenericImpl *This, int obj, LPCDIPROPRANGE pr) 355 { 356 ObjProps remap_props; 357 remap_init(This, obj, &remap_props); 358 359 remap_props.lMin = pr->lMin; 360 remap_props.lMax = pr->lMax; 361 362 remap_apply(This, obj, &remap_props); 363 364 /* Store new values */ 365 This->props[obj].lMin = pr->lMin; 366 This->props[obj].lMax = pr->lMax; 367 } 368 369 static void remap_deadzone(JoystickGenericImpl *This, int obj, LPCDIPROPDWORD pd) 370 { 371 ObjProps remap_props; 372 remap_init(This, obj, &remap_props); 373 374 remap_props.lDeadZone = pd->dwData; 375 376 remap_apply(This, obj, &remap_props); 377 378 /* Store new value */ 379 This->props[obj].lDeadZone = pd->dwData; 380 } 381 382 static void remap_saturation(JoystickGenericImpl *This, int obj, LPCDIPROPDWORD pd) 383 { 384 ObjProps remap_props; 385 remap_init(This, obj, &remap_props); 386 387 remap_props.lSaturation = pd->dwData; 388 389 remap_apply(This, obj, &remap_props); 390 391 /* Store new value */ 392 This->props[obj].lSaturation = pd->dwData; 393 } 394 395 /****************************************************************************** 396 * SetProperty : change input device properties 397 */ 398 HRESULT WINAPI JoystickWGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPCDIPROPHEADER ph) 399 { 400 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 401 DWORD i; 402 403 TRACE("(%p,%s,%p)\n",This,debugstr_guid(rguid),ph); 404 405 if (ph == NULL) { 406 WARN("invalid parameter: ph == NULL\n"); 407 return DIERR_INVALIDPARAM; 408 } 409 410 if (TRACE_ON(dinput)) 411 _dump_DIPROPHEADER(ph); 412 413 if (IS_DIPROP(rguid)) { 414 switch (LOWORD(rguid)) { 415 case (DWORD_PTR)DIPROP_RANGE: { 416 LPCDIPROPRANGE pr = (LPCDIPROPRANGE)ph; 417 if (ph->dwHow == DIPH_DEVICE) { 418 TRACE("proprange(%d,%d) all\n", pr->lMin, pr->lMax); 419 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) 420 remap_range(This, i, pr); 421 } else { 422 int obj = find_property(&This->base.data_format, ph); 423 424 TRACE("proprange(%d,%d) obj=%d\n", pr->lMin, pr->lMax, obj); 425 if (obj >= 0) 426 remap_range(This, obj, pr); 427 } 428 break; 429 } 430 case (DWORD_PTR)DIPROP_DEADZONE: { 431 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph; 432 if (ph->dwHow == DIPH_DEVICE) { 433 TRACE("deadzone(%d) all\n", pd->dwData); 434 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) 435 remap_deadzone(This, i, pd); 436 } else { 437 int obj = find_property(&This->base.data_format, ph); 438 439 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj); 440 if (obj >= 0) 441 remap_deadzone(This, obj, pd); 442 } 443 break; 444 } 445 case (DWORD_PTR)DIPROP_SATURATION: { 446 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph; 447 if (ph->dwHow == DIPH_DEVICE) { 448 TRACE("saturation(%d) all\n", pd->dwData); 449 for (i = 0; i < This->base.data_format.wine_df->dwNumObjs; i++) 450 remap_saturation(This, i, pd); 451 } else { 452 int obj = find_property(&This->base.data_format, ph); 453 454 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj); 455 if (obj >= 0) 456 remap_saturation(This, obj, pd); 457 } 458 break; 459 } 460 case (DWORD_PTR)DIPROP_CALIBRATIONMODE: { 461 LPCDIPROPDWORD pd = (LPCDIPROPDWORD)ph; 462 FIXME("DIPROP_CALIBRATIONMODE(%d)\n", pd->dwData); 463 break; 464 } 465 default: 466 return IDirectInputDevice2WImpl_SetProperty(iface, rguid, ph); 467 } 468 } 469 470 return DI_OK; 471 } 472 473 HRESULT WINAPI JoystickAGenericImpl_SetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPCDIPROPHEADER ph) 474 { 475 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 476 return JoystickWGenericImpl_SetProperty(IDirectInputDevice8W_from_impl(This), rguid, ph); 477 } 478 479 #define DEBUG_TYPE(x) case (x): str = #x; break 480 void _dump_DIDEVCAPS(const DIDEVCAPS *lpDIDevCaps) 481 { 482 int type = GET_DIDEVICE_TYPE(lpDIDevCaps->dwDevType); 483 const char *str, *hid = ""; 484 TRACE("dwSize: %d\n", lpDIDevCaps->dwSize); 485 TRACE("dwFlags: %08x\n", lpDIDevCaps->dwFlags); 486 switch(type) 487 { 488 /* Direct X <= 7 definitions */ 489 DEBUG_TYPE(DIDEVTYPE_DEVICE); 490 DEBUG_TYPE(DIDEVTYPE_MOUSE); 491 DEBUG_TYPE(DIDEVTYPE_KEYBOARD); 492 DEBUG_TYPE(DIDEVTYPE_JOYSTICK); 493 /* Direct X >= 8 definitions */ 494 DEBUG_TYPE(DI8DEVTYPE_DEVICE); 495 DEBUG_TYPE(DI8DEVTYPE_MOUSE); 496 DEBUG_TYPE(DI8DEVTYPE_KEYBOARD); 497 DEBUG_TYPE(DI8DEVTYPE_JOYSTICK); 498 DEBUG_TYPE(DI8DEVTYPE_GAMEPAD); 499 DEBUG_TYPE(DI8DEVTYPE_DRIVING); 500 DEBUG_TYPE(DI8DEVTYPE_FLIGHT); 501 DEBUG_TYPE(DI8DEVTYPE_1STPERSON); 502 DEBUG_TYPE(DI8DEVTYPE_DEVICECTRL); 503 DEBUG_TYPE(DI8DEVTYPE_SCREENPOINTER); 504 DEBUG_TYPE(DI8DEVTYPE_REMOTE); 505 DEBUG_TYPE(DI8DEVTYPE_SUPPLEMENTAL); 506 default: str = "UNKNOWN"; 507 } 508 509 if (lpDIDevCaps->dwDevType & DIDEVTYPE_HID) 510 hid = " (HID)"; 511 512 TRACE("dwDevType: %08x %s%s\n", lpDIDevCaps->dwDevType, str, hid); 513 TRACE("dwAxes: %d\n", lpDIDevCaps->dwAxes); 514 TRACE("dwButtons: %d\n", lpDIDevCaps->dwButtons); 515 TRACE("dwPOVs: %d\n", lpDIDevCaps->dwPOVs); 516 if (lpDIDevCaps->dwSize > sizeof(DIDEVCAPS_DX3)) { 517 TRACE("dwFFSamplePeriod: %d\n", lpDIDevCaps->dwFFSamplePeriod); 518 TRACE("dwFFMinTimeResolution: %d\n", lpDIDevCaps->dwFFMinTimeResolution); 519 TRACE("dwFirmwareRevision: %d\n", lpDIDevCaps->dwFirmwareRevision); 520 TRACE("dwHardwareRevision: %d\n", lpDIDevCaps->dwHardwareRevision); 521 TRACE("dwFFDriverVersion: %d\n", lpDIDevCaps->dwFFDriverVersion); 522 } 523 } 524 #undef DEBUG_TYPE 525 526 HRESULT WINAPI JoystickWGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8W iface, LPDIDEVCAPS lpDIDevCaps) 527 { 528 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 529 int size; 530 531 TRACE("%p->(%p)\n",This,lpDIDevCaps); 532 533 if (lpDIDevCaps == NULL) { 534 WARN("invalid pointer\n"); 535 return E_POINTER; 536 } 537 538 size = lpDIDevCaps->dwSize; 539 540 if (!(size == sizeof(DIDEVCAPS) || size == sizeof(DIDEVCAPS_DX3))) { 541 WARN("invalid parameter\n"); 542 return DIERR_INVALIDPARAM; 543 } 544 545 CopyMemory(lpDIDevCaps, &This->devcaps, size); 546 lpDIDevCaps->dwSize = size; 547 548 if (TRACE_ON(dinput)) 549 _dump_DIDEVCAPS(lpDIDevCaps); 550 551 return DI_OK; 552 } 553 554 HRESULT WINAPI JoystickAGenericImpl_GetCapabilities(LPDIRECTINPUTDEVICE8A iface, LPDIDEVCAPS lpDIDevCaps) 555 { 556 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 557 return JoystickWGenericImpl_GetCapabilities(IDirectInputDevice8W_from_impl(This), lpDIDevCaps); 558 } 559 560 /****************************************************************************** 561 * GetObjectInfo : get object info 562 */ 563 HRESULT WINAPI JoystickWGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8W iface, 564 LPDIDEVICEOBJECTINSTANCEW pdidoi, DWORD dwObj, DWORD dwHow) 565 { 566 static const WCHAR axisW[] = {'A','x','i','s',' ','%','d',0}; 567 static const WCHAR povW[] = {'P','O','V',' ','%','d',0}; 568 static const WCHAR buttonW[] = {'B','u','t','t','o','n',' ','%','d',0}; 569 HRESULT res; 570 571 res = IDirectInputDevice2WImpl_GetObjectInfo(iface, pdidoi, dwObj, dwHow); 572 if (res != DI_OK) return res; 573 574 if (pdidoi->dwType & DIDFT_AXIS) { 575 sprintfW(pdidoi->tszName, axisW, DIDFT_GETINSTANCE(pdidoi->dwType)); 576 pdidoi->dwFlags |= DIDOI_ASPECTPOSITION; 577 } else if (pdidoi->dwType & DIDFT_POV) 578 sprintfW(pdidoi->tszName, povW, DIDFT_GETINSTANCE(pdidoi->dwType)); 579 else if (pdidoi->dwType & DIDFT_BUTTON) 580 sprintfW(pdidoi->tszName, buttonW, DIDFT_GETINSTANCE(pdidoi->dwType)); 581 582 _dump_OBJECTINSTANCEW(pdidoi); 583 return res; 584 } 585 586 HRESULT WINAPI JoystickAGenericImpl_GetObjectInfo(LPDIRECTINPUTDEVICE8A iface, 587 LPDIDEVICEOBJECTINSTANCEA pdidoi, DWORD dwObj, DWORD dwHow) 588 { 589 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 590 HRESULT res; 591 DIDEVICEOBJECTINSTANCEW didoiW; 592 DWORD dwSize = pdidoi->dwSize; 593 594 didoiW.dwSize = sizeof(didoiW); 595 res = JoystickWGenericImpl_GetObjectInfo(IDirectInputDevice8W_from_impl(This), &didoiW, dwObj, dwHow); 596 if (res != DI_OK) return res; 597 598 memset(pdidoi, 0, pdidoi->dwSize); 599 memcpy(pdidoi, &didoiW, FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName)); 600 pdidoi->dwSize = dwSize; 601 WideCharToMultiByte(CP_ACP, 0, didoiW.tszName, -1, pdidoi->tszName, 602 sizeof(pdidoi->tszName), NULL, NULL); 603 604 return res; 605 } 606 607 /****************************************************************************** 608 * GetProperty : get input device properties 609 */ 610 HRESULT WINAPI JoystickWGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8W iface, REFGUID rguid, LPDIPROPHEADER pdiph) 611 { 612 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 613 614 TRACE("(%p,%s,%p)\n", This, debugstr_guid(rguid), pdiph); 615 616 if (TRACE_ON(dinput)) 617 _dump_DIPROPHEADER(pdiph); 618 619 if (IS_DIPROP(rguid)) { 620 switch (LOWORD(rguid)) { 621 case (DWORD_PTR) DIPROP_RANGE: { 622 LPDIPROPRANGE pr = (LPDIPROPRANGE)pdiph; 623 int obj = find_property(&This->base.data_format, pdiph); 624 625 /* The app is querying the current range of the axis 626 * return the lMin and lMax values */ 627 if (obj >= 0) { 628 pr->lMin = This->props[obj].lMin; 629 pr->lMax = This->props[obj].lMax; 630 TRACE("range(%d, %d) obj=%d\n", pr->lMin, pr->lMax, obj); 631 return DI_OK; 632 } 633 break; 634 } 635 case (DWORD_PTR) DIPROP_DEADZONE: { 636 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; 637 int obj = find_property(&This->base.data_format, pdiph); 638 639 if (obj >= 0) { 640 pd->dwData = This->props[obj].lDeadZone; 641 TRACE("deadzone(%d) obj=%d\n", pd->dwData, obj); 642 return DI_OK; 643 } 644 break; 645 } 646 case (DWORD_PTR) DIPROP_SATURATION: { 647 LPDIPROPDWORD pd = (LPDIPROPDWORD)pdiph; 648 int obj = find_property(&This->base.data_format, pdiph); 649 650 if (obj >= 0) { 651 pd->dwData = This->props[obj].lSaturation; 652 TRACE("saturation(%d) obj=%d\n", pd->dwData, obj); 653 return DI_OK; 654 } 655 break; 656 } 657 case (DWORD_PTR) DIPROP_PRODUCTNAME: 658 case (DWORD_PTR) DIPROP_INSTANCENAME: { 659 DIPROPSTRING *ps = (DIPROPSTRING*) pdiph; 660 DIDEVICEINSTANCEW didev; 661 662 didev.dwSize = sizeof(didev); 663 664 IDirectInputDevice_GetDeviceInfo(iface, &didev); 665 if (LOWORD(rguid) == (DWORD_PTR) DIPROP_PRODUCTNAME) 666 lstrcpynW(ps->wsz, didev.tszProductName, MAX_PATH); 667 else 668 lstrcpynW(ps->wsz, didev.tszInstanceName, MAX_PATH); 669 670 return DI_OK; 671 } 672 default: 673 return IDirectInputDevice2WImpl_GetProperty(iface, rguid, pdiph); 674 } 675 } 676 677 return DI_OK; 678 } 679 680 HRESULT WINAPI JoystickAGenericImpl_GetProperty(LPDIRECTINPUTDEVICE8A iface, REFGUID rguid, LPDIPROPHEADER pdiph) 681 { 682 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 683 return JoystickWGenericImpl_GetProperty(IDirectInputDevice8W_from_impl(This), rguid, pdiph); 684 } 685 686 /****************************************************************************** 687 * GetDeviceInfo : get information about a device's identity 688 */ 689 HRESULT WINAPI JoystickAGenericImpl_GetDeviceInfo( 690 LPDIRECTINPUTDEVICE8A iface, 691 LPDIDEVICEINSTANCEA pdidi) 692 { 693 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 694 DIPROPDWORD pd; 695 DWORD index = 0; 696 697 TRACE("(%p,%p)\n", This, pdidi); 698 699 if (pdidi == NULL) { 700 WARN("invalid pointer\n"); 701 return E_POINTER; 702 } 703 704 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3A)) && 705 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEA))) { 706 WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize); 707 return DIERR_INVALIDPARAM; 708 } 709 710 /* Try to get joystick index */ 711 pd.diph.dwSize = sizeof(pd); 712 pd.diph.dwHeaderSize = sizeof(pd.diph); 713 pd.diph.dwObj = 0; 714 pd.diph.dwHow = DIPH_DEVICE; 715 if (SUCCEEDED(IDirectInputDevice2_GetProperty(iface, DIPROP_JOYSTICKID, &pd.diph))) 716 index = pd.dwData; 717 718 /* Return joystick */ 719 pdidi->guidInstance = This->guidInstance; 720 pdidi->guidProduct = This->guidProduct; 721 /* we only support traditional joysticks for now */ 722 pdidi->dwDevType = This->devcaps.dwDevType; 723 snprintf(pdidi->tszInstanceName, MAX_PATH, "Joystick %d", index); 724 lstrcpynA(pdidi->tszProductName, This->name, MAX_PATH); 725 if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3A)) { 726 pdidi->guidFFDriver = GUID_NULL; 727 pdidi->wUsagePage = 0; 728 pdidi->wUsage = 0; 729 } 730 731 return DI_OK; 732 } 733 734 /****************************************************************************** 735 * GetDeviceInfo : get information about a device's identity 736 */ 737 HRESULT WINAPI JoystickWGenericImpl_GetDeviceInfo( 738 LPDIRECTINPUTDEVICE8W iface, 739 LPDIDEVICEINSTANCEW pdidi) 740 { 741 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 742 CHAR buffer[MAX_PATH]; 743 DIPROPDWORD pd; 744 DWORD index = 0; 745 746 TRACE("(%p,%p)\n", iface, pdidi); 747 748 if ((pdidi->dwSize != sizeof(DIDEVICEINSTANCE_DX3W)) && 749 (pdidi->dwSize != sizeof(DIDEVICEINSTANCEW))) { 750 WARN("invalid parameter: pdidi->dwSize = %d\n", pdidi->dwSize); 751 return DIERR_INVALIDPARAM; 752 } 753 754 /* Try to get joystick index */ 755 pd.diph.dwSize = sizeof(pd); 756 pd.diph.dwHeaderSize = sizeof(pd.diph); 757 pd.diph.dwObj = 0; 758 pd.diph.dwHow = DIPH_DEVICE; 759 if (SUCCEEDED(IDirectInputDevice2_GetProperty(iface, DIPROP_JOYSTICKID, &pd.diph))) 760 index = pd.dwData; 761 762 /* Return joystick */ 763 pdidi->guidInstance = This->guidInstance; 764 pdidi->guidProduct = This->guidProduct; 765 /* we only support traditional joysticks for now */ 766 pdidi->dwDevType = This->devcaps.dwDevType; 767 snprintf(buffer, sizeof(buffer), "Joystick %d", index); 768 MultiByteToWideChar(CP_ACP, 0, buffer, -1, pdidi->tszInstanceName, MAX_PATH); 769 MultiByteToWideChar(CP_ACP, 0, This->name, -1, pdidi->tszProductName, MAX_PATH); 770 if (pdidi->dwSize > sizeof(DIDEVICEINSTANCE_DX3W)) { 771 pdidi->guidFFDriver = GUID_NULL; 772 pdidi->wUsagePage = 0; 773 pdidi->wUsage = 0; 774 } 775 776 return DI_OK; 777 } 778 779 HRESULT WINAPI JoystickWGenericImpl_Poll(LPDIRECTINPUTDEVICE8W iface) 780 { 781 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 782 783 TRACE("(%p)\n",This); 784 785 if (!This->base.acquired) { 786 WARN("not acquired\n"); 787 return DIERR_NOTACQUIRED; 788 } 789 790 This->joy_polldev(IDirectInputDevice8A_from_impl(This)); 791 return DI_OK; 792 } 793 794 HRESULT WINAPI JoystickAGenericImpl_Poll(LPDIRECTINPUTDEVICE8A iface) 795 { 796 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 797 return JoystickWGenericImpl_Poll(IDirectInputDevice8W_from_impl(This)); 798 } 799 800 /****************************************************************************** 801 * GetDeviceState : returns the "state" of the joystick. 802 * 803 */ 804 HRESULT WINAPI JoystickWGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8W iface, DWORD len, LPVOID ptr) 805 { 806 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 807 808 TRACE("(%p,0x%08x,%p)\n", This, len, ptr); 809 810 if (!This->base.acquired) { 811 WARN("not acquired\n"); 812 return DIERR_NOTACQUIRED; 813 } 814 815 /* update joystick state */ 816 This->joy_polldev(IDirectInputDevice8A_from_impl(This)); 817 818 /* convert and copy data to user supplied buffer */ 819 fill_DataFormat(ptr, len, &This->js, &This->base.data_format); 820 821 return DI_OK; 822 } 823 824 HRESULT WINAPI JoystickAGenericImpl_GetDeviceState(LPDIRECTINPUTDEVICE8A iface, DWORD len, LPVOID ptr) 825 { 826 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 827 return JoystickWGenericImpl_GetDeviceState(IDirectInputDevice8W_from_impl(This), len, ptr); 828 } 829 830 831 HRESULT WINAPI JoystickWGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8W iface, 832 LPDIACTIONFORMATW lpdiaf, 833 LPCWSTR lpszUserName, 834 DWORD dwFlags) 835 { 836 static const DWORD object_types[] = { DIDFT_AXIS, DIDFT_BUTTON }; 837 static const DWORD type_map[] = { DIDFT_RELAXIS, DIDFT_PSHBUTTON }; 838 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 839 unsigned int i, j; 840 BOOL has_actions = FALSE; 841 WCHAR *username; 842 DWORD size; 843 BOOL load_success = FALSE; 844 845 FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags); 846 847 /* Unless asked the contrary by these flags, try to load a previous mapping */ 848 if (!(dwFlags & DIDBAM_HWDEFAULTS)) 849 { 850 if (!lpszUserName) 851 GetUserNameW(NULL, &size); 852 else 853 size = lstrlenW(lpszUserName) + 1; 854 855 username = heap_alloc(size * sizeof(WCHAR)); 856 if (!lpszUserName) 857 GetUserNameW(username, &size); 858 else 859 lstrcpynW(username, lpszUserName, size); 860 861 load_success = load_mapping_settings(&This->base, lpdiaf, username); 862 heap_free(username); 863 } 864 865 if (load_success) return DI_OK; 866 867 for (i=0; i < lpdiaf->dwNumActions; i++) 868 { 869 DWORD inst = (0x000000ff & (lpdiaf->rgoAction[i].dwSemantic)) - 1; 870 DWORD type = 0x000000ff & (lpdiaf->rgoAction[i].dwSemantic >> 8); 871 DWORD genre = 0xff000000 & lpdiaf->rgoAction[i].dwSemantic; 872 873 /* Don't touch a user configured action */ 874 if (lpdiaf->rgoAction[i].dwHow == DIAH_USERCONFIG) continue; 875 876 /* Only consider actions of the right genre */ 877 if (lpdiaf->dwGenre != genre && genre != DIGENRE_ANY) continue; 878 879 for (j = 0; j < ARRAY_SIZE(object_types); j++) 880 { 881 if (type & object_types[j]) 882 { 883 /* Ensure that the object exists */ 884 LPDIOBJECTDATAFORMAT odf = dataformat_to_odf_by_type(This->base.data_format.wine_df, inst, object_types[j]); 885 886 if (odf != NULL) 887 { 888 lpdiaf->rgoAction[i].dwObjID = type_map[j] | (0x0000ff00 & (inst << 8)); 889 lpdiaf->rgoAction[i].guidInstance = This->base.guid; 890 lpdiaf->rgoAction[i].dwHow = DIAH_DEFAULT; 891 892 has_actions = TRUE; 893 894 /* No need to try other types if the action was already mapped */ 895 break; 896 } 897 } 898 } 899 } 900 901 if (!has_actions) return DI_NOEFFECT; 902 903 return IDirectInputDevice8WImpl_BuildActionMap(iface, lpdiaf, lpszUserName, dwFlags); 904 } 905 906 HRESULT WINAPI JoystickAGenericImpl_BuildActionMap(LPDIRECTINPUTDEVICE8A iface, 907 LPDIACTIONFORMATA lpdiaf, 908 LPCSTR lpszUserName, 909 DWORD dwFlags) 910 { 911 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 912 DIACTIONFORMATW diafW; 913 HRESULT hr; 914 WCHAR *lpszUserNameW = NULL; 915 int username_size; 916 917 diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiaf->dwNumActions); 918 _copy_diactionformatAtoW(&diafW, lpdiaf); 919 920 if (lpszUserName != NULL) 921 { 922 username_size = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0); 923 lpszUserNameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*username_size); 924 MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, lpszUserNameW, username_size); 925 } 926 927 hr = JoystickWGenericImpl_BuildActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags); 928 929 _copy_diactionformatWtoA(lpdiaf, &diafW); 930 HeapFree(GetProcessHeap(), 0, diafW.rgoAction); 931 HeapFree(GetProcessHeap(), 0, lpszUserNameW); 932 933 return hr; 934 } 935 936 HRESULT WINAPI JoystickWGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8W iface, 937 LPDIACTIONFORMATW lpdiaf, 938 LPCWSTR lpszUserName, 939 DWORD dwFlags) 940 { 941 JoystickGenericImpl *This = impl_from_IDirectInputDevice8W(iface); 942 943 FIXME("(%p)->(%p,%s,%08x): semi-stub !\n", This, lpdiaf, debugstr_w(lpszUserName), dwFlags); 944 945 return _set_action_map(iface, lpdiaf, lpszUserName, dwFlags, This->base.data_format.wine_df); 946 } 947 948 HRESULT WINAPI JoystickAGenericImpl_SetActionMap(LPDIRECTINPUTDEVICE8A iface, 949 LPDIACTIONFORMATA lpdiaf, 950 LPCSTR lpszUserName, 951 DWORD dwFlags) 952 { 953 JoystickGenericImpl *This = impl_from_IDirectInputDevice8A(iface); 954 DIACTIONFORMATW diafW; 955 HRESULT hr; 956 WCHAR *lpszUserNameW = NULL; 957 int username_size; 958 959 diafW.rgoAction = HeapAlloc(GetProcessHeap(), 0, sizeof(DIACTIONW)*lpdiaf->dwNumActions); 960 _copy_diactionformatAtoW(&diafW, lpdiaf); 961 962 if (lpszUserName != NULL) 963 { 964 username_size = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0); 965 lpszUserNameW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*username_size); 966 MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, lpszUserNameW, username_size); 967 } 968 969 hr = JoystickWGenericImpl_SetActionMap(&This->base.IDirectInputDevice8W_iface, &diafW, lpszUserNameW, dwFlags); 970 971 lpdiaf->dwCRC = diafW.dwCRC; 972 973 HeapFree(GetProcessHeap(), 0, diafW.rgoAction); 974 HeapFree(GetProcessHeap(), 0, lpszUserNameW); 975 976 return hr; 977 } 978 979 /* 980 * This maps the read value (from the input event) to a value in the 981 * 'wanted' range. 982 * Notes: 983 * Dead zone is in % multiplied by a 100 (range 0..10000) 984 */ 985 LONG joystick_map_axis(ObjProps *props, int val) 986 { 987 LONG ret; 988 LONG dead_zone = MulDiv( props->lDeadZone, props->lDevMax - props->lDevMin, 10000 ); 989 LONG dev_range = props->lDevMax - props->lDevMin - dead_zone; 990 991 /* Center input */ 992 val -= (props->lDevMin + props->lDevMax) / 2; 993 994 /* Remove dead zone */ 995 if (abs( val ) <= dead_zone / 2) 996 val = 0; 997 else 998 val = val < 0 ? val + dead_zone / 2 : val - dead_zone / 2; 999 1000 /* Scale and map the value from the device range into the required range */ 1001 ret = MulDiv( val, props->lMax - props->lMin, dev_range ) + 1002 (props->lMin + props->lMax) / 2; 1003 1004 /* Clamp in case or rounding errors */ 1005 if (ret > props->lMax) ret = props->lMax; 1006 else if (ret < props->lMin) ret = props->lMin; 1007 1008 TRACE( "(%d <%d> %d) -> (%d <%d> %d): val=%d ret=%d\n", 1009 props->lDevMin, dead_zone, props->lDevMax, 1010 props->lMin, props->lDeadZone, props->lMax, 1011 val, ret ); 1012 1013 return ret; 1014 } 1015 1016 /* 1017 * Maps POV x & y event values to a DX "clock" position: 1018 * 0 1019 * 31500 4500 1020 * 27000 -1 9000 1021 * 22500 13500 1022 * 18000 1023 */ 1024 DWORD joystick_map_pov(const POINTL *p) 1025 { 1026 if (p->x > 0) 1027 return p->y < 0 ? 4500 : !p->y ? 9000 : 13500; 1028 else if (p->x < 0) 1029 return p->y < 0 ? 31500 : !p->y ? 27000 : 22500; 1030 else 1031 return p->y < 0 ? 0 : !p->y ? -1 : 18000; 1032 } 1033 1034 /* 1035 * Setup the dinput options. 1036 */ 1037 1038 HRESULT setup_dinput_options(JoystickGenericImpl *This, const int *default_axis_map) 1039 { 1040 char buffer[MAX_PATH+16]; 1041 HKEY hkey, appkey; 1042 int tokens = 0; 1043 int axis = 0; 1044 int pov = 0; 1045 int button; 1046 1047 get_app_key(&hkey, &appkey); 1048 1049 /* get options */ 1050 1051 if (!get_config_key(hkey, appkey, "DefaultDeadZone", buffer, sizeof(buffer))) 1052 { 1053 This->deadzone = atoi(buffer); 1054 TRACE("setting default deadzone to: \"%s\" %d\n", buffer, This->deadzone); 1055 } 1056 1057 for (button = 0; button < MAX_MAP_BUTTONS; button++) 1058 This->button_map[button] = button; 1059 1060 if (!get_config_key(hkey, appkey, "ButtonMap", buffer, sizeof(buffer))) 1061 { 1062 static const char *delim = ","; 1063 int button = 0; 1064 char *token; 1065 1066 TRACE("ButtonMap = \"%s\"\n", buffer); 1067 for (token = strtok(buffer, delim); 1068 token != NULL && button < MAX_MAP_BUTTONS; 1069 token = strtok(NULL, delim), button++) 1070 { 1071 char *s; 1072 int value = strtol(token, &s, 10); 1073 if (value < 0 || *s != '\0') 1074 { 1075 ERR("invalid button number: \"%s\"", token); 1076 } 1077 else 1078 { 1079 TRACE("mapping physical button %d to DInput button %d", value, button); 1080 This->button_map[value] = button; 1081 } 1082 } 1083 } 1084 1085 This->axis_map = HeapAlloc(GetProcessHeap(), 0, This->device_axis_count * sizeof(int)); 1086 if (!This->axis_map) return DIERR_OUTOFMEMORY; 1087 1088 if (!get_config_key(hkey, appkey, This->name, buffer, sizeof(buffer))) 1089 { 1090 static const char *axis_names[] = {"X", "Y", "Z", "Rx", "Ry", "Rz", 1091 "Slider1", "Slider2", 1092 "POV1", "POV2", "POV3", "POV4"}; 1093 const char *delim = ","; 1094 char * ptr; 1095 TRACE("\"%s\" = \"%s\"\n", This->name, buffer); 1096 1097 if ((ptr = strtok(buffer, delim)) != NULL) 1098 { 1099 do 1100 { 1101 int i; 1102 1103 for (i = 0; i < ARRAY_SIZE(axis_names); i++) 1104 { 1105 if (!strcmp(ptr, axis_names[i])) 1106 { 1107 if (!strncmp(ptr, "POV", 3)) 1108 { 1109 if (pov >= 4) 1110 { 1111 WARN("Only 4 POVs supported - ignoring extra\n"); 1112 i = -1; 1113 } 1114 else 1115 { 1116 /* Pov takes two axes */ 1117 This->axis_map[tokens++] = i; 1118 pov++; 1119 } 1120 } 1121 else 1122 { 1123 if (axis >= 8) 1124 { 1125 FIXME("Only 8 Axes supported - ignoring extra\n"); 1126 i = -1; 1127 } 1128 else 1129 axis++; 1130 } 1131 break; 1132 } 1133 } 1134 1135 if (i == ARRAY_SIZE(axis_names)) 1136 { 1137 ERR("invalid joystick axis type: \"%s\"\n", ptr); 1138 i = -1; 1139 } 1140 1141 This->axis_map[tokens] = i; 1142 tokens++; 1143 } while ((ptr = strtok(NULL, delim)) != NULL); 1144 1145 if (tokens != This->device_axis_count) 1146 { 1147 ERR("not all joystick axes mapped: %d axes(%d,%d), %d arguments\n", 1148 This->device_axis_count, axis, pov, tokens); 1149 while (tokens < This->device_axis_count) 1150 { 1151 This->axis_map[tokens] = -1; 1152 tokens++; 1153 } 1154 } 1155 } 1156 } 1157 else 1158 { 1159 int i; 1160 1161 if (default_axis_map) 1162 { 1163 /* Use default mapping from the driver */ 1164 for (i = 0; i < This->device_axis_count; i++) 1165 { 1166 This->axis_map[i] = default_axis_map[i]; 1167 tokens = default_axis_map[i]; 1168 if (tokens < 0) 1169 continue; 1170 if (tokens < 8) 1171 axis++; 1172 else if (tokens < 15) 1173 { 1174 i++; 1175 pov++; 1176 This->axis_map[i] = default_axis_map[i]; 1177 } 1178 } 1179 } 1180 else 1181 { 1182 /* No config - set default mapping. */ 1183 for (i = 0; i < This->device_axis_count; i++) 1184 { 1185 if (i < 8) 1186 This->axis_map[i] = axis++; 1187 else if (i < 15) 1188 { 1189 This->axis_map[i++] = 8 + pov; 1190 This->axis_map[i ] = 8 + pov++; 1191 } 1192 else 1193 This->axis_map[i] = -1; 1194 } 1195 } 1196 } 1197 This->devcaps.dwAxes = axis; 1198 This->devcaps.dwPOVs = pov; 1199 1200 if (appkey) RegCloseKey(appkey); 1201 if (hkey) RegCloseKey(hkey); 1202 1203 return DI_OK; 1204 } 1205