1671 lines
49 KiB
C
1671 lines
49 KiB
C
|
/*****************************************************************************
|
||
|
*
|
||
|
* 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;
|
||
|
}
|
||
|
|