|
|
/*****************************************************************************
* * DIDevEf.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * The part of IDirectInputDevice that worries about * IDirectInputEffect. * *****************************************************************************/
#include "dinputpr.h"
#include "didev.h"
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | CreateEffectDriver | * * If we don't already have one, create the effect * driver shepherd * so we can do force feedback goo. If we already * have one, then there's nothing to do. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device does * not support force feeback, or there was an error loading * the force feedback driver. * *****************************************************************************/
HRESULT INTERNAL CDIDev_CreateEffectDriver(PDD this) { HRESULT hres;
CDIDev_EnterCrit(this);
if (this->pes) { hres = S_OK; } else { hres = this->pdcb->lpVtbl->CreateEffect(this->pdcb, &this->pes);
/*
* If we have acquisition, then do a force feedback * acquire to get everything back in sync. */ if (SUCCEEDED(hres) && this->fAcquired) { CDIDev_FFAcquire(this); hres = S_OK; } }
CDIDev_LeaveCrit(this);
return hres; }
/*****************************************************************************
* * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice8 | CreateEffect | * * Creates and initializes an instance of an effect * identified by the effect GUID. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN REFGUID | rguid | * * The identity of the effect to be created. This can be * a predefined effect GUID, or it can be a GUID obtained * from <mf IDirectInputDevice8::EnumEffects>. * * @parm IN LPCDIEFFECT | lpeff | * * Pointer to a <t DIEFFECT> structure which provides * parameters for the created effect. This parameter * is optional. If it is <c NULL>, then the effect object * is created without parameters. The application must * call <mf IDirectInputEffect::SetParameters> to set * the parameters of the effect before it can download * the effect. * * @parm OUT LPDIRECTINPUTEFFECT * | ppdeff | * * Points to where to return * the pointer to the <i IDirectInputEffect> interface, if successful. * * @parm IN LPUNKNOWN | punkOuter | * * Pointer to controlling unknown * for OLE aggregation, or 0 if the interface is not aggregated. * Most callers will pass 0. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The object was created and initialized * successfully. * * <c DI_TRUNCATED>: The effect was successfully created, * but some of the effect parameters were * beyond the capabilities of the device and were truncated * to the nearest valid value. * Note that this is a success code, because the effect was * successfully created. * * <c DIERR_DEVICENOTREG>: The effect GUID is not supported * by the device. * * <c DIERR_DEVICEFULL>: The device is full. * * <c DIERR_INVALIDPARAM>: At least one of the parameters * was invalid. * * * @devnote * * Future versions of DirectX will allow <p lpeff> to be NULL, * indicating that the effect should not be initialized. * This is important to support effects which are created * but which aren't downloaded until the app explicitly * requests it. * *****************************************************************************/
/*
* Helper function which decides how many parameters to set, * based on the incoming DIEFFECT structure. */ DWORD INLINE CDIDev_DiepFromPeff(LPCDIEFFECT peff) { /*
* If we received a DIEFFECT_DX5, then we need to * pass DIEP_ALLPARAMS_DX5 instead of DIEP_ALLPARAMS. */ return peff->dwSize < cbX(DIEFFECT_DX6) ? DIEP_ALLPARAMS_DX5 : DIEP_ALLPARAMS; }
STDMETHODIMP CDIDev_CreateEffect(PV pdd, REFGUID rguid, LPCDIEFFECT peff, LPDIRECTINPUTEFFECT *ppdeff, LPUNKNOWN punkOuter _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::CreateEffect, (_ "pGpp", pdd, rguid, peff, punkOuter));
/*
* CDIEff_New will validate the ppdeff and punkOuter, but * we need to validate ppdeff because we're going to * shove a zero into it. * * We also need to check peff->dwSize as we need to test it * before calling IDirectInputEffect_SetParameters. * * CDIEff_Initialize will validate the rguid. * * CDIEff_SetParameters will validate the peff. */ if (SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = (peff && IsBadReadPtr(&peff->dwSize, cbX(peff->dwSize))) ? E_POINTER : S_OK) && SUCCEEDED(hres = hresFullValidPcbOut(ppdeff, cbX(*ppdeff), 4))) {
PDD this = _thisPv(pdd);
hres = CDIDev_CreateEffectDriver(this);
*ppdeff = 0; /* If CDIDev_CreateEffectDriver fails */
if (SUCCEEDED(hres)) {
hres = CDIEff_New(this, this->pes, punkOuter, &IID_IDirectInputEffect, (PPV)ppdeff);
/*
* We assume that IDirectInputEffect is the primary interface. */ AssertF(fLimpFF(SUCCEEDED(hres), (PV)*ppdeff == _thisPv(*ppdeff)));
if (SUCCEEDED(hres) && punkOuter == 0) { LPDIRECTINPUTEFFECT pdeff = *ppdeff;
hres = IDirectInputEffect_Initialize(pdeff, g_hinst, this->dwVersion, rguid); if (SUCCEEDED(hres)) { if (fLimpFF(peff, SUCCEEDED(hres = IDirectInputEffect_SetParameters( pdeff, peff, CDIDev_DiepFromPeff(peff))))) { /*
* Woo-hoo, all is well. */ hres = S_OK; } else { Invoke_Release(ppdeff); } } else { /*
* Error initializing. */ Invoke_Release(ppdeff); } } else { /*
* Error creating, or object is aggregated and therefore * should not be initialized. */ } } else { /*
* Error creating effect driver, or no effect driver. */ } }
ExitOleProcPpv(ppdeff); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | SyncShepHandle | * * Synchronize the caller's <t SHEPHANDLE> with the * <t SHEPHANDLE> of the parent. This lets * dieshep.c know that the two are talking about the same thing. * * @cwrap PDD | this * * @returns * Returns <c S_OK> if the tags already matched, * or <c S_FALSE> if the tag changed. If the tag changed, * then the handle inside the <t SHEPHANDLE> is zero'd. * * Note that <f CDIEff_DownloadWorker> assumes that the * return value is exactly <c S_OK> or <c S_FALSE>. * *****************************************************************************/
STDMETHODIMP CDIDev_SyncShepHandle(PDD this, PSHEPHANDLE psh) { HRESULT hres;
AssertF(CDIDev_InCrit(this));
if (psh->dwTag == this->sh.dwTag) { hres = S_OK; } else { psh->dwTag = this->sh.dwTag; psh->dwEffect = 0; hres = S_FALSE; }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | NotifyCreateEffect | * * Add the effect pointer to the list of effects that * have been created by the device. * * The device critical section must not be owned by the * calling thread. * * @cwrap PDD | this * * @parm IN struct CDIEff * | pdeff | * * The effect pointer to add. * * @returns * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: Everything is okay. * * <c DIERR_OUTOFMEMORY> = * <c E_OUTOFMEMORY>: No memory to record the effect. * *****************************************************************************/
HRESULT EXTERNAL CDIDev_NotifyCreateEffect(PDD this, struct CDIEff *pdeff) { HRESULT hres;
AssertF(!CDIDev_InCrit(this));
CDIDev_EnterCrit(this); hres = GPA_Append(&this->gpaEff, pdeff);
CDIDev_LeaveCrit(this);
/*
* Note that we must leave the device critical section * before talking to the effect, in order to preserve * the synchronization hierarchy. */ if (SUCCEEDED(hres)) { Common_Hold(pdeff); hres = S_OK; }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | NotifyDestroyEffect | * * Remove the effect pointer from the list of effects that * have been created by the device. * * The device critical section must not be owned by the * calling thread. * * @cwrap PDD | this * * @parm IN struct CDIEff * | pdeff | * * The effect pointer to remove. * * @returns * * Returns a COM error code on failure. * * On success, returns the number of items left in the GPA. * *****************************************************************************/
HRESULT EXTERNAL CDIDev_NotifyDestroyEffect(PDD this, struct CDIEff *pdeff) { HRESULT hres;
AssertF(!CDIDev_InCrit(this));
CDIDev_EnterCrit(this); hres = GPA_DeletePtr(&this->gpaEff, pdeff);
CDIDev_LeaveCrit(this);
/*
* Note that we must leave the device critical section * before talking to the effect, in order to preserve * the synchronization hierarchy. * * Note that there you might think there's a deadlock here if * effect A notifies us, and we in turn try to unhold * effect B. But that won't happen, because we only * unhold the effect that notified us. */ if (SUCCEEDED(hres)) { Common_Unhold(pdeff); hres = S_OK; }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | FindEffectGUID | * * Look for an effect <t GUID>; if found, fetch associated * information. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm REFGUID | rguid | * * Effect <t GUID> to locate. * * @parm PEFFECTMAPINFO | pemi | * * Receives associated information for the effect. * We must return a copy instead of a pointer, because * the original might disappear suddenly if the * device gets <mf CDIDev::Reset>(). * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>: Out of memory. * * <c DIERR_DEVICENOTREG> = <c REGDB_E_CLASSNOTREG>: * The effect is not supported by the device. * *****************************************************************************/
STDMETHODIMP CDIDev_FindEffectGUID_(PDD this, REFGUID rguid, PEFFECTMAPINFO pemi, LPCSTR s_szProc, int iarg) { UINT iemi; HRESULT hres;
D(iarg);
CDIDev_EnterCrit(this);
for (iemi = 0; iemi < this->cemi; iemi++) { if (IsEqualGUID(rguid, &this->rgemi[iemi].guid)) { *pemi = this->rgemi[iemi]; hres = S_OK; goto found; } }
RPF("%s: Effect not supported by device", s_szProc); hres = DIERR_DEVICENOTREG; /* Effect not found */
found:;
CDIDev_LeaveCrit(this);
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CDIDev | GetEffectInfoHelper | * * Transfer information from an * <t EFFECTMAPINFO> to a <t DIEFFECTINFOW>. * * @cwrap PDD | this * * @parm LPDIEFFECTINFOW | pdeiW | * * Destination. * * @parm PCEFFECTMAPINFO | pemi | * * Source. * *****************************************************************************/
void INTERNAL CDIDev_GetEffectInfoHelper(PDD this, LPDIEFFECTINFOW pdeiW, PCEFFECTMAPINFO pemi) { AssertF(pdeiW->dwSize == cbX(*pdeiW));
pdeiW->guid = pemi->guid; pdeiW->dwEffType = pemi->attr.dwEffType; pdeiW->dwStaticParams = pemi->attr.dwStaticParams; pdeiW->dwDynamicParams = pemi->attr.dwDynamicParams; CAssertF(cbX(pdeiW->tszName) == cbX(pemi->wszName)); CopyMemory(pdeiW->tszName, pemi->wszName, cbX(pemi->wszName)); }
/*****************************************************************************
* * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice8 | EnumEffects | * * Enumerates all of the effects supported by the force * feedback system on the device. The enumerated GUIDs * may represent predefined effects as well as effects * peculiar to the device manufacturer. * * An application * can use the <e DIEFFECTINFO.dwEffType> field of the * <t DIEFFECTINFO> structure to obtain general * information about the effect, such as its type and * which envelope and condition parameters are supported * by the effect. * * In order to exploit an effect to its fullest, * you must contact the device manufacturer to obtain * information on the semantics of the effect and its * effect-specific parameters. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm LPDIENUMEFFECTSCALLBACK | lpCallback | * * Points to an application-defined callback function. * For more information, see the description of the * <f DIEnumEffectsProc> callback function. * * @parm LPVOID | pvRef | * * Specifies a 32-bit application-defined * value to be passed to the callback function. This value * may be any 32-bit value; it is prototyped as an <t LPVOID> * for convenience. * * @parm DWORD | dwEffType | * * Effect type filter. If <c DIEFT_ALL>, then all * effect types are * enumerated. Otherwise, it is a <c DIEFT_*> value, * indicating the device type that should be enumerated. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * Note that if the callback stops the enumeration prematurely, * the enumeration is considered to have succeeded. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The * <p fl> parameter contains invalid flags, or the callback * procedure returned an invalid status code. * * @cb BOOL CALLBACK | DIEnumEffectsProc | * * An application-defined callback function that receives * device effects as a result of a call to the * <om IDirectInputDevice8::EnumEffects> method. * * @parm IN LPCDIEFFECTINFO | pdei | * * A <t DIEFFECTINFO> structure that describes the enumerated * effect. * * @parm IN OUT LPVOID | pvRef | * * Specifies the application-defined value given in the * <mf IDirectInputDevice8::EnumEffects> function. * * @returns * * Returns <c DIENUM_CONTINUE> to continue the enumeration * or <c DIENUM_STOP> to stop the enumeration. * *//**************************************************************************
* * In DEBUG/RDEBUG, if the callback returns a bogus value, raise * a validation exception. * *****************************************************************************/
STDMETHODIMP CDIDev_EnumEffectsW (PV pddW, LPDIENUMEFFECTSCALLBACKW pecW, PV pvRef, DWORD dwEffType) { HRESULT hres; EnterProcR(IDirectInputDevice8W::EnumEffects, (_ "pppx", pddW, pecW, pvRef, dwEffType));
if (SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidPfn(pecW, 1)) && SUCCEEDED(hres = hresFullValidFl(dwEffType, DIEFT_ENUMVALID, 3))) {
PDD this = _thisPvNm(pddW, ddW); PEFFECTMAPINFO rgemi; UINT iemi, cemi;
/*
* We need to make a private copy of the GUID list, * because somebody else might suddenly Reset() the * device and mess up everything. * * Indeed, it might've been Reset() during this comment! * That's why we need to create the private copy * while under the critical section. */
CDIDev_EnterCrit(this);
cemi = this->cemi; hres = AllocCbPpv(cbCxX(this->cemi, EFFECTMAPINFO), &rgemi);
if (SUCCEEDED(hres)) { if (this->cemi) { CopyMemory(rgemi, this->rgemi, cbCxX(this->cemi, EFFECTMAPINFO)); } }
CDIDev_LeaveCrit(this);
if (SUCCEEDED(hres)) { for (iemi = 0; iemi < cemi; iemi++) { PEFFECTMAPINFO pemi = &rgemi[iemi]; if (fLimpFF(dwEffType, dwEffType == LOBYTE(pemi->attr.dwEffType))) { BOOL fRc; DIEFFECTINFOW deiW;
deiW.dwSize = cbX(deiW); CDIDev_GetEffectInfoHelper(this, &deiW, pemi);
fRc = Callback(pecW, &deiW, pvRef);
switch (fRc) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: RPF("%s: Invalid return value from callback", s_szProc); ValidationException(); break; } } }
enumdoneok:;
FreePpv(&rgemi); hres = S_OK;
}
}
ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | IDirectInputDevice8A | EnumEffectsCallbackA | * * Custom callback that wraps * <mf IDirectInputDevice8::EnumObjects> which * translates the UNICODE string to an ANSI string. * * @parm IN LPCDIEFFECTINFOA | pdoiA | * * Structure to be translated to ANSI. * * @parm IN LPVOID | pvRef | * * Pointer to <t struct ENUMEFFECTSINFO> which describes * the original callback. * * @returns * * Returns whatever the original callback returned. * *****************************************************************************/
typedef struct ENUMEFFECTSINFO { LPDIENUMEFFECTSCALLBACKA pecA; PV pvRef; } ENUMEFFECTSINFO, *PENUMEFFECTSINFO;
BOOL CALLBACK CDIDev_EnumEffectsCallbackA(LPCDIEFFECTINFOW pdeiW, PV pvRef) { PENUMEFFECTSINFO peei = pvRef; BOOL fRc; DIEFFECTINFOA deiA; EnterProc(CDIObj_EnumObjectsCallbackA, (_ "GxWp", &pdeiW->guid, &pdeiW->dwEffType, pdeiW->tszName, pvRef));
deiA.dwSize = cbX(deiA); EffectInfoWToA(&deiA, pdeiW);
fRc = peei->pecA(&deiA, peei->pvRef);
ExitProcX(fRc); return fRc; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | IDirectInputDevice8A | EnumEffects | * * Enumerate the effects available on a device, in ANSI. * See <mf IDirectInputDevice8::EnumEffects> for more information. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN LPDIENUMEFFECTSCALLBACKA | lpCallback | * * Same as <mf IDirectInputDevice8W::EnumObjects>, except in ANSI. * * @parm IN LPVOID | pvRef | * * Same as <mf IDirectInputDevice8W::EnumObjects>. * * @parm IN DWORD | fl | * * Same as <mf IDirectInputDevice8W::EnumObjects>. * *****************************************************************************/
STDMETHODIMP CDIDev_EnumEffectsA (PV pddA, LPDIENUMEFFECTSCALLBACKA pecA, PV pvRef, DWORD dwEffType) { HRESULT hres; EnterProcR(IDirectInputDevice8A::EnumEffects, (_ "pppx", pddA, pecA, pvRef, dwEffType));
/*
* EnumEffectsW will validate the rest. */ if (SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidPfn(pecA, 1))) { ENUMEFFECTSINFO eei = { pecA, pvRef }; PDD this = _thisPvNm(pddA, ddA);
hres = CDIDev_EnumEffectsW(&this->ddW, CDIDev_EnumEffectsCallbackA, &eei, dwEffType); }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice8 | GetEffectInfo | * * Obtains information about an effect. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm OUT LPDIEFFECTINFO | pdei | * * Receives information about the effect. * The caller "must" initialize the <e DIEFFECTINFO.dwSize> * field before calling this method. * * @parm REFGUID | rguid | * * Identifies the effect for which information is being requested. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more * parameters was invalid. * * <c DIERR_DEVICENOTREG>: The effect GUID does not exist * on the current device. * *****************************************************************************/
STDMETHODIMP CDIDev_GetEffectInfoW(PV pddW, LPDIEFFECTINFOW pdeiW, REFGUID rguid) { HRESULT hres; EnterProcR(IDirectInputDevice8W::GetEffectInfo, (_ "ppG", pddW, pdeiW, rguid));
if (SUCCEEDED(hres = hresPvW(pddW)) && SUCCEEDED(hres = hresFullValidWritePxCb(pdeiW, DIEFFECTINFOW, 1))) { PDD this = _thisPvNm(pddW, ddW); EFFECTMAPINFO emi;
if (SUCCEEDED(hres = CDIDev_FindEffectGUID(this, rguid, &emi, 2))) {
CDIDev_GetEffectInfoHelper(this, pdeiW, &emi); hres = S_OK; }
if (FAILED(hres)) { ScrambleBuf(&pdeiW->guid, cbX(DIEFFECTINFOW) - FIELD_OFFSET(DIEFFECTINFOW, guid)); } }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | IDirectInputDevice8A | GetEffectInfo | * * ANSI version of same. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm OUT LPDIEFFECTINFO | pdei | * * Receives information about the effect. * The caller "must" initialize the <e DIEFFECTINFO.dwSize> * field before calling this method. * * @parm REFGUID | rguid | * * Identifies the effect for which information is being requested. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more * parameters was invalid. * * <c DIERR_DEVICENOTREG>: The effect GUID does not exist * on the current device. * *****************************************************************************/
STDMETHODIMP CDIDev_GetEffectInfoA(PV pddA, LPDIEFFECTINFOA pdeiA, REFGUID rguid) { HRESULT hres; EnterProcR(IDirectInputDevice8A::GetEffectInfo, (_ "ppG", pddA, pdeiA, rguid));
if (SUCCEEDED(hres = hresPvA(pddA)) && SUCCEEDED(hres = hresFullValidWritePxCb(pdeiA, DIEFFECTINFOA, 1))) { PDD this = _thisPvNm(pddA, ddA); DIEFFECTINFOW deiW;
deiW.dwSize = cbX(deiW);
hres = CDIDev_GetEffectInfoW(&this->ddW, &deiW, rguid);
if (SUCCEEDED(hres)) { EffectInfoWToA(pdeiA, &deiW); hres = S_OK; } else { ScrambleBuf(&pdeiA->guid, cbX(DIEFFECTINFOA) - FIELD_OFFSET(DIEFFECTINFOA, guid)); } }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice8 | GetForceFeedbackState | * * Retrieves the state of the device's force feedback system. * The device must be acquired for this method to succeed. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm LPDWORD | pdwOut | * * Receives a <t DWORD> of flags which describe the current * state of the device's force feedback system. * * The value is a combination of zero or more <c DIGFFS_*> * flags. * * Note that future versions of DirectInput may define * additional flags. Applications should ignore any flags * that are not currently defined. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The * <p pdwOut> parameter is not a valid pointer. * * <c DIERR_INPUTLOST>: Acquisition has been lost. * * <c DIERR_NOTEXCLUSIVEACQUIRED>: The device is acquired, * but not exclusively, or the device is not acquired * at all. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device * does not support force feedback. * *****************************************************************************/
STDMETHODIMP CDIDev_GetForceFeedbackState(PV pdd, LPDWORD pdwOut _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::GetForceFeedbackState, (_ "p", pdd));
if (SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = hresFullValidPcbOut(pdwOut, cbX(*pdwOut), 1))) { PDD this = _thisPv(pdd);
/*
* I just know people aren't going to check the error code, * so don't let them see garbage. */ *pdwOut = 0;
hres = CDIDev_CreateEffectDriver(this);
if (SUCCEEDED(hres)) { DIDEVICESTATE ds;
CDIDev_EnterCrit(this);
/*
* Note that it isn't necessary to check * CDIDev_IsExclAcquired(), because the effect shepherd * will deny us access to a device we don't own. * * ISSUE-2001/03/29-timgill Need to handle DIERR_INPUTLOST case */
ds.dwSize = cbX(ds); /*
* Prefix raises a warning (mb:34564) that this->pes could * be NULL however CDIDev_CreateEffectDriver only succeeds * if it is not. */ hres = this->pes->lpVtbl-> GetForceFeedbackState(this->pes, &this->sh, &ds);
/*
* We put as many flags in matching places in * DISFFC_* and DIGFFS_* because I just know * app writers are going to mess it up. */ if (SUCCEEDED(hres)) {
CAssertF(DISFFC_RESET == DIGFFS_EMPTY); CAssertF(DISFFC_STOPALL == DIGFFS_STOPPED); CAssertF(DISFFC_PAUSE == DIGFFS_PAUSED); CAssertF(DISFFC_SETACTUATORSON == DIGFFS_ACTUATORSON); CAssertF(DISFFC_SETACTUATORSOFF == DIGFFS_ACTUATORSOFF);
*pdwOut = ds.dwState; hres = S_OK; }
CDIDev_LeaveCrit(this);
} ScrambleBit(pdwOut, DIGFFS_RANDOM);
}
/*
* Can't use ExitOleProcPpv here because pdwOut might have * DIFFS_RANDOM set in it, even on error. */ ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice8 | SendForceFeedbackCommand | * * Sends a command to the the device's force feedback system. * The device must be acquired for this method to succeed. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm DWORD | dwCommand | * * One of the <c DISFFC_*> values indicating * the command to send. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The * <p dwFlags> parameter is invalid. * * <c DIERR_INPUTLOST>: Acquisition has been lost. * * <c DIERR_NOTEXCLUSIVEACQUIRED>: The device is acquired, * but not exclusively, or the device is not acquired * at all. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device * does not support force feedback. * *****************************************************************************/
HRESULT INLINE CDIDev_SendForceFeedbackCommand_IsValidCommand(DWORD dwCmd) { HRESULT hres; RD(static char s_szProc[] = "IDirectInputDevice8::SendForceFeedbackCommand");
/*
* dwCmd must not be zero (therefore at least one bit set). * !(dwCmd & (dwCmd - 1)) checks that at most one bit is set. * (dwCmd & ~DISFFC_VALID) checks that no bad bits are set. */ if (dwCmd && !(dwCmd & ~DISFFC_VALID) && !(dwCmd & (dwCmd - 1))) {
hres = S_OK;
} else { RPF("ERROR %s: arg %d: invalid command", s_szProc, 1); hres = E_INVALIDARG; } return hres; }
STDMETHODIMP CDIDev_SendForceFeedbackCommand(PV pdd, DWORD dwCmd _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::SendForceFeedbackCommand, (_ "px", pdd, dwCmd));
if (SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = CDIDev_SendForceFeedbackCommand_IsValidCommand (dwCmd))) { PDD this = _thisPv(pdd);
hres = CDIDev_CreateEffectDriver(this);
if (SUCCEEDED(hres)) {
CDIDev_EnterCrit(this);
/*
* Note that it isn't necessary to check * CDIDev_IsExclAcquired(), because the effect shepherd * will deny us access to a device we don't own. * * ISSUE-2001/03/29-timgill Need to handle DIERR_INPUTLOST case */
/*
* Prefix raises a warning (mb:34564) that this->pes could * be NULL however CDIDev_CreateEffectDriver only succeeds * if it is not. */ hres = this->pes->lpVtbl-> SendForceFeedbackCommand(this->pes, &this->sh, dwCmd);
if (SUCCEEDED(hres) && (dwCmd & DISFFC_RESET)) { /*
* Re-establish the gain after a reset. */ CDIDev_RefreshGain(this); }
CDIDev_LeaveCrit(this);
} }
ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc EXTERNAL * * @method HRESULT | IDirectInputDevice8 | EnumCreatedEffectObjects | * * Enumerates all of the currently created effect objects for * this device. Effect objects created via * <mf IDirectInputDevice::CreateEffect> * are enumerated. * * Note that results will be unpredictable if you destroy * or create an effect object while an enumeration is in progress. * The sole exception is that the callback function itself * <f DIEnumCreatedEffectObjectsProc> is permitted to * <mf IDirectInputEffect::Release> the effect that it is * passed as its first parameter. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @parm IN LPDIENUMCREATEDEFFECTOBJECTSCALLBACK | lpCallback | * * Callback function. * * @parm IN LPVOID | pvRef | * * Reference data (context) for callback. * * @parm IN DWORD | fl | * * No flags are currently defined. This parameter must be 0. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * Note that if the callback stops the enumeration prematurely, * the enumeration is considered to have succeeded. * * <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The * <p fl> parameter contains invalid flags, or the callback * procedure returned an invalid status code. * * @cb BOOL CALLBACK | DIEnumCreatedEffectObjectsProc | * * An application-defined callback function that receives * IDirectInputEffect effect objects as a result of a call to the * <om IDirectInputDevice8::EnumCreatedEffects> method. * * @parm LPDIRECTINPUTEFFECT | peff | * * A pointer to an effect object that has been created. * * @parm LPVOID | pvRef | * Specifies the application-defined value given in the * <mf IDirectInputDevice8::EnumCreatedEffectObjects> function. * * @returns * * Returns <c DIENUM_CONTINUE> to continue the enumeration * or <c DIENUM_STOP> to stop the enumeration. * *****************************************************************************/
STDMETHODIMP CDIDev_EnumCreatedEffectObjects(PV pdd, LPDIENUMCREATEDEFFECTOBJECTSCALLBACK pec, LPVOID pvRef, DWORD fl _THAT)
{ HRESULT hres; EnterProcR(IDirectInputDevice8::EnumCreatedEffects, (_ "ppx", pdd, pec, fl));
if (SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = hresFullValidPfn(pec, 1)) && SUCCEEDED(hres = hresFullValidFl(fl, DIECEFL_VALID, 3))) { PDD this = _thisPv(pdd); GPA gpaEff;
CDIDev_EnterCrit(this);
/*
* We must snapshot the list to make sure we don't run * with a stale handle later. Actually, we also need * to Hold the guys as we transfer them across so they * won't vanish until we're ready. * * Note: It is important that we Hold and not AddRef, * because AddRef will talk to the controlling unknown, * which is not safe to do while holding a critical * section. */
hres = GPA_Clone(&gpaEff, &this->gpaEff);
if (SUCCEEDED(hres)) { int ipv;
for (ipv = 0; ipv < gpaEff.cpv; ipv++) { Common_Hold(gpaEff.rgpv[ipv]); } }
CDIDev_LeaveCrit(this);
if (SUCCEEDED(hres)) { int ipv;
for (ipv = 0; ipv < gpaEff.cpv; ipv++) { BOOL fRc;
fRc = Callback(pec, gpaEff.rgpv[ipv], pvRef);
switch (fRc) { case DIENUM_STOP: goto enumdoneok; case DIENUM_CONTINUE: break; default: RPF("%s: Invalid return value from callback", s_szProc); ValidationException(); break; }
}
enumdoneok:;
for (ipv = 0; ipv < gpaEff.cpv; ipv++) { Common_Unhold(gpaEff.rgpv[ipv]); }
GPA_Term(&gpaEff); }
hres = S_OK; }
ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | GetLoad | * * Retrieve the memory load setting for the device. * * @cwrap PDD | this * * @parm LPDWORD | pdwLoad | * * Receives memory load. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_NOTEXCLUSIVEACQUIRED>: The device is acquired, * but not exclusively, or the device is not acquired * at all. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device * does not support force feedback. * *****************************************************************************/
STDMETHODIMP CDIDev_GetLoad(PDD this, LPDWORD pdwOut) { HRESULT hres; EnterProc(CDIDev_GetLoad, (_ "p", this));
hres = CDIDev_CreateEffectDriver(this);
if (SUCCEEDED(hres)) { DIDEVICESTATE ds;
CDIDev_EnterCrit(this);
/*
* Note that it isn't necessary to check * CDIDev_IsExclAcquired(), because the effect shepherd * will deny us access to a device we don't own. * * ISSUE-2001/03/29-timgill Need to handle DIERR_INPUTLOST case */
ds.dwSize = cbX(ds); /*
* Prefix raises a warning (mb:34564) that this->pes could * be NULL however CDIDev_CreateEffectDriver only succeeds * if it is not. */ hres = this->pes->lpVtbl-> GetForceFeedbackState(this->pes, &this->sh, &ds);
*pdwOut = ds.dwLoad;
CDIDev_LeaveCrit(this);
}
/*
* Can't use ExitOleProcPpv here because pdwOut is garbage on error. */ ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | Escape | * * Send a hardware-specific command to the driver. * * @cwrap PDD | this * * @parm LPDIEFFESCAPE | pesc | * * Pointer to a <t DIEFFESCAPE> structure which describes * the command to be sent. On success, the * <e DIEFFESCAPE.cbOutBuffer> field contains the number * of bytes of the output buffer actually used. * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_NOTEXCLUSIVEACQUIRED>: The device is acquired, * but not exclusively, or the device is not acquired * at all. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device * does not support force feedback. * *****************************************************************************/
STDMETHODIMP CDIDev_Escape(PV pdd, LPDIEFFESCAPE pesc _THAT) { HRESULT hres; EnterProcR(IDirectInputDevice8::Escape, (_ "p", pdd));
if (SUCCEEDED(hres = hresPvT(pdd)) && SUCCEEDED(hres = hresFullValidPesc(pesc, 1))) { PDD this = _thisPv(pdd);
AssertF(this->sh.dwEffect == 0);
hres = CDIDev_CreateEffectDriver(this);
if (SUCCEEDED(hres)) {
CDIDev_EnterCrit(this);
/*
* Note that it isn't necessary to check * CDIDev_IsExclAcquired(), because the effect shepherd * will deny us access to a device we don't own. * * ISSUE-2001/03/29-timgill Need to handle DIERR_INPUTLOST case */
/*
* Prefix raises a warning (mb:34564) that this->pes could * be NULL however CDIDev_CreateEffectDriver only succeeds * if it is not. */ hres = IDirectInputEffectShepherd_DeviceEscape( this->pes, &this->sh, pesc);
CDIDev_LeaveCrit(this);
}
}
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | RefreshGain | * * Set the device gain setting appropriately. * * The device shepherd will take care of muxing in the * global gain. * * @cwrap PDD | this * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_NOTEXCLUSIVEACQUIRED>: The device is acquired, * but not exclusively, or the device is not acquired * at all. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device * does not support force feedback. * *****************************************************************************/
STDMETHODIMP CDIDev_RefreshGain(PDD this) { HRESULT hres;
AssertF(CDIDev_InCrit(this));
if (this->pes) { hres = this->pes->lpVtbl->SetGain(this->pes, &this->sh, this->dwGain); } else { hres = S_OK; }
return hres; }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CDIDev | SnapOneEffect | * * Read one force feedback effect key and record the * information stored therein. We also tally the * flag into the <e CDIDev.didcFF> so we can return * the global flags from <mf IDirectInputDevice::GetCapabilities>. * * The <e CDIDev.rgemi> field already points to a * preallocated array * of <t EFFECTMAPINFO> structures, and * the <e CDIDev.cemi> field contains the number * of entries in that array which are already in use. * * @cwrap PDD | this * * @parm HKEY | hkEffects | * * The registry key containing the effects. * * @parm DWORD | iKey | * * Index number of the subkey. * * @returns * * None. * * If the specified subkey is damaged, it is skipped. * *****************************************************************************/
void INTERNAL CDIDev_SnapOneEffect(PDD this, HKEY hkEffects, DWORD ihk) { TCHAR tszGuid[ctchGuid]; LONG lRc; PEFFECTMAPINFO pemi;
/*
* Make sure that DIEFT_* and DIDC_* agree where they overlap. */ CAssertF(DIEFT_FORCEFEEDBACK == DIDC_FORCEFEEDBACK); CAssertF(DIEFT_FFATTACK == DIDC_FFATTACK); CAssertF(DIEFT_FFFADE == DIDC_FFFADE); CAssertF(DIEFT_SATURATION == DIDC_SATURATION); CAssertF(DIEFT_POSNEGCOEFFICIENTS == DIDC_POSNEGCOEFFICIENTS); CAssertF(DIEFT_POSNEGSATURATION == DIDC_POSNEGSATURATION);
pemi = &this->rgemi[this->cemi];
/*
* First get the GUID for the effect. */ lRc = RegEnumKey(hkEffects, ihk, tszGuid, cA(tszGuid));
if (lRc == ERROR_SUCCESS && ParseGUID(&pemi->guid, tszGuid)) { HKEY hk;
/*
* Note that we don't need to check for duplicates. * The registry itself does not allow two keys to have * the same name. */
lRc = RegOpenKeyEx(hkEffects, tszGuid, 0, KEY_QUERY_VALUE, &hk); if (lRc == ERROR_SUCCESS) {
DWORD cb;
cb = cbX(pemi->wszName); lRc = RegQueryStringValueW(hk, 0, pemi->wszName, &cb); if (lRc == ERROR_SUCCESS) { HRESULT hres; hres = JoyReg_GetValue(hk, TEXT("Attributes"), REG_BINARY, &pemi->attr, cbX(pemi->attr)); if (SUCCEEDED(hres) && (pemi->attr.dwCoords & DIEFF_COORDMASK)) {
this->didcFF |= (pemi->attr.dwEffType & DIEFT_VALIDFLAGS); this->cemi++;
} }
RegCloseKey(hk); } } }
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CDIDev | InitFF | * * Initialize the force-feedback portion of the device. * * Collect the force feedback attributes. * * Snapshot the list of force feedback effects. * * @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice * * @returns * * Returns a COM error code. The following error codes are * intended to be illustrative and not necessarily comprehensive. * * <c DI_OK> = <c S_OK>: The operation completed successfully. * * <c DIERR_OUTOFMEMORY> = <c E_OUTOFMEMORY>: Out of memory. * * <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The device * does not support force feedback. * *****************************************************************************/ STDMETHODIMP CDIDev_InitFF(PDD this) { HKEY hkFF; HRESULT hres; EnterProcI(CDIDev_InitFF, (_ "p", this));
AssertF(this->didcFF == 0);
hres = this->pdcb->lpVtbl->GetFFConfigKey(this->pdcb, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkFF);
if( hres == S_FALSE ) { /*
* Try causing the driver to be loaded so that it can initialize * the effects in the registry before we proceed. * * Need to exercise caution here. A driver that expects to use this * functionality cannot call into Dinput query for device attributes. * Can result in endless loop where we call the driver and the driver * calls us back */ hres = CDIDev_CreateEffectDriver(this); if( SUCCEEDED( hres ) ) { hres = this->pdcb->lpVtbl->GetFFConfigKey(this->pdcb, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkFF); if( hres == S_FALSE ) { hres = E_FAIL; } } }
if (SUCCEEDED(hres)) { DWORD chk; HKEY hkEffects; LONG lRc; AssertF( hkFF );
lRc = JoyReg_GetValue(hkFF, TEXT("Attributes"), REG_BINARY, &this->ffattr, cbX(this->ffattr)); if (lRc != S_OK) { ZeroX(this->ffattr); }
lRc = RegOpenKeyEx(hkFF, TEXT("Effects"), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkEffects); if (lRc == ERROR_SUCCESS) { lRc = RegQueryInfoKey(hkEffects, 0, 0, 0, &chk, 0, 0, 0, 0, 0, 0, 0); if (lRc == ERROR_SUCCESS) { hres = AllocCbPpv(cbCxX(chk, EFFECTMAPINFO), &this->rgemi); if (SUCCEEDED(hres)) { DWORD ihk;
this->cemi = 0; for (ihk = 0; ihk < chk; ihk++) {
CDIDev_SnapOneEffect(this, hkEffects, ihk);
} this->didcFF &= DIDC_FFFLAGS;
/*
* Note that we mark DIDC_FORCEFEEDBACK only if * we actually find any effects. */ if (this->cemi) { this->didcFF |= DIDC_FORCEFEEDBACK; }
hres = S_OK; } else { RPF("Warning: Insufficient memory for force feedback"); }
} else { hres = E_FAIL; } RegCloseKey(hkEffects);
} else { hres = E_NOTIMPL; }
RegCloseKey(hkFF); }
ExitBenignOleProc(); return hres; }
|