ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

effect_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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.