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