Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygeneffect_linuxinput.c
Go to the documentation of this file.
00001 /* DirectInput Linux Event Device Effect 00002 * 00003 * Copyright 2005 Daniel Remenak 00004 * 00005 * Thanks to Google's Summer of Code Program (2005) 00006 * 00007 * This library is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00020 */ 00021 00022 #include "config.h" 00023 00024 #ifdef HAVE_STRUCT_FF_EFFECT_DIRECTION 00025 00026 #include <stdarg.h> 00027 #include <string.h> 00028 #ifdef HAVE_LINUX_INPUT_H 00029 # include <linux/input.h> 00030 # undef SW_MAX 00031 #endif 00032 #include <errno.h> 00033 #ifdef HAVE_UNISTD_H 00034 # include <unistd.h> 00035 #endif 00036 #include <math.h> 00037 #include "wine/debug.h" 00038 #include "wine/unicode.h" 00039 #include "windef.h" 00040 #include "winbase.h" 00041 #include "winerror.h" 00042 #include "dinput.h" 00043 00044 #include "device_private.h" 00045 00046 WINE_DEFAULT_DEBUG_CHANNEL(dinput); 00047 00048 static const IDirectInputEffectVtbl LinuxInputEffectVtbl; 00049 typedef struct LinuxInputEffectImpl LinuxInputEffectImpl; 00050 struct LinuxInputEffectImpl 00051 { 00052 const void *lpVtbl; 00053 LONG ref; 00054 GUID guid; 00055 00056 struct ff_effect effect; /* Effect data */ 00057 int gain; /* Effect gain */ 00058 int first_axis_is_x; 00059 int* fd; /* Parent device */ 00060 struct list *entry; /* Entry into the parent's list of effects */ 00061 }; 00062 00063 00064 /****************************************************************************** 00065 * DirectInputEffect Functional Helper 00066 */ 00067 00068 static DWORD _typeFromGUID(REFGUID guid) 00069 { 00070 if (IsEqualGUID(guid, &GUID_ConstantForce)) { 00071 return DIEFT_CONSTANTFORCE; 00072 } else if (IsEqualGUID(guid, &GUID_Square) 00073 || IsEqualGUID(guid, &GUID_Sine) 00074 || IsEqualGUID(guid, &GUID_Triangle) 00075 || IsEqualGUID(guid, &GUID_SawtoothUp) 00076 || IsEqualGUID(guid, &GUID_SawtoothDown)) { 00077 return DIEFT_PERIODIC; 00078 } else if (IsEqualGUID(guid, &GUID_RampForce)) { 00079 return DIEFT_RAMPFORCE; 00080 } else if (IsEqualGUID(guid, &GUID_Spring) 00081 || IsEqualGUID(guid, &GUID_Damper) 00082 || IsEqualGUID(guid, &GUID_Inertia) 00083 || IsEqualGUID(guid, &GUID_Friction)) { 00084 return DIEFT_CONDITION; 00085 } else if (IsEqualGUID(guid, &GUID_CustomForce)) { 00086 return DIEFT_CUSTOMFORCE; 00087 } else { 00088 WARN("GUID (%s) is not a known force type\n", _dump_dinput_GUID(guid)); 00089 return 0; 00090 } 00091 } 00092 00093 00094 /****************************************************************************** 00095 * DirectInputEffect debug helpers 00096 */ 00097 00098 static void _dump_DIEFFECT_flags(DWORD dwFlags) 00099 { 00100 if (TRACE_ON(dinput)) { 00101 unsigned int i; 00102 static const struct { 00103 DWORD mask; 00104 const char *name; 00105 } flags[] = { 00106 #define FE(x) { x, #x} 00107 FE(DIEFF_CARTESIAN), 00108 FE(DIEFF_OBJECTIDS), 00109 FE(DIEFF_OBJECTOFFSETS), 00110 FE(DIEFF_POLAR), 00111 FE(DIEFF_SPHERICAL) 00112 #undef FE 00113 }; 00114 for (i = 0; i < (sizeof(flags) / sizeof(flags[0])); i++) 00115 if (flags[i].mask & dwFlags) 00116 TRACE("%s ", flags[i].name); 00117 TRACE("\n"); 00118 } 00119 } 00120 00121 static void _dump_DIENVELOPE(LPCDIENVELOPE env) 00122 { 00123 if (env->dwSize != sizeof(DIENVELOPE)) { 00124 WARN("Non-standard DIENVELOPE structure size %d.\n", env->dwSize); 00125 } 00126 TRACE("Envelope has attack (level: %d time: %d), fade (level: %d time: %d)\n", 00127 env->dwAttackLevel, env->dwAttackTime, env->dwFadeLevel, env->dwFadeTime); 00128 } 00129 00130 static void _dump_DICONSTANTFORCE(LPCDICONSTANTFORCE frc) 00131 { 00132 TRACE("Constant force has magnitude %d\n", frc->lMagnitude); 00133 } 00134 00135 static void _dump_DIPERIODIC(LPCDIPERIODIC frc) 00136 { 00137 TRACE("Periodic force has magnitude %d, offset %d, phase %d, period %d\n", 00138 frc->dwMagnitude, frc->lOffset, frc->dwPhase, frc->dwPeriod); 00139 } 00140 00141 static void _dump_DIRAMPFORCE(LPCDIRAMPFORCE frc) 00142 { 00143 TRACE("Ramp force has start %d, end %d\n", 00144 frc->lStart, frc->lEnd); 00145 } 00146 00147 static void _dump_DICONDITION(LPCDICONDITION frc) 00148 { 00149 TRACE("Condition has offset %d, pos/neg coefficients %d and %d, pos/neg saturations %d and %d, deadband %d\n", 00150 frc->lOffset, frc->lPositiveCoefficient, frc->lNegativeCoefficient, 00151 frc->dwPositiveSaturation, frc->dwNegativeSaturation, frc->lDeadBand); 00152 } 00153 00154 static void _dump_DICUSTOMFORCE(LPCDICUSTOMFORCE frc) 00155 { 00156 unsigned int i; 00157 TRACE("Custom force uses %d channels, sample period %d. Has %d samples at %p.\n", 00158 frc->cChannels, frc->dwSamplePeriod, frc->cSamples, frc->rglForceData); 00159 if (frc->cSamples % frc->cChannels != 0) 00160 WARN("Custom force has a non-integral samples-per-channel count!\n"); 00161 if (TRACE_ON(dinput)) { 00162 TRACE("Custom force data (time aligned, axes in order):\n"); 00163 for (i = 1; i <= frc->cSamples; ++i) { 00164 TRACE("%d ", frc->rglForceData[i]); 00165 if (i % frc->cChannels == 0) 00166 TRACE("\n"); 00167 } 00168 } 00169 } 00170 00171 static void _dump_DIEFFECT(LPCDIEFFECT eff, REFGUID guid) 00172 { 00173 unsigned int i; 00174 DWORD type = _typeFromGUID(guid); 00175 00176 TRACE("Dumping DIEFFECT structure:\n"); 00177 TRACE(" - dwSize: %d\n", eff->dwSize); 00178 if ((eff->dwSize != sizeof(DIEFFECT)) && (eff->dwSize != sizeof(DIEFFECT_DX5))) { 00179 WARN("Non-standard DIEFFECT structure size %d\n", eff->dwSize); 00180 } 00181 TRACE(" - dwFlags: %d\n", eff->dwFlags); 00182 TRACE(" "); 00183 _dump_DIEFFECT_flags(eff->dwFlags); 00184 TRACE(" - dwDuration: %d\n", eff->dwDuration); 00185 TRACE(" - dwGain: %d\n", eff->dwGain); 00186 if (eff->dwGain > 10000) 00187 WARN("dwGain is out of range (>10,000)\n"); 00188 TRACE(" - dwTriggerButton: %d\n", eff->dwTriggerButton); 00189 TRACE(" - dwTriggerRepeatInterval: %d\n", eff->dwTriggerRepeatInterval); 00190 TRACE(" - cAxes: %d\n", eff->cAxes); 00191 TRACE(" - rgdwAxes: %p\n", eff->rgdwAxes); 00192 if (TRACE_ON(dinput) && eff->rgdwAxes) { 00193 TRACE(" "); 00194 for (i = 0; i < eff->cAxes; ++i) 00195 TRACE("%d ", eff->rgdwAxes[i]); 00196 TRACE("\n"); 00197 } 00198 TRACE(" - rglDirection: %p\n", eff->rglDirection); 00199 TRACE(" - lpEnvelope: %p\n", eff->lpEnvelope); 00200 TRACE(" - cbTypeSpecificParams: %d\n", eff->cbTypeSpecificParams); 00201 TRACE(" - lpvTypeSpecificParams: %p\n", eff->lpvTypeSpecificParams); 00202 if (eff->dwSize > sizeof(DIEFFECT_DX5)) 00203 TRACE(" - dwStartDelay: %d\n", eff->dwStartDelay); 00204 if (eff->lpEnvelope != NULL) 00205 _dump_DIENVELOPE(eff->lpEnvelope); 00206 if (type == DIEFT_CONSTANTFORCE) { 00207 if (eff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) { 00208 WARN("Effect claims to be a constant force but the type-specific params are the wrong size!\n"); 00209 } else { 00210 _dump_DICONSTANTFORCE(eff->lpvTypeSpecificParams); 00211 } 00212 } else if (type == DIEFT_PERIODIC) { 00213 if (eff->cbTypeSpecificParams != sizeof(DIPERIODIC)) { 00214 WARN("Effect claims to be a periodic force but the type-specific params are the wrong size!\n"); 00215 } else { 00216 _dump_DIPERIODIC(eff->lpvTypeSpecificParams); 00217 } 00218 } else if (type == DIEFT_RAMPFORCE) { 00219 if (eff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) { 00220 WARN("Effect claims to be a ramp force but the type-specific params are the wrong size!\n"); 00221 } else { 00222 _dump_DIRAMPFORCE(eff->lpvTypeSpecificParams); 00223 } 00224 } else if (type == DIEFT_CONDITION) { 00225 if (eff->cbTypeSpecificParams != sizeof(DICONDITION)) { 00226 WARN("Effect claims to be a condition but the type-specific params are the wrong size!\n"); 00227 } else { 00228 _dump_DICONDITION(eff->lpvTypeSpecificParams); 00229 } 00230 } else if (type == DIEFT_CUSTOMFORCE) { 00231 if (eff->cbTypeSpecificParams != sizeof(DICUSTOMFORCE)) { 00232 WARN("Effect claims to be a custom force but the type-specific params are the wrong size!\n"); 00233 } else { 00234 _dump_DICUSTOMFORCE(eff->lpvTypeSpecificParams); 00235 } 00236 } 00237 } 00238 00239 00240 /****************************************************************************** 00241 * LinuxInputEffectImpl 00242 */ 00243 00244 static ULONG WINAPI LinuxInputEffectImpl_AddRef( 00245 LPDIRECTINPUTEFFECT iface) 00246 { 00247 LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface; 00248 return InterlockedIncrement(&(This->ref)); 00249 } 00250 00251 static HRESULT WINAPI LinuxInputEffectImpl_Download( 00252 LPDIRECTINPUTEFFECT iface) 00253 { 00254 LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface; 00255 00256 TRACE("(this=%p)\n", This); 00257 00258 if (ioctl(*(This->fd), EVIOCSFF, &This->effect) == -1) { 00259 if (errno == ENOMEM) { 00260 return DIERR_DEVICEFULL; 00261 } else { 00262 FIXME("Could not upload effect. Assuming a disconnected device %d \"%s\".\n", *This->fd, strerror(errno)); 00263 return DIERR_INPUTLOST; 00264 } 00265 } 00266 00267 return DI_OK; 00268 } 00269 00270 static HRESULT WINAPI LinuxInputEffectImpl_Escape( 00271 LPDIRECTINPUTEFFECT iface, 00272 LPDIEFFESCAPE pesc) 00273 { 00274 WARN("(this=%p,%p): invalid: no hardware-specific escape codes in this" 00275 " driver!\n", iface, pesc); 00276 00277 return DI_OK; 00278 } 00279 00280 static HRESULT WINAPI LinuxInputEffectImpl_GetEffectGuid( 00281 LPDIRECTINPUTEFFECT iface, 00282 LPGUID pguid) 00283 { 00284 LinuxInputEffectImpl *This = (LinuxInputEffectImpl*)iface; 00285 00286 TRACE("(this=%p,%p)\n", This, pguid); 00287 00288 pguid = &This->guid; 00289 00290 return DI_OK; 00291 } 00292 00293 static HRESULT WINAPI LinuxInputEffectImpl_GetEffectStatus( 00294 LPDIRECTINPUTEFFECT iface, 00295 LPDWORD pdwFlags) 00296 { 00297 TRACE("(this=%p,%p)\n", iface, pdwFlags); 00298 00299 /* linux sends the effect status through an event. 00300 * that event is trapped by our parent joystick driver 00301 * and there is no clean way to pass it back to us. */ 00302 FIXME("Not enough information to provide a status.\n"); 00303 00304 (*pdwFlags) = 0; 00305 00306 return DI_OK; 00307 } 00308 00309 static HRESULT WINAPI LinuxInputEffectImpl_GetParameters( 00310 LPDIRECTINPUTEFFECT iface, 00311 LPDIEFFECT peff, 00312 DWORD dwFlags) 00313 { 00314 HRESULT diErr = DI_OK; 00315 LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface; 00316 TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags); 00317 00318 /* Major conversion factors are: 00319 * times: millisecond (linux) -> microsecond (windows) (x * 1000) 00320 * forces: scale 0x7FFF (linux) -> scale 10000 (windows) approx ((x / 33) * 10) 00321 * angles: scale 0x7FFF (linux) -> scale 35999 (windows) approx ((x / 33) * 36) 00322 * angle bases: 0 -> -y (down) (linux) -> 0 -> +x (right) (windows) 00323 */ 00324 00325 if (dwFlags & DIEP_AXES) { 00326 if (peff->cAxes < 2 /* linuxinput effects always use 2 axes, x and y */) 00327 diErr = DIERR_MOREDATA; 00328 peff->cAxes = 2; 00329 if (diErr) 00330 return diErr; 00331 else { 00332 peff->rgdwAxes[0] = DIJOFS_X; 00333 peff->rgdwAxes[1] = DIJOFS_Y; 00334 } 00335 } 00336 00337 if (dwFlags & DIEP_DIRECTION) { 00338 if (peff->cAxes < 2) 00339 diErr = DIERR_MOREDATA; 00340 peff->cAxes = 2; 00341 if (diErr) 00342 return diErr; 00343 else { 00344 if (peff->dwFlags & DIEFF_CARTESIAN) { 00345 peff->rglDirection[0] = (long)(sin(M_PI * 3 * This->effect.direction / 0x7FFF) * 1000); 00346 peff->rglDirection[1] = (long)(cos(M_PI * 3 * This->effect.direction / 0x7FFF) * 1000); 00347 } else { 00348 /* Polar and spherical coordinates are the same for two or less 00349 * axes. 00350 * Note that we also use this case if NO flags are marked. 00351 * According to MSDN, we should return the direction in the 00352 * format that it was specified in, if no flags are marked. 00353 */ 00354 peff->rglDirection[0] = (This->effect.direction / 33) * 36 + 9000; 00355 if (peff->rglDirection[0] > 35999) 00356 peff->rglDirection[0] -= 35999; 00357 } 00358 } 00359 } 00360 00361 if (dwFlags & DIEP_DURATION) { 00362 peff->dwDuration = (DWORD)This->effect.replay.length * 1000; 00363 } 00364 00365 if (dwFlags & DIEP_ENVELOPE) { 00366 struct ff_envelope* env; 00367 if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope; 00368 else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope; 00369 else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope; 00370 else env = NULL; 00371 if (env == NULL) { 00372 peff->lpEnvelope = NULL; 00373 } else if (peff->lpEnvelope == NULL) { 00374 return DIERR_INVALIDPARAM; 00375 } else { 00376 peff->lpEnvelope->dwAttackLevel = (env->attack_level / 33) * 10; 00377 peff->lpEnvelope->dwAttackTime = env->attack_length * 1000; 00378 peff->lpEnvelope->dwFadeLevel = (env->fade_level / 33) * 10; 00379 peff->lpEnvelope->dwFadeTime = env->fade_length * 1000; 00380 } 00381 } 00382 00383 if (dwFlags & DIEP_GAIN) { 00384 peff->dwGain = This->gain * 10000 / 0xFFFF; 00385 } 00386 00387 if (dwFlags & DIEP_SAMPLEPERIOD) { 00388 /* the linux input ff driver has no support for setting 00389 * the playback sample period. 0 means default. */ 00390 peff->dwSamplePeriod = 0; 00391 } 00392 00393 if (dwFlags & DIEP_STARTDELAY) { 00394 peff->dwStartDelay = This->effect.replay.delay * 1000; 00395 } 00396 00397 if (dwFlags & DIEP_TRIGGERBUTTON) { 00398 FIXME("LinuxInput button mapping needs redoing; for now, assuming we're using an actual joystick.\n"); 00399 peff->dwTriggerButton = DIJOFS_BUTTON(This->effect.trigger.button - BTN_JOYSTICK); 00400 } 00401 00402 if (dwFlags & DIEP_TRIGGERREPEATINTERVAL) { 00403 peff->dwTriggerRepeatInterval = This->effect.trigger.interval * 1000; 00404 } 00405 00406 if (dwFlags & DIEP_TYPESPECIFICPARAMS) { 00407 DWORD expectedsize = 0; 00408 if (This->effect.type == FF_PERIODIC) { 00409 expectedsize = sizeof(DIPERIODIC); 00410 } else if (This->effect.type == FF_CONSTANT) { 00411 expectedsize = sizeof(DICONSTANTFORCE); 00412 } else if (This->effect.type == FF_SPRING 00413 || This->effect.type == FF_FRICTION 00414 || This->effect.type == FF_INERTIA 00415 || This->effect.type == FF_DAMPER) { 00416 expectedsize = sizeof(DICONDITION) * 2; 00417 } else if (This->effect.type == FF_RAMP) { 00418 expectedsize = sizeof(DIRAMPFORCE); 00419 } 00420 if (expectedsize > peff->cbTypeSpecificParams) 00421 diErr = DIERR_MOREDATA; 00422 peff->cbTypeSpecificParams = expectedsize; 00423 if (diErr) 00424 return diErr; 00425 else { 00426 if (This->effect.type == FF_PERIODIC) { 00427 LPDIPERIODIC tsp = peff->lpvTypeSpecificParams; 00428 tsp->dwMagnitude = (This->effect.u.periodic.magnitude / 33) * 10; 00429 tsp->lOffset = (This->effect.u.periodic.offset / 33) * 10; 00430 tsp->dwPhase = (This->effect.u.periodic.phase / 33) * 36; 00431 tsp->dwPeriod = (This->effect.u.periodic.period * 1000); 00432 } else if (This->effect.type == FF_CONSTANT) { 00433 LPDICONSTANTFORCE tsp = peff->lpvTypeSpecificParams; 00434 tsp->lMagnitude = (This->effect.u.constant.level / 33) * 10; 00435 } else if (This->effect.type == FF_SPRING 00436 || This->effect.type == FF_FRICTION 00437 || This->effect.type == FF_INERTIA 00438 || This->effect.type == FF_DAMPER) { 00439 LPDICONDITION tsp = peff->lpvTypeSpecificParams; 00440 int i; 00441 for (i = 0; i < 2; ++i) { 00442 tsp[i].lOffset = (This->effect.u.condition[i].center / 33) * 10; 00443 tsp[i].lPositiveCoefficient = (This->effect.u.condition[i].right_coeff / 33) * 10; 00444 tsp[i].lNegativeCoefficient = (This->effect.u.condition[i].left_coeff / 33) * 10; 00445 tsp[i].dwPositiveSaturation = (This->effect.u.condition[i].right_saturation / 33) * 10; 00446 tsp[i].dwNegativeSaturation = (This->effect.u.condition[i].left_saturation / 33) * 10; 00447 tsp[i].lDeadBand = (This->effect.u.condition[i].deadband / 33) * 10; 00448 } 00449 } else if (This->effect.type == FF_RAMP) { 00450 LPDIRAMPFORCE tsp = peff->lpvTypeSpecificParams; 00451 tsp->lStart = (This->effect.u.ramp.start_level / 33) * 10; 00452 tsp->lEnd = (This->effect.u.ramp.end_level / 33) * 10; 00453 } 00454 } 00455 } 00456 00457 return diErr; 00458 } 00459 00460 static HRESULT WINAPI LinuxInputEffectImpl_Initialize( 00461 LPDIRECTINPUTEFFECT iface, 00462 HINSTANCE hinst, 00463 DWORD dwVersion, 00464 REFGUID rguid) 00465 { 00466 FIXME("(this=%p,%p,%d,%s): stub!\n", 00467 iface, hinst, dwVersion, debugstr_guid(rguid)); 00468 00469 return DI_OK; 00470 } 00471 00472 static HRESULT WINAPI LinuxInputEffectImpl_QueryInterface( 00473 LPDIRECTINPUTEFFECT iface, 00474 REFIID riid, 00475 void **ppvObject) 00476 { 00477 LinuxInputEffectImpl* This = (LinuxInputEffectImpl*)iface; 00478 00479 TRACE("(this=%p,%s,%p)\n", This, debugstr_guid(riid), ppvObject); 00480 00481 if (IsEqualGUID(&IID_IUnknown, riid) || 00482 IsEqualGUID(&IID_IDirectInputEffect, riid)) { 00483 LinuxInputEffectImpl_AddRef(iface); 00484 *ppvObject = This; 00485 return 0; 00486 } 00487 00488 TRACE("Unsupported interface!\n"); 00489 return E_FAIL; 00490 } 00491 00492 static HRESULT WINAPI LinuxInputEffectImpl_Start( 00493 LPDIRECTINPUTEFFECT iface, 00494 DWORD dwIterations, 00495 DWORD dwFlags) 00496 { 00497 struct input_event event; 00498 LinuxInputEffectImpl* This = (LinuxInputEffectImpl*)iface; 00499 00500 TRACE("(this=%p,%d,%d)\n", This, dwIterations, dwFlags); 00501 00502 if (!(dwFlags & DIES_NODOWNLOAD)) { 00503 /* Download the effect if necessary */ 00504 if (This->effect.id == -1) { 00505 HRESULT res = LinuxInputEffectImpl_Download(iface); 00506 if (res != DI_OK) 00507 return res; 00508 } 00509 } 00510 00511 if (dwFlags & DIES_SOLO) { 00512 FIXME("Solo mode requested: should be stopping all effects here!\n"); 00513 } 00514 00515 event.type = EV_FF; 00516 event.code = This->effect.id; 00517 event.value = dwIterations; 00518 if (write(*(This->fd), &event, sizeof(event)) == -1) { 00519 FIXME("Unable to write event. Assuming device disconnected.\n"); 00520 return DIERR_INPUTLOST; 00521 } 00522 00523 return DI_OK; 00524 } 00525 00526 static HRESULT WINAPI LinuxInputEffectImpl_SetParameters( 00527 LPDIRECTINPUTEFFECT iface, 00528 LPCDIEFFECT peff, 00529 DWORD dwFlags) 00530 { 00531 LinuxInputEffectImpl* This = (LinuxInputEffectImpl*)iface; 00532 DWORD type = _typeFromGUID(&This->guid); 00533 HRESULT retval = DI_OK; 00534 00535 TRACE("(this=%p,%p,%d)\n", This, peff, dwFlags); 00536 00537 _dump_DIEFFECT(peff, &This->guid); 00538 00539 if ((dwFlags & ~DIEP_NORESTART & ~DIEP_NODOWNLOAD & ~DIEP_START) == 0) { 00540 /* set everything */ 00541 dwFlags = DIEP_AXES | DIEP_DIRECTION | DIEP_DURATION | DIEP_ENVELOPE | 00542 DIEP_GAIN | DIEP_SAMPLEPERIOD | DIEP_STARTDELAY | DIEP_TRIGGERBUTTON | 00543 DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS; 00544 } 00545 00546 if (dwFlags & DIEP_AXES) { 00547 /* the linux input effect system only supports one or two axes */ 00548 if (peff->cAxes > 2) 00549 return DIERR_INVALIDPARAM; 00550 else if (peff->cAxes < 1) 00551 return DIERR_INCOMPLETEEFFECT; 00552 This->first_axis_is_x = peff->rgdwAxes[0] == DIJOFS_X; 00553 } 00554 00555 /* some of this may look funky, but it's 'cause the linux driver and directx have 00556 * different opinions about which way direction "0" is. directx has 0 along the x 00557 * axis (left), linux has it along the y axis (down). */ 00558 if (dwFlags & DIEP_DIRECTION) { 00559 if (peff->cAxes == 1) { 00560 if (peff->dwFlags & DIEFF_CARTESIAN) { 00561 if (dwFlags & DIEP_AXES) { 00562 if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] >= 0) 00563 This->effect.direction = 0x4000; 00564 else if (peff->rgdwAxes[0] == DIJOFS_X && peff->rglDirection[0] < 0) 00565 This->effect.direction = 0xC000; 00566 else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] >= 0) 00567 This->effect.direction = 0; 00568 else if (peff->rgdwAxes[0] == DIJOFS_Y && peff->rglDirection[0] < 0) 00569 This->effect.direction = 0x8000; 00570 } 00571 } else { 00572 /* one-axis effects must use cartesian coords */ 00573 return DIERR_INVALIDPARAM; 00574 } 00575 } else { /* two axes */ 00576 if (peff->dwFlags & DIEFF_CARTESIAN) { 00577 LONG x, y; 00578 if (This->first_axis_is_x) { 00579 x = peff->rglDirection[0]; 00580 y = peff->rglDirection[1]; 00581 } else { 00582 x = peff->rglDirection[1]; 00583 y = peff->rglDirection[0]; 00584 } 00585 This->effect.direction = (int)((3 * M_PI / 2 - atan2(y, x)) * -0x7FFF / M_PI); 00586 } else { 00587 /* Polar and spherical are the same for 2 axes */ 00588 /* Precision is important here, so we do double math with exact constants */ 00589 This->effect.direction = (int)(((double)peff->rglDirection[0] - 90) / 35999) * 0x7FFF; 00590 } 00591 } 00592 } 00593 00594 if (dwFlags & DIEP_DURATION) 00595 This->effect.replay.length = peff->dwDuration / 1000; 00596 00597 if (dwFlags & DIEP_ENVELOPE) { 00598 struct ff_envelope* env; 00599 if (This->effect.type == FF_CONSTANT) env = &This->effect.u.constant.envelope; 00600 else if (This->effect.type == FF_PERIODIC) env = &This->effect.u.periodic.envelope; 00601 else if (This->effect.type == FF_RAMP) env = &This->effect.u.ramp.envelope; 00602 else env = NULL; 00603 00604 if (peff->lpEnvelope == NULL) { 00605 /* if this type had an envelope, reset it 00606 * note that length can never be zero, so we set it to something minuscule */ 00607 if (env) { 00608 env->attack_length = 0x10; 00609 env->attack_level = 0x7FFF; 00610 env->fade_length = 0x10; 00611 env->fade_level = 0x7FFF; 00612 } 00613 } else { 00614 /* did we get passed an envelope for a type that doesn't even have one? */ 00615 if (!env) return DIERR_INVALIDPARAM; 00616 /* copy the envelope */ 00617 env->attack_length = peff->lpEnvelope->dwAttackTime / 1000; 00618 env->attack_level = (peff->lpEnvelope->dwAttackLevel / 10) * 32; 00619 env->fade_length = peff->lpEnvelope->dwFadeTime / 1000; 00620 env->fade_level = (peff->lpEnvelope->dwFadeLevel / 10) * 32; 00621 } 00622 } 00623 00624 /* Gain and Sample Period settings are not supported by the linux 00625 * event system */ 00626 if (dwFlags & DIEP_GAIN) { 00627 This->gain = 0xFFFF * peff->dwGain / 10000; 00628 TRACE("Effect gain requested but no effect gain functionality present.\n"); 00629 } 00630 00631 if (dwFlags & DIEP_SAMPLEPERIOD) 00632 TRACE("Sample period requested but no sample period functionality present.\n"); 00633 00634 if (dwFlags & DIEP_STARTDELAY) 00635 This->effect.replay.delay = peff->dwStartDelay / 1000; 00636 00637 if (dwFlags & DIEP_TRIGGERBUTTON) { 00638 if (peff->dwTriggerButton != -1) { 00639 FIXME("Linuxinput button mapping needs redoing, assuming we're using a joystick.\n"); 00640 FIXME("Trigger button translation not yet implemented!\n"); 00641 } 00642 This->effect.trigger.button = 0; 00643 } 00644 00645 if (dwFlags & DIEP_TRIGGERREPEATINTERVAL) 00646 This->effect.trigger.interval = peff->dwTriggerRepeatInterval / 1000; 00647 00648 if (dwFlags & DIEP_TYPESPECIFICPARAMS) { 00649 if (!(peff->lpvTypeSpecificParams)) 00650 return DIERR_INCOMPLETEEFFECT; 00651 if (type == DIEFT_PERIODIC) { 00652 LPCDIPERIODIC tsp; 00653 if (peff->cbTypeSpecificParams != sizeof(DIPERIODIC)) 00654 return DIERR_INVALIDPARAM; 00655 tsp = peff->lpvTypeSpecificParams; 00656 This->effect.u.periodic.magnitude = (tsp->dwMagnitude / 10) * 32; 00657 This->effect.u.periodic.offset = (tsp->lOffset / 10) * 32; 00658 This->effect.u.periodic.phase = (tsp->dwPhase / 9) * 8; /* == (/ 36 * 32) */ 00659 This->effect.u.periodic.period = tsp->dwPeriod / 1000; 00660 } else if (type == DIEFT_CONSTANTFORCE) { 00661 LPCDICONSTANTFORCE tsp; 00662 if (peff->cbTypeSpecificParams != sizeof(DICONSTANTFORCE)) 00663 return DIERR_INVALIDPARAM; 00664 tsp = peff->lpvTypeSpecificParams; 00665 This->effect.u.constant.level = (max(min(tsp->lMagnitude, 10000), -10000) / 10) * 32; 00666 } else if (type == DIEFT_RAMPFORCE) { 00667 LPCDIRAMPFORCE tsp; 00668 if (peff->cbTypeSpecificParams != sizeof(DIRAMPFORCE)) 00669 return DIERR_INVALIDPARAM; 00670 tsp = peff->lpvTypeSpecificParams; 00671 This->effect.u.ramp.start_level = (tsp->lStart / 10) * 32; 00672 This->effect.u.ramp.end_level = (tsp->lStart / 10) * 32; 00673 } else if (type == DIEFT_CONDITION) { 00674 LPCDICONDITION tsp = peff->lpvTypeSpecificParams; 00675 if (peff->cbTypeSpecificParams == sizeof(DICONDITION)) { 00676 /* One condition block. This needs to be rotated to direction, 00677 * and expanded to separate x and y conditions. */ 00678 int i; 00679 double factor[2]; 00680 factor[0] = asin((This->effect.direction * 3.0 * M_PI) / 0x7FFF); 00681 factor[1] = acos((This->effect.direction * 3.0 * M_PI) / 0x7FFF); 00682 for (i = 0; i < 2; ++i) { 00683 This->effect.u.condition[i].center = (int)(factor[i] * (tsp->lOffset / 10) * 32); 00684 This->effect.u.condition[i].right_coeff = (int)(factor[i] * (tsp->lPositiveCoefficient / 10) * 32); 00685 This->effect.u.condition[i].left_coeff = (int)(factor[i] * (tsp->lNegativeCoefficient / 10) * 32); 00686 This->effect.u.condition[i].right_saturation = (int)(factor[i] * (tsp->dwPositiveSaturation / 10) * 32); 00687 This->effect.u.condition[i].left_saturation = (int)(factor[i] * (tsp->dwNegativeSaturation / 10) * 32); 00688 This->effect.u.condition[i].deadband = (int)(factor[i] * (tsp->lDeadBand / 10) * 32); 00689 } 00690 } else if (peff->cbTypeSpecificParams == 2 * sizeof(DICONDITION)) { 00691 /* Two condition blocks. Direct parameter copy. */ 00692 int i; 00693 for (i = 0; i < 2; ++i) { 00694 This->effect.u.condition[i].center = (tsp[i].lOffset / 10) * 32; 00695 This->effect.u.condition[i].right_coeff = (tsp[i].lPositiveCoefficient / 10) * 32; 00696 This->effect.u.condition[i].left_coeff = (tsp[i].lNegativeCoefficient / 10) * 32; 00697 This->effect.u.condition[i].right_saturation = (tsp[i].dwPositiveSaturation / 10) * 32; 00698 This->effect.u.condition[i].left_saturation = (tsp[i].dwNegativeSaturation / 10) * 32; 00699 This->effect.u.condition[i].deadband = (tsp[i].lDeadBand / 10) * 32; 00700 } 00701 } else { 00702 return DIERR_INVALIDPARAM; 00703 } 00704 } else { 00705 FIXME("Custom force types are not supported\n"); 00706 return DIERR_INVALIDPARAM; 00707 } 00708 } 00709 00710 if (!(dwFlags & DIEP_NODOWNLOAD)) 00711 retval = LinuxInputEffectImpl_Download(iface); 00712 if (retval != DI_OK) 00713 return DI_DOWNLOADSKIPPED; 00714 00715 if (dwFlags & DIEP_NORESTART) 00716 TRACE("DIEP_NORESTART: not handled (we have no control of that).\n"); 00717 00718 if (dwFlags & DIEP_START) 00719 retval = LinuxInputEffectImpl_Start(iface, 1, 0); 00720 if (retval != DI_OK) 00721 return retval; 00722 00723 return DI_OK; 00724 } 00725 00726 static HRESULT WINAPI LinuxInputEffectImpl_Stop( 00727 LPDIRECTINPUTEFFECT iface) 00728 { 00729 struct input_event event; 00730 LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface; 00731 00732 TRACE("(this=%p)\n", This); 00733 00734 event.type = EV_FF; 00735 event.code = This->effect.id; 00736 event.value = 0; 00737 /* we don't care about the success or failure of this call */ 00738 write(*(This->fd), &event, sizeof(event)); 00739 00740 return DI_OK; 00741 } 00742 00743 static HRESULT WINAPI LinuxInputEffectImpl_Unload( 00744 LPDIRECTINPUTEFFECT iface) 00745 { 00746 LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface; 00747 TRACE("(this=%p)\n", This); 00748 00749 /* Erase the downloaded effect */ 00750 if (ioctl(*(This->fd), EVIOCRMFF, This->effect.id) == -1) 00751 return DIERR_INVALIDPARAM; 00752 00753 /* Mark the effect as deallocated */ 00754 This->effect.id = -1; 00755 00756 return DI_OK; 00757 } 00758 00759 static ULONG WINAPI LinuxInputEffectImpl_Release(LPDIRECTINPUTEFFECT iface) 00760 { 00761 LinuxInputEffectImpl *This = (LinuxInputEffectImpl *)iface; 00762 ULONG ref = InterlockedDecrement(&(This->ref)); 00763 00764 if (ref == 0) 00765 { 00766 LinuxInputEffectImpl_Stop(iface); 00767 LinuxInputEffectImpl_Unload(iface); 00768 list_remove(This->entry); 00769 HeapFree(GetProcessHeap(), 0, LIST_ENTRY(This->entry, effect_list_item, entry)); 00770 HeapFree(GetProcessHeap(), 0, This); 00771 } 00772 return ref; 00773 } 00774 00775 /****************************************************************************** 00776 * LinuxInputEffect 00777 */ 00778 00779 HRESULT linuxinput_create_effect( 00780 int* fd, 00781 REFGUID rguid, 00782 struct list *parent_list_entry, 00783 LPDIRECTINPUTEFFECT* peff) 00784 { 00785 LinuxInputEffectImpl* newEffect = HeapAlloc(GetProcessHeap(), 00786 HEAP_ZERO_MEMORY, sizeof(LinuxInputEffectImpl)); 00787 DWORD type = _typeFromGUID(rguid); 00788 00789 newEffect->lpVtbl = &LinuxInputEffectVtbl; 00790 newEffect->ref = 1; 00791 newEffect->guid = *rguid; 00792 newEffect->fd = fd; 00793 newEffect->gain = 0xFFFF; 00794 00795 /* set the type. this cannot be changed over the effect's life. */ 00796 switch (type) { 00797 case DIEFT_PERIODIC: 00798 newEffect->effect.type = FF_PERIODIC; 00799 if (IsEqualGUID(rguid, &GUID_Sine)) { 00800 newEffect->effect.u.periodic.waveform = FF_SINE; 00801 } else if (IsEqualGUID(rguid, &GUID_Triangle)) { 00802 newEffect->effect.u.periodic.waveform = FF_TRIANGLE; 00803 } else if (IsEqualGUID(rguid, &GUID_Square)) { 00804 newEffect->effect.u.periodic.waveform = FF_SQUARE; 00805 } else if (IsEqualGUID(rguid, &GUID_SawtoothUp)) { 00806 newEffect->effect.u.periodic.waveform = FF_SAW_UP; 00807 } else if (IsEqualGUID(rguid, &GUID_SawtoothDown)) { 00808 newEffect->effect.u.periodic.waveform = FF_SAW_DOWN; 00809 } 00810 break; 00811 case DIEFT_CONSTANTFORCE: 00812 newEffect->effect.type = FF_CONSTANT; 00813 break; 00814 case DIEFT_RAMPFORCE: 00815 newEffect->effect.type = FF_RAMP; 00816 break; 00817 case DIEFT_CONDITION: 00818 if (IsEqualGUID(rguid, &GUID_Spring)) { 00819 newEffect->effect.type = FF_SPRING; 00820 } else if (IsEqualGUID(rguid, &GUID_Friction)) { 00821 newEffect->effect.type = FF_FRICTION; 00822 } else if (IsEqualGUID(rguid, &GUID_Inertia)) { 00823 newEffect->effect.type = FF_INERTIA; 00824 } else if (IsEqualGUID(rguid, &GUID_Damper)) { 00825 newEffect->effect.type = FF_DAMPER; 00826 } 00827 break; 00828 case DIEFT_CUSTOMFORCE: 00829 FIXME("Custom forces are not supported.\n"); 00830 HeapFree(GetProcessHeap(), 0, newEffect); 00831 return DIERR_INVALIDPARAM; 00832 default: 00833 FIXME("Unknown force type 0x%x.\n", type); 00834 HeapFree(GetProcessHeap(), 0, newEffect); 00835 return DIERR_INVALIDPARAM; 00836 } 00837 00838 /* mark as non-uploaded */ 00839 newEffect->effect.id = -1; 00840 00841 newEffect->entry = parent_list_entry; 00842 00843 *peff = (LPDIRECTINPUTEFFECT)newEffect; 00844 00845 TRACE("Creating linux input system effect (%p) with guid %s\n", 00846 *peff, _dump_dinput_GUID(rguid)); 00847 00848 return DI_OK; 00849 } 00850 00851 HRESULT linuxinput_get_info_A( 00852 int fd, 00853 REFGUID rguid, 00854 LPDIEFFECTINFOA info) 00855 { 00856 DWORD type = _typeFromGUID(rguid); 00857 00858 TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type); 00859 00860 if (!info) return E_POINTER; 00861 00862 if (info->dwSize != sizeof(DIEFFECTINFOA)) return DIERR_INVALIDPARAM; 00863 00864 info->guid = *rguid; 00865 00866 info->dwEffType = type; 00867 /* the event device API does not support querying for all these things 00868 * therefore we assume that we have support for them 00869 * that's not as dangerous as it sounds, since drivers are allowed to 00870 * ignore parameters they claim to support anyway */ 00871 info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE 00872 | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION 00873 | DIEFT_SATURATION | DIEFT_STARTDELAY; 00874 00875 /* again, assume we have support for everything */ 00876 info->dwStaticParams = DIEP_ALLPARAMS; 00877 info->dwDynamicParams = info->dwStaticParams; 00878 00879 /* yes, this is windows behavior (print the GUID_Name for name) */ 00880 strcpy(info->tszName, _dump_dinput_GUID(rguid)); 00881 00882 return DI_OK; 00883 } 00884 00885 HRESULT linuxinput_get_info_W( 00886 int fd, 00887 REFGUID rguid, 00888 LPDIEFFECTINFOW info) 00889 { 00890 DWORD type = _typeFromGUID(rguid); 00891 00892 TRACE("(%d, %s, %p) type=%d\n", fd, _dump_dinput_GUID(rguid), info, type); 00893 00894 if (!info) return E_POINTER; 00895 00896 if (info->dwSize != sizeof(DIEFFECTINFOW)) return DIERR_INVALIDPARAM; 00897 00898 info->guid = *rguid; 00899 00900 info->dwEffType = type; 00901 /* the event device API does not support querying for all these things 00902 * therefore we assume that we have support for them 00903 * that's not as dangerous as it sounds, since drivers are allowed to 00904 * ignore parameters they claim to support anyway */ 00905 info->dwEffType |= DIEFT_DEADBAND | DIEFT_FFATTACK | DIEFT_FFFADE 00906 | DIEFT_POSNEGCOEFFICIENTS | DIEFT_POSNEGSATURATION 00907 | DIEFT_SATURATION | DIEFT_STARTDELAY; 00908 00909 /* again, assume we have support for everything */ 00910 info->dwStaticParams = DIEP_ALLPARAMS; 00911 info->dwDynamicParams = info->dwStaticParams; 00912 00913 /* yes, this is windows behavior (print the GUID_Name for name) */ 00914 MultiByteToWideChar(CP_ACP, 0, _dump_dinput_GUID(rguid), -1, 00915 info->tszName, MAX_PATH); 00916 00917 return DI_OK; 00918 } 00919 00920 static const IDirectInputEffectVtbl LinuxInputEffectVtbl = { 00921 LinuxInputEffectImpl_QueryInterface, 00922 LinuxInputEffectImpl_AddRef, 00923 LinuxInputEffectImpl_Release, 00924 LinuxInputEffectImpl_Initialize, 00925 LinuxInputEffectImpl_GetEffectGuid, 00926 LinuxInputEffectImpl_GetParameters, 00927 LinuxInputEffectImpl_SetParameters, 00928 LinuxInputEffectImpl_Start, 00929 LinuxInputEffectImpl_Stop, 00930 LinuxInputEffectImpl_GetEffectStatus, 00931 LinuxInputEffectImpl_Download, 00932 LinuxInputEffectImpl_Unload, 00933 LinuxInputEffectImpl_Escape 00934 }; 00935 00936 #endif /* HAVE_STRUCT_FF_EFFECT_DIRECTION */ Generated on Sat May 26 2012 04:20:02 for ReactOS by
1.7.6.1
|