1 /* DirectInput Linux Event Device Effect 2 * 3 * Copyright 2005 Daniel Remenak 4 * 5 * Thanks to Google's Summer of Code Program (2005) 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "config.h" 23 24 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION 25 26 #include <stdarg.h> 27 #include <string.h> 28 #ifdef HAVE_LINUX_INPUT_H 29 # include <linux/input.h> 30 # undef SW_MAX 31 #endif 32 #include <limits.h> 33 #include <errno.h> 34 #ifdef HAVE_UNISTD_H 35 # include <unistd.h> 36 #endif 37 #include <math.h> 38 #include "wine/debug.h" 39 #include "wine/unicode.h" 40 #include "windef.h" 41 #include "winbase.h" 42 #include "winerror.h" 43 #include "dinput.h" 44 45 #include "device_private.h" 46 #include "joystick_private.h" 47 48 WINE_DEFAULT_DEBUG_CHANNEL(dinput); 49 50 static const IDirectInputEffectVtbl LinuxInputEffectVtbl; 51 typedef struct LinuxInputEffectImpl LinuxInputEffectImpl; 52 struct LinuxInputEffectImpl 53 { 54 IDirectInputEffect IDirectInputEffect_iface; 55 LONG ref; 56 GUID guid; 57 58 struct ff_effect effect; /* Effect data */ 59 int gain; /* Effect gain */ 60 BOOL first_axis_is_x; 61 int* fd; /* Parent device */ 62 struct list *entry; /* Entry into the parent's list of effects */ 63 }; 64 65 static inline LinuxInputEffectImpl *impl_from_IDirectInputEffect(IDirectInputEffect *iface) 66 { 67 return CONTAINING_RECORD(iface, LinuxInputEffectImpl, IDirectInputEffect_iface); 68 } 69 70 static double ff_effect_direction_to_rad(unsigned int dir) 71 { 72 return (dir & 0xffff) * M_PI / 0x8000; 73 } 74 75 static void ff_dump_effect(struct ff_effect *effect) 76 { 77 const char *type = "(Unknown)", *length = "INFINITE"; 78 struct ff_envelope *env = NULL; 79 double angle; 80 #define FE(x) case x: type = #x; break 81 switch (effect->type) 82 { 83 FE(FF_RUMBLE); 84 FE(FF_PERIODIC); 85 FE(FF_CONSTANT); 86 FE(FF_SPRING); 87 FE(FF_FRICTION); 88 FE(FF_DAMPER); 89 FE(FF_INERTIA); 90 FE(FF_RAMP); 91 } 92 #undef FE 93 94 /* rotate so 0 points right */ 95 angle = 360 - ff_effect_direction_to_rad(effect->direction + 0xc000) * 180 / M_PI; 96 97 if (effect->replay.length) 98 length = wine_dbg_sprintf("%u ms", effect->replay.length); 99 100 TRACE("type 0x%x %s, id %d, direction 0x%x (source angle %.2f), time length %s, start delay %u ms\n", 101 effect->type, type, effect->id, effect->direction, angle, length, effect->replay.delay); 102 if (effect->trigger.button || effect->trigger.interval) 103 TRACE(" -> trigger button %u, re-trigger interval %u ms\n", 104 effect->trigger.button, effect->trigger.interval); 105 106 if (effect->type == FF_PERIODIC) 107 { 108 struct ff_periodic_effect *per = &effect->u.periodic; 109 const char *wave = "(Unknown)"; 110 #define FE(x) case x: wave = #x; break 111 switch (per->waveform) 112 { 113 FE(FF_SQUARE); 114 FE(FF_TRIANGLE); 115 FE(FF_SINE); 116 FE(FF_SAW_UP); 117 FE(FF_SAW_DOWN); 118 FE(FF_CUSTOM); 119 } 120 #undef FE 121 angle = ff_effect_direction_to_rad(per->phase) * 180 / M_PI; 122 TRACE(" -> waveform 0x%x %s, period %u ms, magnitude %d, offset %d, phase 0x%x (angle %.2f), custom len %d\n", 123 per->waveform, wave, per->period, per->magnitude, per->offset, per->phase, angle, per->custom_len); 124 env = &per->envelope; 125 } 126 else if (effect->type == FF_CONSTANT) 127 { 128 struct ff_constant_effect *cons = &effect->u.constant; 129 TRACE(" -> level %d\n", cons->level); 130 env = &cons->envelope; 131 } 132 else if (effect->type == FF_RAMP) 133 { 134 struct ff_ramp_effect *ramp = &effect->u.ramp; 135 TRACE(" -> start/end level %d/%d\n", ramp->start_level, ramp->end_level); 136 env = &ramp->envelope; 137 } 138 else if (effect->type == FF_RUMBLE) 139 { 140 struct ff_rumble_effect *rumble = &effect->u.rumble; 141 TRACE(" -> strong/weak magnitude %u/%u\n", rumble->strong_magnitude, rumble->weak_magnitude); 142 } 143 else if (effect->type == FF_SPRING || effect->type == FF_FRICTION || 144 effect->type == FF_DAMPER || effect->type == FF_INERTIA) 145 { 146 struct ff_condition_effect *cond = effect->u.condition; 147 int i; 148 for (i = 0; i < 2; i++) 149 { 150 /* format numbers here to make them align correctly */ 151 TRACE(" -> [%d] right/left saturation %5u/%5u, right/left coefficient %5d/%5d," 152 " deadband %5u, center %5d\n", i, cond[i].right_saturation, cond[i].left_saturation, 153 cond[i].right_coeff, cond[i].left_coeff, cond[i].deadband, cond[i].center); 154 } 155 } 156 157 if (env) 158 TRACE(" -> envelope attack length(ms)/level %u/%u, fade length(ms)/level %u/%u\n", 159 env->attack_length, env->attack_level, env->fade_length, env->fade_level); 160 } 161 162 /****************************************************************************** 163 * LinuxInputEffectImpl 164 */ 165 166 static ULONG WINAPI LinuxInputEffectImpl_AddRef( 167 LPDIRECTINPUTEFFECT iface) 168 { 169 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 170 ULONG ref = InterlockedIncrement(&This->ref); 171 TRACE( "(%p) ref %d\n", This, ref ); 172 return ref; 173 } 174 175 static HRESULT WINAPI LinuxInputEffectImpl_Download( 176 LPDIRECTINPUTEFFECT iface) 177 { 178 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 179 int ret, old_effect_id; 180 181 TRACE("(this=%p)\n", This); 182 ff_dump_effect(&This->effect); 183 184 old_effect_id = This->effect.id; 185 if (ioctl(*(This->fd), EVIOCSFF, &This->effect) != -1) 186 return DI_OK; 187 188 /* Linux kernel < 3.14 has a bug that incorrectly assigns an effect ID even 189 * on error, restore it here if that is the case. */ 190 This->effect.id = old_effect_id; 191 192 switch (errno) 193 { 194 case EINVAL: 195 ret = DIERR_INVALIDPARAM; 196 break; 197 case ENOSPC: 198 ret = DIERR_DEVICEFULL; 199 break; 200 case ENOMEM: 201 ret = DIERR_OUTOFMEMORY; 202 break; 203 default: 204 ret = DIERR_INPUTLOST; 205 break; 206 } 207 TRACE("Could not upload effect to fd %d, errno %d \"%s\", returning 0x%x.\n", 208 *This->fd, errno, strerror(errno), ret); 209 return ret; 210 } 211 212 static HRESULT WINAPI LinuxInputEffectImpl_Escape( 213 LPDIRECTINPUTEFFECT iface, 214 LPDIEFFESCAPE pesc) 215 { 216 WARN("(this=%p,%p): invalid: no hardware-specific escape codes in this" 217 " driver!\n", iface, pesc); 218 219 return DI_OK; 220 } 221 222 static HRESULT WINAPI LinuxInputEffectImpl_GetEffectGuid( 223 LPDIRECTINPUTEFFECT iface, 224 LPGUID pguid) 225 { 226 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 227 228 TRACE("(this=%p,%p)\n", This, pguid); 229 230 *pguid = This->guid; 231 232 return DI_OK; 233 } 234 235 static HRESULT WINAPI LinuxInputEffectImpl_GetEffectStatus( 236 LPDIRECTINPUTEFFECT iface, 237 LPDWORD pdwFlags) 238 { 239 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 240 241 TRACE("(this=%p,%p)\n", This, pdwFlags); 242 243 if (!pdwFlags) 244 return E_POINTER; 245 246 if (This->effect.id == -1) 247 return DIERR_NOTDOWNLOADED; 248 249 /* linux sends the effect status through an event. 250 * that event is trapped by our parent joystick driver 251 * and there is no clean way to pass it back to us. */ 252 FIXME("Not enough information to provide a status.\n"); 253 254 (*pdwFlags) = 0; 255 256 return DI_OK; 257 } 258 259 static HRESULT WINAPI LinuxInputEffectImpl_GetParameters( 260 LPDIRECTINPUTEFFECT iface, 261 LPDIEFFECT peff, 262 DWORD dwFlags) 263 { 264 HRESULT diErr = DI_OK; 265 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 266 TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags); 267 268 /* Major conversion factors are: 269 * times: millisecond (linux) -> microsecond (windows) (x * 1000) 270 * forces: scale 0x7FFF (linux) -> scale 10000 (windows) approx ((x / 33) * 10) 271 * angles: scale 0x7FFF (linux) -> scale 35999 (windows) approx ((x / 33) * 36) 272 * angle bases: 0 -> -y (down) (linux) -> 0 -> +x (right) (windows) 273 */ 274 275 if (dwFlags & DIEP_AXES) { 276 if (peff->cAxes < 2 /* linuxinput effects always use 2 axes, x and y */) 277 diErr = DIERR_MOREDATA; 278 peff->cAxes = 2; 279 if (diErr) 280 return diErr; 281 else { 282 peff->rgdwAxes[0] = DIJOFS_X; 283 peff->rgdwAxes[1] = DIJOFS_Y; 284 } 285 } 286 287 if (dwFlags & DIEP_DIRECTION) { 288 if (peff->cAxes < 2) 289 diErr = DIERR_MOREDATA; 290 peff->cAxes = 2; 291 if (diErr) 292 return diErr; 293 else { 294 if (peff->dwFlags & DIEFF_CARTESIAN) { 295 /* rotate so 0 points right */ 296 double angle = ff_effect_direction_to_rad(This->effect.direction + 0xc000); 297 peff->rglDirection[0] = sin(angle) * 1000; 298 peff->rglDirection[1] = -cos(angle) * 1000; 299 } else { 300 /* Polar and spherical coordinates are the same for two or less 301 * axes. 302 * Note that we also use this case if NO flags are marked. 303 * According to MSDN, we should return the direction in the 304 * format that it was specified in, if no flags are marked. 305 */ 306 peff->rglDirection[0] = (This->effect.direction / 33) * 36 + 9000; 307 if (peff->rglDirection[0] > 35999) 308 peff->rglDirection[0] -= 35999; 309 } 310 } 311 } 312 313 if (dwFlags & DIEP_DURATION) 314 { 315 if (!This->effect.replay.length) /* infinite for the linux driver */ 316 peff->dwDuration = INFINITE; 317 else 318 peff->dwDuration = (DWORD)This->effect.replay.length * 1000; 319 } 320 321 if (dwFlags & DIEP_ENVELOPE) { 322 struct ff_envelope* env; 323 if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope; 324 else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope; 325 else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope; 326 else env = NULL; 327 if (env == NULL) { 328 peff->lpEnvelope = NULL; 329 } else if (peff->lpEnvelope == NULL) { 330 return DIERR_INVALIDPARAM; 331 } else { 332 peff->lpEnvelope->dwAttackLevel = (env->attack_level / 33) * 10; 333 peff->lpEnvelope->dwAttackTime = env->attack_length * 1000; 334 peff->lpEnvelope->dwFadeLevel = (env->fade_level / 33) * 10; 335 peff->lpEnvelope->dwFadeTime = env->fade_length * 1000; 336 } 337 } 338 339 if (dwFlags & DIEP_GAIN) { 340 peff->dwGain = This->gain * 10000 / 0xFFFF; 341 } 342 343 if (dwFlags & DIEP_SAMPLEPERIOD) { 344 /* the linux input ff driver has no support for setting 345 * the playback sample period. 0 means default. */ 346 peff->dwSamplePeriod = 0; 347 } 348 349 if ((dwFlags & DIEP_STARTDELAY) && peff->dwSize > sizeof(DIEFFECT_DX5)) 350 peff->dwStartDelay = This->effect.replay.delay * 1000; 351 352 if (dwFlags & DIEP_TRIGGERBUTTON) { 353 FIXME("LinuxInput button mapping needs redoing; for now, assuming we're using an actual joystick.\n"); 354 peff->dwTriggerButton = DIJOFS_BUTTON(This->effect.trigger.button - BTN_JOYSTICK); 355 } 356 357 if (dwFlags & DIEP_TRIGGERREPEATINTERVAL) { 358 peff->dwTriggerRepeatInterval = This->effect.trigger.interval * 1000; 359 } 360 361 if (dwFlags & DIEP_TYPESPECIFICPARAMS) { 362 DWORD expectedsize = 0; 363 if (This->effect.type == FF_PERIODIC) { 364 expectedsize = sizeof(DIPERIODIC); 365 } else if (This->effect.type == FF_CONSTANT) { 366 expectedsize = sizeof(DICONSTANTFORCE); 367 } else if (This->effect.type == FF_SPRING 368 || This->effect.type == FF_FRICTION 369 || This->effect.type == FF_INERTIA 370 || This->effect.type == FF_DAMPER) { 371 expectedsize = sizeof(DICONDITION) * 2; 372 } else if (This->effect.type == FF_RAMP) { 373 expectedsize = sizeof(DIRAMPFORCE); 374 } 375 if (expectedsize > peff->cbTypeSpecificParams) 376 diErr = DIERR_MOREDATA; 377 peff->cbTypeSpecificParams = expectedsize; 378 if (diErr) 379 return diErr; 380 else { 381 if (This->effect.type == FF_PERIODIC) { 382 LPDIPERIODIC tsp = peff->lpvTypeSpecificParams; 383 tsp->dwMagnitude = (This->effect.u.periodic.magnitude / 33) * 10; 384 tsp->lOffset = (This->effect.u.periodic.offset / 33) * 10; 385 tsp->dwPhase = (This->effect.u.periodic.phase / 33) * 36; 386 tsp->dwPeriod = (This->effect.u.periodic.period * 1000); 387 } else if (This->effect.type == FF_CONSTANT) { 388 LPDICONSTANTFORCE tsp = peff->lpvTypeSpecificParams; 389 tsp->lMagnitude = (This->effect.u.constant.level / 33) * 10; 390 } else if (This->effect.type == FF_SPRING 391 || This->effect.type == FF_FRICTION 392 || This->effect.type == FF_INERTIA 393 || This->effect.type == FF_DAMPER) { 394 LPDICONDITION tsp = peff->lpvTypeSpecificParams; 395 int i; 396 for (i = 0; i < 2; ++i) { 397 tsp[i].lOffset = (This->effect.u.condition[i].center / 33) * 10; 398 tsp[i].lPositiveCoefficient = (This->effect.u.condition[i].right_coeff / 33) * 10; 399 tsp[i].lNegativeCoefficient = (This->effect.u.condition[i].left_coeff / 33) * 10; 400 tsp[i].dwPositiveSaturation = (This->effect.u.condition[i].right_saturation / 33) * 10; 401 tsp[i].dwNegativeSaturation = (This->effect.u.condition[i].left_saturation / 33) * 10; 402 tsp[i].lDeadBand = (This->effect.u.condition[i].deadband / 33) * 10; 403 } 404 } else if (This->effect.type == FF_RAMP) { 405 LPDIRAMPFORCE tsp = peff->lpvTypeSpecificParams; 406 tsp->lStart = (This->effect.u.ramp.start_level / 33) * 10; 407 tsp->lEnd = (This->effect.u.ramp.end_level / 33) * 10; 408 } 409 } 410 } 411 412 return diErr; 413 } 414 415 static HRESULT WINAPI LinuxInputEffectImpl_Initialize( 416 LPDIRECTINPUTEFFECT iface, 417 HINSTANCE hinst, 418 DWORD dwVersion, 419 REFGUID rguid) 420 { 421 FIXME("(this=%p,%p,%d,%s): stub!\n", 422 iface, hinst, dwVersion, debugstr_guid(rguid)); 423 424 return DI_OK; 425 } 426 427 static HRESULT WINAPI LinuxInputEffectImpl_QueryInterface( 428 LPDIRECTINPUTEFFECT iface, 429 REFIID riid, 430 void **ppvObject) 431 { 432 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 433 434 TRACE("(this=%p,%s,%p)\n", This, debugstr_guid(riid), ppvObject); 435 436 if (IsEqualGUID(&IID_IUnknown, riid) || 437 IsEqualGUID(&IID_IDirectInputEffect, riid)) { 438 LinuxInputEffectImpl_AddRef(iface); 439 *ppvObject = This; 440 return 0; 441 } 442 443 TRACE("Unsupported interface!\n"); 444 return E_FAIL; 445 } 446 447 static HRESULT WINAPI LinuxInputEffectImpl_Start( 448 LPDIRECTINPUTEFFECT iface, 449 DWORD dwIterations, 450 DWORD dwFlags) 451 { 452 struct input_event event; 453 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 454 455 TRACE("(this=%p,%d,%d)\n", This, dwIterations, dwFlags); 456 457 if (!(dwFlags & DIES_NODOWNLOAD)) { 458 /* Download the effect if necessary */ 459 if (This->effect.id == -1) { 460 HRESULT res = LinuxInputEffectImpl_Download(iface); 461 if (res != DI_OK) 462 return res; 463 } 464 } 465 466 if (dwFlags & DIES_SOLO) { 467 FIXME("Solo mode requested: should be stopping all effects here!\n"); 468 } 469 470 event.type = EV_FF; 471 event.code = This->effect.id; 472 event.value = min( dwIterations, INT_MAX ); 473 if (write(*(This->fd), &event, sizeof(event)) == -1) { 474 FIXME("Unable to write event. Assuming device disconnected.\n"); 475 return DIERR_INPUTLOST; 476 } 477 478 return DI_OK; 479 } 480 481 static HRESULT WINAPI LinuxInputEffectImpl_SetParameters( 482 LPDIRECTINPUTEFFECT iface, 483 LPCDIEFFECT peff, 484 DWORD dwFlags) 485 { 486 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 487 DWORD type = typeFromGUID(&This->guid); 488 HRESULT retval = DI_OK; 489 490 TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags); 491 492 dump_DIEFFECT(peff, &This->guid, dwFlags); 493 494 if ((dwFlags & ~DIEP_NORESTART & ~DIEP_NODOWNLOAD & ~DIEP_START) == 0) { 495 /* set everything */ 496 dwFlags = DIEP_AXES | DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE | 497 DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_STARTDELAY | DIEP_TRIGGERBUTTON | 498 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; 499 } 500 501 if (dwFlags & DIEP_AXES) { 502 /* the linux input effect system only supports one or two axes */ 503 if (peff->cAxes > 2) 504 return DIERR_INVALIDPARAM; 505 else if (peff->cAxes < 1) 506 return DIERR_INCOMPLETEEFFECT; 507 This->first_axis_is_x = peff->rgdwAxes[0] == DIJOFS_X; 508 } 509 510 /* some of this may look funky, but it's 'cause the linux driver and directx have 511 * different opinions about which way direction "0" is. directx has 0 along the x 512 * axis (left), linux has it along the y axis (down). */ 513 if (dwFlags & DIEP_DIRECTION) { 514 if (peff->cAxes == 1) { 515 if (peff->dwFlags & DIEFF_CARTESIAN) { 516 if (dwFlags & DIEP_AXES) { 517 if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] >= 0) 518 This->effect.direction = 0x4000; 519 else if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] < 0) 520 This->effect.direction = 0xC000; 521 else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] >= 0) 522 This->effect.direction = 0; 523 else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] < 0) 524 This->effect.direction = 0x8000; 525 } 526 } else { 527 /* one-axis effects must use cartesian coords */ 528 return DIERR_INVALIDPARAM; 529 } 530 } 531 /* two axes */ 532 else 533 { 534 if (peff->dwFlags & DIEFF_CARTESIAN) 535 { 536 LONG x, y; 537 if (This->first_axis_is_x) 538 { 539 x = peff->rglDirection[0]; 540 y = peff->rglDirection[1]; 541 } 542 else 543 { 544 x = peff->rglDirection[1]; 545 y = peff->rglDirection[0]; 546 } 547 This->effect.direction = (unsigned int)((M_PI / 2 + atan2(y, x)) * 0x8000 / M_PI); 548 } 549 else 550 { 551 /* Polar and spherical are the same for 2 axes */ 552 /* Precision is important here, so we do double math with exact constants */ 553 This->effect.direction = (unsigned int)(((double)peff->rglDirection[0] / 18000) * 0x8000); 554 } 555 } 556 } 557 558 if (dwFlags & DIEP_DURATION) 559 { 560 if (peff->dwDuration == INFINITE) 561 This->effect.replay.length = 0; /* infinite for the linux driver */ 562 else if(peff->dwDuration > 1000) 563 This->effect.replay.length = peff->dwDuration / 1000; 564 else 565 This->effect.replay.length = 1; 566 } 567 568 if (dwFlags & DIEP_ENVELOPE) 569 { 570 struct ff_envelope* env; 571 if (This->effect.type == FF_CONSTANT) 572 env = &This->effect.u.constant.envelope; 573 else if (This->effect.type == FF_PERIODIC) 574 env = &This->effect.u.periodic.envelope; 575 else if (This->effect.type == FF_RAMP) 576 env = &This->effect.u.ramp.envelope; 577 else 578 env = NULL; 579 580 /* copy the envelope if it is present and the linux effect supports it */ 581 if (peff->lpEnvelope && env) 582 { 583 env->attack_length = peff->lpEnvelope->dwAttackTime / 1000; 584 env->attack_level = (peff->lpEnvelope->dwAttackLevel / 10) * 32; 585 env->fade_length = peff->lpEnvelope->dwFadeTime / 1000; 586 env->fade_level = (peff->lpEnvelope->dwFadeLevel / 10) * 32; 587 } 588 /* if the dinput envelope is NULL we will clear the linux envelope */ 589 else if (env) 590 { 591 env->attack_length = 0; 592 env->attack_level = 0; 593 env->fade_length = 0; 594 env->fade_level = 0; 595 } 596 else if(peff->lpEnvelope) 597 { 598 if(peff->lpEnvelope->dwAttackTime || peff->lpEnvelope->dwAttackLevel || 599 peff->lpEnvelope->dwFadeTime || peff->lpEnvelope->dwFadeLevel) 600 WARN("Ignoring dinput envelope not supported in the linux effect\n"); 601 } 602 } 603 604 /* Gain and Sample Period settings are not supported by the linux 605 * event system */ 606 if (dwFlags & DIEP_GAIN) { 607 This->gain = 0xFFFF * peff->dwGain / 10000; 608 TRACE("Effect gain requested but no effect gain functionality present.\n"); 609 } 610 611 if (dwFlags & DIEP_SAMPLEPERIOD) 612 TRACE("Sample period requested but no sample period functionality present.\n"); 613 614 if (dwFlags & DIEP_STARTDELAY) 615 if ((dwFlags & DIEP_STARTDELAY) && peff->dwSize > sizeof(DIEFFECT_DX5)) 616 This->effect.replay.delay = peff->dwStartDelay / 1000; 617 618 if (dwFlags & DIEP_TRIGGERBUTTON) { 619 if (peff->dwTriggerButton != -1) { 620 FIXME("Linuxinput button mapping needs redoing, assuming we're using a joystick.\n"); 621 FIXME("Trigger button translation not yet implemented!\n"); 622 } 623 This->effect.trigger.button = 0; 624 } 625 626 if (dwFlags & DIEP_TRIGGERREPEATINTERVAL) 627 This->effect.trigger.interval = peff->dwTriggerRepeatInterval / 1000; 628 629 if (dwFlags & DIEP_TYPESPECIFICPARAMS) 630 { 631 if (!(peff->lpvTypeSpecificParams)) 632 return DIERR_INCOMPLETEEFFECT; 633 634 if (type == DIEFT_PERIODIC) 635 { 636 DIPERIODIC *tsp; 637 if (peff->cbTypeSpecificParams != sizeof(DIPERIODIC)) 638 return DIERR_INVALIDPARAM; 639 tsp = peff->lpvTypeSpecificParams; 640 641 This->effect.u.periodic.magnitude = (tsp->dwMagnitude / 10) * 32; 642 This->effect.u.periodic.offset = (tsp->lOffset / 10) * 32; 643 /* phase ranges from 0 - 35999 in dinput and 0 - 65535 on Linux */ 644 This->effect.u.periodic.phase = (tsp->dwPhase / 36) * 65; 645 /* dinput uses microseconds, Linux uses milliseconds */ 646 if (tsp->dwPeriod <= 1000) 647 This->effect.u.periodic.period = 1; 648 else 649 This->effect.u.periodic.period = tsp->dwPeriod / 1000; 650 } 651 else if (type == DIEFT_CONSTANTFORCE) 652 { 653 LPCDICONSTANTFORCE tsp; 654 if (peff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) 655 return DIERR_INVALIDPARAM; 656 tsp = peff->lpvTypeSpecificParams; 657 This->effect.u.constant.level = (max(min(tsp->lMagnitude, 10000), -10000) / 10) * 32; 658 } else if (type == DIEFT_RAMPFORCE) { 659 LPCDIRAMPFORCE tsp; 660 if (peff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) 661 return DIERR_INVALIDPARAM; 662 tsp = peff->lpvTypeSpecificParams; 663 This->effect.u.ramp.start_level = (tsp->lStart / 10) * 32; 664 This->effect.u.ramp.end_level = (tsp->lEnd / 10) * 32; 665 } 666 else if (type == DIEFT_CONDITION) 667 { 668 DICONDITION *tsp = peff->lpvTypeSpecificParams; 669 struct ff_condition_effect *cond = This->effect.u.condition; 670 int i, j, sources; 671 double factor[2]; 672 673 if (peff->cbTypeSpecificParams == sizeof(DICONDITION)) 674 { 675 /* One condition block. This needs to be rotated to direction, 676 * and expanded to separate x and y conditions. Ensures 0 points right */ 677 double angle = ff_effect_direction_to_rad(This->effect.direction + 0xc000); 678 factor[0] = sin(angle); 679 factor[1] = -cos(angle); 680 sources = 1; 681 } 682 else if (peff->cbTypeSpecificParams == 2 * sizeof(DICONDITION)) 683 { 684 /* Direct parameter copy without changes */ 685 factor[0] = factor[1] = 1; 686 sources = 2; 687 } 688 else 689 return DIERR_INVALIDPARAM; 690 691 for (i = j = 0; i < 2; ++i) 692 { 693 cond[i].center = (int)(factor[i] * (tsp[j].lOffset / 10) * 32); 694 cond[i].right_coeff = (int)(factor[i] * (tsp[j].lPositiveCoefficient / 10) * 32); 695 cond[i].left_coeff = (int)(factor[i] * (tsp[j].lNegativeCoefficient / 10) * 32); 696 cond[i].right_saturation = (int)(factor[i] * (tsp[j].dwPositiveSaturation / 10) * 65); 697 cond[i].left_saturation = (int)(factor[i] * (tsp[j].dwNegativeSaturation / 10) * 65); 698 cond[i].deadband = (int)(factor[i] * (tsp[j].lDeadBand / 10) * 32); 699 if (sources == 2) 700 j++; 701 } 702 } 703 else 704 { 705 FIXME("Custom force types are not supported\n"); 706 return DIERR_INVALIDPARAM; 707 } 708 } 709 710 if (!(dwFlags & DIEP_NODOWNLOAD)) 711 retval = LinuxInputEffectImpl_Download(iface); 712 if (retval != DI_OK) 713 return DI_DOWNLOADSKIPPED; 714 715 if (dwFlags & DIEP_NORESTART) 716 TRACE("DIEP_NORESTART: not handled (we have no control of that).\n"); 717 718 if (dwFlags & DIEP_START) 719 retval = LinuxInputEffectImpl_Start(iface, 1, 0); 720 if (retval != DI_OK) 721 return retval; 722 723 return DI_OK; 724 } 725 726 static HRESULT WINAPI LinuxInputEffectImpl_Stop( 727 LPDIRECTINPUTEFFECT iface) 728 { 729 struct input_event event; 730 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 731 732 TRACE("(this=%p)\n", This); 733 734 event.type = EV_FF; 735 event.code = This->effect.id; 736 event.value = 0; 737 /* we don't care about the success or failure of this call */ 738 write(*(This->fd), &event, sizeof(event)); 739 740 return DI_OK; 741 } 742 743 static HRESULT WINAPI LinuxInputEffectImpl_Unload( 744 LPDIRECTINPUTEFFECT iface) 745 { 746 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 747 TRACE("(this=%p)\n", This); 748 749 /* Erase the downloaded effect */ 750 if (ioctl(*(This->fd), EVIOCRMFF, This->effect.id) == -1) 751 return DIERR_INVALIDPARAM; 752 753 /* Mark the effect as deallocated */ 754 This->effect.id = -1; 755 756 return DI_OK; 757 } 758 759 static ULONG WINAPI LinuxInputEffectImpl_Release(LPDIRECTINPUTEFFECT iface) 760 { 761 LinuxInputEffectImpl *This = impl_from_IDirectInputEffect(iface); 762 ULONG ref = InterlockedDecrement(&(This->ref)); 763 764 TRACE( "(%p) ref %d\n", This, ref ); 765 766 if (ref == 0) 767 { 768 LinuxInputEffectImpl_Stop(iface); 769 LinuxInputEffectImpl_Unload(iface); 770 list_remove(This->entry); 771 HeapFree(GetProcessHeap(), 0, LIST_ENTRY(This->entry, effect_list_item, entry)); 772 HeapFree(GetProcessHeap(), 0, This); 773 } 774 return ref; 775 } 776 777 /****************************************************************************** 778 * LinuxInputEffect 779 */ 780 781 DECLSPEC_HIDDEN HRESULT linuxinput_create_effect( 782 int* fd, 783 REFGUID rguid, 784 struct list *parent_list_entry, 785 LPDIRECTINPUTEFFECT* peff) 786 { 787 LinuxInputEffectImpl* newEffect = HeapAlloc(GetProcessHeap(), 788 HEAP_ZERO_MEMORY, sizeof(LinuxInputEffectImpl)); 789 DWORD type = typeFromGUID(rguid); 790 791 newEffect->IDirectInputEffect_iface.lpVtbl = &LinuxInputEffectVtbl; 792 newEffect->ref = 1; 793 newEffect->guid = *rguid; 794 newEffect->fd = fd; 795 newEffect->gain = 0xFFFF; 796 797 /* set the type. this cannot be changed over the effect's life. */ 798 switch (type) { 799 case DIEFT_PERIODIC: 800 newEffect->effect.type = FF_PERIODIC; 801 if (IsEqualGUID(rguid, &GUID_Sine)) { 802 newEffect->effect.u.periodic.waveform = FF_SINE; 803 } else if (IsEqualGUID(rguid, &GUID_Triangle)) { 804 newEffect->effect.u.periodic.waveform = FF_TRIANGLE; 805 } else if (IsEqualGUID(rguid, &GUID_Square)) { 806 newEffect->effect.u.periodic.waveform = FF_SQUARE; 807 } else if (IsEqualGUID(rguid, &GUID_SawtoothUp)) { 808 newEffect->effect.u.periodic.waveform = FF_SAW_UP; 809 } else if (IsEqualGUID(rguid, &GUID_SawtoothDown)) { 810 newEffect->effect.u.periodic.waveform = FF_SAW_DOWN; 811 } 812 break; 813 case DIEFT_CONSTANTFORCE: 814 newEffect->effect.type = FF_CONSTANT; 815 break; 816 case DIEFT_RAMPFORCE: 817 newEffect->effect.type = FF_RAMP; 818 break; 819 case DIEFT_CONDITION: 820 if (IsEqualGUID(rguid, &GUID_Spring)) { 821 newEffect->effect.type = FF_SPRING; 822 } else if (IsEqualGUID(rguid, &GUID_Friction)) { 823 newEffect->effect.type = FF_FRICTION; 824 } else if (IsEqualGUID(rguid, &GUID_Inertia)) { 825 newEffect->effect.type = FF_INERTIA; 826 } else if (IsEqualGUID(rguid, &GUID_Damper)) { 827 newEffect->effect.type = FF_DAMPER; 828 } 829 break; 830 case DIEFT_CUSTOMFORCE: 831 FIXME("Custom forces are not supported.\n"); 832 HeapFree(GetProcessHeap(), 0, newEffect); 833 return DIERR_INVALIDPARAM; 834 default: 835 FIXME("Unknown force type 0x%x.\n", type); 836 HeapFree(GetProcessHeap(), 0, newEffect); 837 return DIERR_INVALIDPARAM; 838 } 839 840 /* mark as non-uploaded */ 841 newEffect->effect.id = -1; 842 843 newEffect->entry = parent_list_entry; 844 845 *peff = &newEffect->IDirectInputEffect_iface; 846 847 TRACE("Creating linux input system effect (%p) with guid %s\n", 848 *peff, _dump_dinput_GUID(rguid)); 849 850 return DI_OK; 851 } 852 853 DECLSPEC_HIDDEN HRESULT linuxinput_get_info_A( 854 int fd, 855 REFGUID rguid, 856 LPDIEFFECTINFOA info) 857 { 858 DWORD type = typeFromGUID(rguid); 859 860 TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type); 861 862 if (!info) return E_POINTER; 863 864 if (info->dwSize != sizeof(DIEFFECTINFOA)) return DIERR_INVALIDPARAM; 865 866 info->guid = *rguid; 867 868 info->dwEffType = type; 869 /* the event device API does not support querying for all these things 870 * therefore we assume that we have support for them 871 * that's not as dangerous as it sounds, since drivers are allowed to 872 * ignore parameters they claim to support anyway */ 873 info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE 874 | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION 875 | DIEFT_SATURATION | DIEFT_STARTDELAY; 876 877 /* again, assume we have support for everything */ 878 info->dwStaticParams = DIEP_ALLPARAMS; 879 info->dwDynamicParams = info->dwStaticParams; 880 881 /* yes, this is windows behavior (print the GUID_Name for name) */ 882 strcpy(info->tszName, _dump_dinput_GUID(rguid)); 883 884 return DI_OK; 885 } 886 887 DECLSPEC_HIDDEN HRESULT linuxinput_get_info_W( 888 int fd, 889 REFGUID rguid, 890 LPDIEFFECTINFOW info) 891 { 892 DWORD type = typeFromGUID(rguid); 893 894 TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type); 895 896 if (!info) return E_POINTER; 897 898 if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM; 899 900 info->guid = *rguid; 901 902 info->dwEffType = type; 903 /* the event device API does not support querying for all these things 904 * therefore we assume that we have support for them 905 * that's not as dangerous as it sounds, since drivers are allowed to 906 * ignore parameters they claim to support anyway */ 907 info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE 908 | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION 909 | DIEFT_SATURATION | DIEFT_STARTDELAY; 910 911 /* again, assume we have support for everything */ 912 info->dwStaticParams = DIEP_ALLPARAMS; 913 info->dwDynamicParams = info->dwStaticParams; 914 915 /* yes, this is windows behavior (print the GUID_Name for name) */ 916 MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1, 917 info->tszName, MAX_PATH); 918 919 return DI_OK; 920 } 921 922 static const IDirectInputEffectVtbl LinuxInputEffectVtbl = { 923 LinuxInputEffectImpl_QueryInterface, 924 LinuxInputEffectImpl_AddRef, 925 LinuxInputEffectImpl_Release, 926 LinuxInputEffectImpl_Initialize, 927 LinuxInputEffectImpl_GetEffectGuid, 928 LinuxInputEffectImpl_GetParameters, 929 LinuxInputEffectImpl_SetParameters, 930 LinuxInputEffectImpl_Start, 931 LinuxInputEffectImpl_Stop, 932 LinuxInputEffectImpl_GetEffectStatus, 933 LinuxInputEffectImpl_Download, 934 LinuxInputEffectImpl_Unload, 935 LinuxInputEffectImpl_Escape 936 }; 937 938 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */ 939