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