Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4817 lines
140 KiB

/*****************************************************************************
*
* DIEff.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* The standard implementation of IDirectInputEffect.
*
* This is the device-independent part. the device-dependent
* part is handled by the IDirectInputEffectShepherd.
*
* Contents:
*
* CDIEff_CreateInstance
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* Note!
*
* Out of laziness, all effects share the same critical section as
* their parent device. This saves us from all sorts of race
* conditions. Not all of them, but a big chunk of them.
*
* A common race condition that this protects us against is
* where an application tries to download an effect at the same
* time another thread decides to unacquire the device.
*
*****************************************************************************/
/*****************************************************************************
*
* More laziness: "TypeSpecificParams" is such a long thing.
*
*****************************************************************************/
#define cbTSP cbTypeSpecificParams
#define lpvTSP lpvTypeSpecificParams
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflEff
/*****************************************************************************
*
* The flags for dwMessage
*
*****************************************************************************/
#define EFF_DEFAULT 0
#define EFF_PLAY 1
#define EFF_STOP 2
/*****************************************************************************
*
* Declare the interfaces we will be providing.
*
*****************************************************************************/
Primary_Interface(CDIEff, IDirectInputEffect);
Interface_Template_Begin(CDIEff)
Primary_Interface_Template(CDIEff, IDirectInputEffect)
Interface_Template_End(CDIEff)
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct CDIEff |
*
* The generic <i IDirectInputEffect> object.
*
* @field IDirectInputEffect | def |
*
* <i DirectInputEffect> object (containing vtbl).
*
* @field struct CDIDev * | pdev |
*
* Reference to parent device tracked via <f Common_Hold>.
*
* @field DICREATEEFFECTINFO | cei |
*
* Parameters that tell us how to talk to the effect driver.
*
* @field BOOL | fInitialized:1 |
*
* Do we know who we are?
*
* @field BOOL | fDadNotified:1 |
*
* Does our parent know that we exist?
*
* @field BOOL | fDadDead:1 |
*
* Has our parent been destroyed (from the app's point of view)?
*
* @field TSDPROC | hresValidTsd |
*
* Callback function that validates the type-specific data.
*
* @field HANDLE | hEventDelete |
*
* Event to signal to the timer thread that the app performed final release on the effect.
*
* @field HANDLE | hEventThreadDead |
*
* Event to signal AppFinalize to perform the final release on the effect.
*
* @field HANDLE | hEventGeneral |
*
* Event to signal to the timer thread that the app called Start(...) or Stop() the effect.
*
* @field HANDLE | hThread |
*
* Handle to the timer thread.
*
* @field DWORD | dwMessage |
*
* Message to the thread as to which event hEventGeneral is actually signaling.
* Can be EFF_DEFAULT, EFF_PLAY or EFF_STOP>
*
* @field DIEFFECTATTRIBUTES | dEffAttributes |
*
* Attributes of the effect (includes dwEffectType and dwEffectId, among others).
*
* @field DWORD | dwcLoop|
*
* Loop count for playing the effect (passed in the call to Start(...))
*
* @field DWORD | dwFlags |
*
* Flags for playing the effect (passed in the call to Start(...))
*
* @field DWORD | diepDirty |
*
* The parts of the effect which are "dirty" and need to
* be updated by the next <mf IDirectInputEffect::Download>.
*
* @field DWORD | diepUnset |
*
* The parts of the effect which have yet to be set by the
* application. Items which we can set decent defaults for
* are not counted.
*
* @field DWORD | dwDirFlags |
*
* Flags that record the direction format the application
* last set.
*
* @field DWORD | dwCoords |
*
* Coordinate systems supported by device.
*
* @field LPVOID | lpvTSP |
*
* Temporary buffer used to cache type-specific parameters
* during Try'ing of proposed effect parameters.
*
* @field SHEPHANDLE | sh |
*
* Effect handle information.
*
* @field DIEFFECT | eff |
*
* Cached effect parameters as they exist (or should exist)
* on the device.
* Direction parameters are in device-preferred coordinates.
*
* @field DIENVELOPE | env |
*
* Cached envelope parameters as they exist (or should exist)
* on the device.
*
* @field LONG | rglDirApp[DIEFFECT_MAXAXES] |
*
* Cached direction list, in application native format.
* The format of this array is kept in the
* <e CDIEff.dwDirFlags> field.
*
* @field DWORD | rgdwAxes[DIEFFECT_MAXAXES] |
*
* Cached axis list, stored as item numbers.
*
* @field LONG | rglDirDev[DIEFFECT_MAXAXES] |
*
* Cached direction list, in device native format.
* The format of this array is kept in the
* <e DIEFFECT.dwFlags> field of the
* <e CDIEff.eff>.
*
* @field GUID | guid |
*
* Identity.
*
*****************************************************************************/
typedef STDMETHOD(TSDPROC)(LPCDIEFFECT peff, DWORD cAxes);
typedef struct CDIEff {
/* Supported interfaces */
IDirectInputEffect def;
struct CDIDev *pdev;
LPDIRECTINPUTEFFECTSHEPHERD pes;
BOOL fDadNotified:1;
BOOL fDadDead:1;
BOOL fInitialized:1;
TSDPROC hresValidTsd;
/* WARNING! EVERYTHING AFTER THIS LINE IS ZERO'd ON A RESET */
HANDLE hEventDelete;
HANDLE hEventGeneral;
HANDLE hEventThreadDead;
HANDLE hThread;
DWORD dwMessage;
DIEFFECTATTRIBUTES dEffAttributes;
DWORD dwcLoop;
DWORD dwFlags;
DWORD diepDirty;
DWORD diepUnset;
DWORD dwDirFlags;
DWORD dwCoords;
LPVOID lpvTSP;
SHEPHANDLE sh;
DIEFFECT effDev;
DIEFFECT effTry;
DIENVELOPE env;
GUID guid;
LONG rglDirApp[DIEFFECT_MAXAXES];
DWORD rgdwAxes[DIEFFECT_MAXAXES];
LONG rglDirDev[DIEFFECT_MAXAXES];
LONG rglDirTry[DIEFFECT_MAXAXES];
/* WARNING! EVERYTHING ABOVE THIS LINE IS ZERO'd ON A RESET */
/*
* The Reset() function assumes that the entire remainder
* of the structure is to be zero'd out, so if you add a field
* here, make sure to adjust Reset() accordingly.
*/
} CDIEff, DE, *PDE;
#define ThisClass CDIEff
#define ThisInterface IDirectInputEffect
/*****************************************************************************
*
* Forward declarations
*
* These are out of laziness, not out of necessity.
*
*****************************************************************************/
STDMETHODIMP CDIEff_IsValidUnknownTsd(LPCDIEFFECT peff, DWORD cAxes);
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | QueryInterface |
*
* Gives a client access to other interfaces on an object.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm IN REFIID | riid |
*
* The requested interface's IID.
*
* @parm OUT LPVOID * | ppvObj |
*
* Receives a pointer to the obtained interface.
*
* @returns
*
* Returns a COM error code.
*
* @xref OLE documentation for <mf IUnknown::QueryInterface>.
*
*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | AddRef |
*
* Increments the reference count for the interface.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::AddRef>.
*
*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Release |
*
* Decrements the reference count for the interface.
* If the reference count on the object falls to zero,
* the object is freed from memory.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::Release>.
*
*****************************************************************************/
#ifdef DEBUG
Default_QueryInterface(CDIEff)
Default_AddRef(CDIEff)
Default_Release(CDIEff)
#else
#define CDIEff_QueryInterface Common_QueryInterface
#define CDIEff_AddRef Common_AddRef
#define CDIEff_Release Common_Release
#endif
#define CDIEff_QIHelper Common_QIHelper
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CDIEff | EnterCrit |
*
* Enter the object critical section.
*
* @cwrap PDE | this
*
*****************************************************************************/
void INLINE
CDIEff_EnterCrit(PDE this)
{
AssertF(this->pdev);
CDIDev_EnterCrit(this->pdev);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CDIEff | LeaveCrit |
*
* Leave the object critical section.
*
* @cwrap PDE | this
*
*****************************************************************************/
void INLINE
CDIEff_LeaveCrit(PDE this)
{
AssertF(this->pdev);
CDIDev_LeaveCrit(this->pdev);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CDIEff | CanAccess |
*
* Check if the effect can be accessed. For this to succeeed,
* the effect must be initialized, and the parent device
* must be acquired in exclusive mode.
*
* @cwrap PDE | this
*
* @returns
*
* <c S_OK> if the device is exclusively acquired.
*
* <c DIERR_INPUTLOST> if acquisition has been lost.
*
* <c DIERR_NOTEXCLUSIVEACQUIRED> the device is acquired,
* but not exclusively, or if the device is not acquired
* at all.
*
* <c DIERR_NOTINITIALIZED> if the effect object has not
* yet been initialized.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_CanAccess_(this, z) \
_CDIEff_CanAccess_(this) \
#endif
STDMETHODIMP
CDIEff_CanAccess_(PDE this, LPCSTR s_szProc)
{
HRESULT hres;
AssertF(this->pdev);
AssertF(CDIDev_InCrit(this->pdev));
if (this->fInitialized) {
hres = CDIDev_IsExclAcquired(this->pdev);
} else {
if (s_szProc) {
RPF("ERROR %s: Effect not initialized", s_szProc);
}
hres = DIERR_NOTINITIALIZED;
}
return hres;
}
#define CDIEff_CanAccess(this) \
CDIEff_CanAccess_(this, s_szProc) \
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_Reset |
*
* Releases all the resources of a generic effect that
* are associated with a particular device effect instance.
*
* This method is called in preparation for reinitialization.
*
* @parm PV | pvObj |
*
* Object being reset. Note that it may not have been
* completely initialized, so everything should be done
* carefully.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_Reset(PDE this)
{
HRESULT hres;
AssertF(this->pdev);
CDIEff_EnterCrit(this);
/*
* Destroying an effect implicitly stops it.
*
* It's okay if this fails (and in fact it usually will).
* We're just playing it safe.
*/
hres = IDirectInputEffectShepherd_DestroyEffect(this->pes, &this->sh);
AssertF(this->lpvTSP == 0);
FreePpv(&this->effDev.lpvTSP);
//rezero the entire DIEFFECTATTRIBUTES!
ZeroBuf(&this->dEffAttributes,
cbX(DE) -
FIELD_OFFSET(DE, dEffAttributes));
if (this->hEventDelete != NULL) {
if( SetEvent(this->hEventDelete) && this->hEventThreadDead != NULL )
{
DWORD dwRc = 0xFFFFFFFF;
do
{
dwRc = WaitForSingleObject(this->hEventThreadDead, INFINITE);
} while( dwRc != WAIT_OBJECT_0 );
CloseHandle(this->hEventThreadDead);
this->hEventThreadDead = NULL;
CloseHandle(this->hEventDelete);
this->hEventDelete = NULL;
if (this->hEventGeneral != NULL)
{
CloseHandle(this->hEventGeneral);
this->hEventGeneral = NULL;
}
if (this->hThread != NULL)
{
CloseHandle(this->hThread);
this->hThread = NULL;
}
}
}
this->dwMessage = EFF_DEFAULT;
this->effDev.dwSize = cbX(this->effDev);
this->env.dwSize = cbX(this->env);
/*
* DIEP_DURATION - Defaults to zero.
* DIEP_SAMPLEPERIOD - Defaults to zero.
* DIEP_GAIN - Defaults to zero.
* DIEP_TRIGGERBUTTON - Defaults to DIEB_NOTRIGGER.
* DIEP_TRIGGERREPEATINTERVAL - Defaults to INFINITE (no autorepeat).
* DIEP_AXES - Must be set manually.
* DIEP_DIRECTION - Must be set manually.
* DIEP_ENVELOPE - No envelope.
* DIEP_TYPESPECIFICPARAMS - Must be set manually.
* DIEP_STARTDELAY - Defaults to zero. (new in DX6.1)
*/
this->effDev.dwTriggerButton = DIEB_NOTRIGGER;
this->diepUnset = DIEP_AXES |
DIEP_DIRECTION |
DIEP_TYPESPECIFICPARAMS;
this->effDev.rgdwAxes = this->rgdwAxes;
this->effDev.rglDirection = this->rglDirDev;
this->fInitialized = 0;
CDIEff_LeaveCrit(this);
hres = S_OK;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | UnloadWorker |
*
* Remove the effect from the device. All parameters have
* been validated.
*
* @cwrap PDE | 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 S_FALSE>: The effect was not previously downloaded,
* so there was nothing to unload. Note that this is a
* success code.
*
* <c DI_PROPNOEFFECT> = <c S_FALSE>: The effect was not
* previously downloaded.
*
* <c DIERR_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not yet been <mf IDirectInputEffect::Initialize>d.
*
* <c DIERR_INPUTLOST> if acquisition has been lost.
*
* <c DIERR_NOTEXCLUSIVEACQUIRED> the device is acquired,
* but not exclusively, or if the device is not acquired
* at all.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_UnloadWorker_(this, z) \
_CDIEff_UnloadWorker_(this) \
#endif
HRESULT INTERNAL
CDIEff_UnloadWorker_(PDE this, LPCSTR s_szProc)
{
HRESULT hres;
AssertF(CDIDev_InCrit(this->pdev));
if (SUCCEEDED(hres = CDIEff_CanAccess(this))) {
/*
* The effect driver will stop the effect (if it is playing)
* before destroying it.
*/
hres = IDirectInputEffectShepherd_DestroyEffect(
this->pes, &this->sh);
} else {
/*
* The effect is dead. Long live the effect.
*/
this->sh.dwEffect = 0;
}
this->diepDirty = DIEP_ALLPARAMS;
return hres;
}
#define CDIEff_UnloadWorker(this) \
CDIEff_UnloadWorker_(this, s_szProc) \
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CDIEff_AppFinalize |
*
* The application has performed its final release.
*
* Tell our parent that we are officially dead, so that
* dad will stop tracking us and release its hold on us.
*
* @parm PV | pvObj |
*
* Object being released. Note that it may not have been
* completely initialized, so everything should be done
* carefully.
*
*****************************************************************************/
void INTERNAL
CDIEff_AppFinalize(PV pvObj)
{
PDE this = pvObj;
DWORD dwRc = 0xFFFFFFFF;
EnterProcR(CDIEff_AppFinalize, (_ "p", pvObj));
if (this->fDadNotified) {
this->fDadNotified = 0;
CDIEff_EnterCrit(this);
/*
* Kill the timer thread, if any.
* For this, fire off the effect's event.
*/
if (this->hEventDelete != NULL) {
if( SetEvent(this->hEventDelete) && this->hEventThreadDead != NULL )
{
do
{
dwRc = WaitForSingleObject(this->hEventThreadDead, INFINITE);
} while( dwRc != WAIT_OBJECT_0 );
CloseHandle(this->hEventThreadDead);
this->hEventThreadDead = NULL;
CloseHandle(this->hEventDelete);
this->hEventDelete = NULL;
if (this->hEventGeneral != NULL)
{
CloseHandle(this->hEventGeneral);
this->hEventGeneral = NULL;
}
if (this->hThread != NULL)
{
CloseHandle(this->hThread);
this->hThread = NULL;
}
}
}
CDIEff_UnloadWorker_(this, 0);
CDIEff_LeaveCrit(this);
CDIDev_NotifyDestroyEffect(this->pdev, this);
}
ExitProcR();
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CDIEff_Finalize |
*
* Releases the resources of an effect.
*
* @parm PV | pvObj |
*
* Object being released. Note that it may not have been
* completely initialized, so everything should be done
* carefully.
*
*****************************************************************************/
void INTERNAL
CDIEff_Finalize(PV pvObj)
{
HRESULT hres;
PDE this = pvObj;
#if 0 // def XDEBUG
if (this->cCrit) {
RPF("IDirectInputEffect::Release: Another thread is using the object; crash soon!");
}
#endif
AssertF(this->sh.dwEffect == 0);
if (this->pdev) {
hres = CDIEff_Reset(this);
AssertF(SUCCEEDED(hres));
}
Invoke_Release(&this->pes);
Common_Unhold(this->pdev);
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | GetEffectGuid |
*
* Retrieve the GUID for the effect represented by the
* <i IDirectInputEffect> object. Additional information
* about the effect can be obtained by passing the
* <t GUID> to <mf IDirectInputDevice8::GetEffectInfo>.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm OUT LPGUID | pguid |
*
* Points to a <t GUID> structure that is filled in
* by the function.
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not yet been <mf IDirectInputEffect::Initialize>d.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p lpDirectInputEffect> or
* <p lpdc> parameter is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_GetEffectGuid(PDIE pdie, LPGUID pguid)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::GetEffectGuid, (_ "p", pdie));
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresFullValidWritePguid(pguid, 1))) {
PDE this = _thisPvNm(pdie, def);
/*
* Race condition: If the caller reinitializes and
* does a GetEffectGuid simultaneously, the return value
* is random. That's okay; it's the caller's problem.
*/
if (this->fInitialized) {
*pguid = this->guid;
hres = S_OK;
} else {
hres = DIERR_NOTINITIALIZED;
}
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func __int64 | _ftol |
*
* Convert a floating point number to an integer.
*
* We do it ourselves because of the C runtime.
*
* It's the caller's job to worry about the rounding mode.
*
* @parm double | lf |
*
* Floating point number to convert.
*
*****************************************************************************/
#if defined(WIN95)
#pragma warning(disable:4035) /* no return value (duh) */
BYTE _fltused;
__declspec(naked) __int64 __cdecl
_ftol(double lf)
{
lf;
_asm {
sub esp, 8
fistp qword ptr [esp]
pop eax
pop edx
ret
}
}
#pragma warning(default:4035)
#endif
/*
* The floating point type we use for intermediates.
*/
typedef long double FPTYPE;
/*****************************************************************************
*
* @doc INTERNAL
*
* @func FPTYPE | CDIEff_IntToAngle |
*
* Convert an integer angle to a floating point angle.
*
* @parm LONG | l |
*
* Integer angle to convert.
*
*****************************************************************************/
#ifndef M_PI
#define M_PI 3.1415926535897932384
#endif
FPTYPE INLINE
CDIEff_IntToAngle(LONG l)
{
FPTYPE theta;
/*
* 2pi radians equals 360 degrees.
*/
theta = l * (2 * M_PI) / (360 * DI_DEGREES);
return theta;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func LONG | CDIEff_IntAtan2 |
*
* Compute the floating point arctangent of y/x and
* convert the resulting angle to an integer in DI_DEGREES.
*
* @parm FPTYPE | y |
*
* Vertical coordinate.
*
* @parm FPTYPE | x |
*
* Horizontal coordinate.
*
* @returns
*
* A value in the range [ 0 .. 360 * DI_DEGREES ).
*
*****************************************************************************/
LONG INLINE
CDIEff_IntAtan2(FPTYPE y, FPTYPE x)
{
FPTYPE theta;
LONG l;
#if defined(_X86_)
/*
* The Intel FPU doesn't care about (0, 0).
*/
theta = atan2(y, x);
#else
/*
* The Alpha gets really upset about (0, 0).
*/
if (y != 0.0 || x != 0.0) {
theta = atan2(y, x);
} else {
theta = 0.0;
}
#endif
/*
* atan2 returns a value in the range -M_PI to +M_PI.
* On the Intel x86, there are four rounding modes:
*
* Round to nearest or even
* Round towards minus infinity
* Round towards plus infinity
* Round towards zero
*
* By ensuring that the value being rounded is positive, we
* reduce to three cases:
*
* Round to nearest or even
* Round down
* Round up
*
* And as long as the app doesn't change its rounding mode
* (few do), the values will be consistent. (Whereas if we
* let negative numbers through, you would get weird behavior
* as the angle neared M_PI aka -M_PI.)
*
* Method 1:
*
* if (theta < 0) theta += 2 * M_PI;
* l = convert(theta);
* return l;
*
* This is bad because if theta starts out as -epsilon, then
* we end up converting 2 * M_PI - epsilon, which might get
* rounded up to 360 * DI_DEGREES. But our return value
* must be in the range 0 <= l < 360 * DI_DEGREES.
*
* So instead, we use method 2:
*
* l = convert(theta + 2 * M_PI);
* if (l >= 360 * DI_DEGREES) l -= 360 * DI_DEGREES;
*
* The value being converted ends up in the range M_PI .. 3 * M_PI,
* which after rounding becomes 180 * DI_DEGREES .. 540 * DI_DEGREES.
* The final check then pulls the value into range.
*/
/*
* 2pi radians equals 360 degrees.
*/
l = (LONG)((theta + 2 * M_PI) * (360 * DI_DEGREES) / (2 * M_PI));
if (l >= 360 * DI_DEGREES) {
l -= 360 * DI_DEGREES;
}
return l;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func FPTYPE | atan2Z |
*
* Just like <f atan2>, except it doesn't barf on
* (0, 0).
*
* @parm FPTYPE | y |
*
* Vertical coordinate.
*
* @parm FPTYPE | x |
*
* Horizontal coordinate.
*
*****************************************************************************/
FPTYPE INLINE
atan2Z(FPTYPE y, FPTYPE x)
{
#if defined(_X86_)
/*
* The Intel FPU doesn't care about (0, 0).
*/
return atan2(y, x);
#else
/*
* The Alpha gets really upset about (0, 0).
*/
if (y != 0.0 || x != 0.0) {
return atan2(y, x);
} else {
return 0.0;
}
#endif
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_CartToAngles |
*
* Convert cartesian coordinates to angle-based coordinates
* (either polar or spherical). Note that the resulting
* angles have not been normalized.
*
* @parm DWORD | cAxes |
*
* Number of axes involved, never zero.
*
* @parm LPLONG | rglAngles |
*
* Buffer to receive angle-base coordinates.
* The final entry of the array contains nothing of interest.
*
* @parm LPCLONG | rglCart |
*
* Buffer containing existing cartesian coordinates.
*
* @parm DWORD | dieff |
*
* Flags specifying whether the target coordinates should
* be <c DIEFF_POLAR> or <c DIEFF_SPHERICAL>.
*
* Polar and spherical coordinates differ only in their
* treatment of the first angle.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_CartToAngles(DWORD cAxes,
LPLONG rglAngles, const LONG *rglCart, DWORD dieff)
{
HRESULT hres;
FPTYPE r;
DWORD iAxis;
AssertF(cAxes);
AssertF(dieff == DIEFF_POLAR || dieff == DIEFF_SPHERICAL);
/*
* If we're converting a 1-axis cartesian effect
* the value of rglAngles[0] will not be overwritten;
* the value that is put there initially can be random,
* since rglAngles is never initialized (Whistler bug 228280).
* but we can't change this behaviour without potentially
* breaking compatibility w/ some devices.
* The best we can do is to print out a warning in debug.
*/
if (cAxes == 1)
{
RPF("Warning: converting a 1-axis cartesian effect to polar or spherical coordinates: the results will be undefined.");
}
/*
* Prime the pump.
*/
r = rglCart[0];
/*
* Then walk the coordinates, converting to angles as we go.
*/
for (iAxis = 1; iAxis < cAxes; iAxis++) {
FPTYPE y = rglCart[iAxis];
rglAngles[iAxis-1] = CDIEff_IntAtan2(y, r);
r = sqrt(r * r + y * y);
}
/*
* The last coordinate is left garbage.
*
* NOTE! Mathematically, the last coordinate is r.
*/
/*
* Adjust for DIEFF_POLAR.
*
* theta(polar) = theta(spherical) + 90deg
*/
if (dieff & DIEFF_POLAR) {
rglAngles[0] += 90 * DI_DEGREES;
}
hres = S_OK;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_AnglesToCart |
*
* Convert angle-based coordinates
* (either polar or spherical)
* to cartesian coordinates.
*
* @parm DWORD | cAxes |
*
* Number of axes involved, never zero.
*
* @parm LPLONG | rglCart |
*
* Buffer to receive cartesian coordinates.
*
* @parm LPCLONG | rglAngles |
*
* Buffer containing existing angle-base coordinates.
*
* @parm DWORD | dieff |
*
* Flags specifying whether the source coordinates are
* <c DIEFF_POLAR> or <c DIEFF_SPHERICAL>.
*
* Polar and spherical coordinates differ only in their
* treatment of the first angle.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_AnglesToCart(DWORD cAxes,
LPLONG rglCart, const LONG *rglAngles, DWORD dieff)
{
HRESULT hres;
FPTYPE x[DIEFFECT_MAXAXES];
DWORD iAxis;
DWORD lAngle;
AssertF(cAxes);
AssertF(cAxes <= DIEFFECT_MAXAXES);
AssertF(dieff == DIEFF_POLAR || dieff == DIEFF_SPHERICAL);
/*
* Prime the pump.
*/
x[0] = 1.0;
/*
* For each angle, rotate in that direction.
*
* If polar, then the first angle is biased by 90deg,
* so unbias it.
*
* theta(spherical) = theta(polar) - 90deg
*/
lAngle = rglAngles[0];
if (dieff & DIEFF_POLAR) {
lAngle -= 90 * DI_DEGREES;
}
for (iAxis = 1; iAxis < cAxes; iAxis++) {
DWORD iX;
FPTYPE theta, costh;
theta = CDIEff_IntToAngle(lAngle);
x[iAxis] = sin(theta);
/*
* Compiler is too *naive* to hoist this expression.
*
* It's also too *naive* to use the FSINCOS instruction.
*/
costh = cos(theta);
for (iX = 0; iX < iAxis; iX++) {
x[iX] *= costh;
}
/*
* Note that this is safe because the angle array
* always contains an extra zero.
*/
lAngle = rglAngles[iAxis];
}
/*
* Now convert the floating point values to scaled integers.
*/
for (iAxis = 0; iAxis < cAxes; iAxis++) {
rglCart[iAxis] = (LONG)(x[iAxis] * DI_FFNOMINALMAX);
}
hres = S_OK;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func DWORD | CDIEff_ConvertDirection |
*
* Given coordinates in a source system and a target system,
* convert them.
*
* There are three possible source systems and three
* possible destination systems.
*
* Yes, this is the most annoying thing you could imagine.
*
* @parm DWORD | cAxes |
*
* Number of axes involved, never zero.
*
* @parm LPLONG | rglDst |
*
* Buffer to receive target coordinates.
*
* @parm DWORD | dieffDst |
*
* Coordinate systems supported by target. As many bits
* may be set as are supported by the target, but at least
* one must be set.
*
* @parm LPCLONG | rglSrc |
*
* Buffer containing source coordinates.
*
* @parm DWORD | dieffSrc |
*
* Coordinate system of source. Exactly one bit should be set.
*
* @returns
*
* Returns the coordinate system stored into the target.
*
*****************************************************************************/
DWORD INTERNAL
CDIEff_ConvertDirection(DWORD cAxes,
LPLONG rglDst, DWORD dieffDst,
const LONG *rglSrc, DWORD dieffSrc)
{
DWORD dieffRc;
HRESULT hres;
dieffSrc &= DIEFF_COORDMASK;
dieffDst &= DIEFF_COORDMASK;
AssertF(cAxes);
AssertF(dieffDst);
AssertF(dieffSrc == DIEFF_CARTESIAN ||
dieffSrc == DIEFF_POLAR ||
dieffSrc == DIEFF_SPHERICAL);
if (dieffSrc & dieffDst) {
/*
* The easy case: The two are directly compatible.
*
* Just slam the bits across and copy the format.
*/
CopyMemory(rglDst, rglSrc, cbCdw(cAxes));
dieffRc = dieffSrc;
} else
/*
* If they two are not directly compatible, see if
* the source is cartesian.
*/
if (dieffSrc & DIEFF_CARTESIAN) {
/*
* Source is cartesian, dest is something angular.
* Choose DIEFF_SPHERICAL if possible.
*/
AssertF(dieffDst & DIEFF_ANGULAR);
dieffRc = dieffDst & DIEFF_SPHERICAL;
if (dieffRc == 0) {
AssertF(dieffDst & DIEFF_POLAR);
dieffRc = DIEFF_POLAR;
}
hres = CDIEff_CartToAngles(cAxes, rglDst, rglSrc, dieffRc);
AssertF(SUCCEEDED(hres));
} else
/*
* The two are not directly compatible, and the source is
* not cartesian. This means that the source is one of the
* angular forms. The destination is a combination of
* the other angular form or cartesian.
*/
if (dieffDst & DIEFF_ANGULAR) {
/*
* Source is angular, dest is the other angular.
*/
AssertF(dieffSrc & DIEFF_ANGULAR);
AssertF((dieffSrc & dieffDst) == 0);
/*
* First copy everything over,
*/
CopyMemory(rglDst, rglSrc, cbCdw(cAxes));
/*
* Now rotate left or right, depending on which way
* we're going.
*/
if (dieffSrc & DIEFF_POLAR) {
/*
* Polar to spherical: Subtract 90deg.
*/
rglDst[0] -= 90 * DI_DEGREES;
} else {
/*
* Spherical to polar: Add 90deg.
*/
rglDst[0] += 90 * DI_DEGREES;
}
dieffRc = dieffDst & DIEFF_ANGULAR;
} else
/*
* All that's left is the source is angular and the destination
* is cartesian.
*/
{
AssertF(dieffSrc & DIEFF_ANGULAR);
AssertF(dieffDst & DIEFF_CARTESIAN);
hres = CDIEff_AnglesToCart(cAxes, rglDst, rglSrc, dieffSrc);
dieffRc = DIEFF_CARTESIAN;
}
/*
* If the resulting coordinate system is angular, then
* normalize all the angles.
*/
if (dieffRc & DIEFF_ANGULAR) {
DWORD iAxis;
/*
* Remember, the last entry is not a direction.
*/
for (iAxis = 0; iAxis < cAxes - 1; iAxis++) {
/*
* An annoying artifact of the C language is that
* the sign of the result of a % operation when the
* numerator is negative and the denominator is
* positive is implementation-defined. The standard
* does require that the absolute value of the result
* not exceed the denominator, so a quick final
* check brings things back into focus.
*/
rglDst[iAxis] %= 360 * DI_DEGREES;
if (rglDst[iAxis] < 0) {
rglDst[iAxis] += 360 * DI_DEGREES;
}
}
/*
* As always, the last coordinate is zero.
*/
rglDst[cAxes - 1] = 0;
}
return dieffRc;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | SyncShepHandle |
*
* Synchronize our private <t SHEPHANDLE> with the
* <t SHEPHANDLE> of the parent device. If they were
* out of sync, then mark the efect as completely dirty
* so it will get re-downloaded in full.
*
* @cwrap PDE | this
*
* @returns
*
* <c DI_OK> = <c S_OK>: The two were already in sync.
*
* <c S_FALSE>: The two were not in sync and are now in sync.
*
*****************************************************************************/
HRESULT INTERNAL
CDIEff_SyncShepHandle(PDE this)
{
HRESULT hres;
hres = CDIDev_SyncShepHandle(this->pdev, &this->sh);
if (hres == S_OK) {
} else {
/*
* We were out of sync with our dad. CDIDev_SyncShepHandle
* already sync'd us with dad and wiped out the effect handle.
* All that's left is to dirty everything because there is
* nothing to update.
*/
AssertF(hres == S_FALSE);
this->diepDirty = DIEP_ALLPARAMS;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | DownloadWorker |
*
* Place the effect on the device. All parameters have
* been validated and the critical section has already been
* taken.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* Effect to send down to the device.
*
* If we are downloading for real, then this is the
* <e CDIEff.effDev>.
*
* If we are hoping to download, then this is the
* <e CDIEff.effTry>.
*
* @parm DWORD | fl |
*
* Flags which specify which parameters are to be sent down
* to the driver as changed since last time.
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not yet been <mf IDirectInputEffect::Initialize>d.
*
* <c DIERR_DEVICEFULL>: The device does not have enough
* available memory to download the effect.
*
* <c DIERR_INPUTLOST> if acquisition has been lost.
*
* <c DIERR_NOTEXCLUSIVEACQUIRED> the device is acquired,
* but not exclusively, or if the device is not acquired
* at all.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_DownloadWorker_(this, peff, fl, z) \
_CDIEff_DownloadWorker_(this, peff, fl) \
#endif
HRESULT INTERNAL
CDIEff_DownloadWorker_(PDE this, LPCDIEFFECT peff, DWORD fl, LPCSTR s_szProc)
{
HRESULT hres;
AssertF(CDIDev_InCrit(this->pdev));
/*
* If we do not have acquisition, but we are coming from
* SetParameters, then turn it into a DIEP_NODOWNLOAD so
* the call will go through.
*/
hres = CDIEff_CanAccess(this);
if ((hres == DIERR_INPUTLOST || hres == DIERR_NOTEXCLUSIVEACQUIRED) &&
peff == &this->effTry) {
fl |= DIEP_NODOWNLOAD;
hres = S_OK;
}
if (SUCCEEDED(hres)) {
hres = CDIEff_SyncShepHandle(this);
if (!(fl & DIEP_NODOWNLOAD)) { /* If we are downloading */
/*
* If there are still unset bits, then barf.
*/
if (this->diepUnset & ~fl) {
RPF("%s: Effect still incomplete; "
"DIEP flags %08x need to be set",
s_szProc, this->diepUnset);
hres = DIERR_INCOMPLETEEFFECT;
goto done;
}
/*
* Since we are downloading, pass down all dirty bits.
*/
fl |= this->diepDirty;
}
/*
* Now call the driver to do the validation or
* the download (accordingly).
*
* Note that if nothing is to be done, then there is no need
* to call the driver.
*/
hres = IDirectInputEffectShepherd_DownloadEffect(
this->pes, (this->dEffAttributes).dwEffectId, &this->sh, peff, fl);
if (SUCCEEDED(hres)) {
if (fl & DIEP_NODOWNLOAD) {
hres = DI_DOWNLOADSKIPPED;
} else {
this->diepDirty = 0;
}
}
AssertF(hres != DIERR_NOTDOWNLOADED);
}
done:;
return hres;
}
#define CDIEff_DownloadWorker(this, peff, fl) \
CDIEff_DownloadWorker_(this, peff, fl, s_szProc) \
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Download |
*
* Place the effect on the device. If the effect is already
* on the device, then the existing effect is updated to
* match the values set via <mf IDirectInputEffect::SetParameters>.
*
* It is valid to update an effect while it is playing.
* The semantics of such an operation are explained in the
* documentation for <mf IDirectInputEffect::SetParameters>.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @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 S_FALSE>: The effect has already been downloaded to the
* device. Note that this is a success code.
*
* <c DIERR_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not yet been <mf IDirectInputEffect::Initialize>d.
*
* <c DIERR_DEVICEFULL>: The device does not have enough
* available memory to download the effect.
*
* <c DIERR_INPUTLOST> if acquisition has been lost.
*
* <c DIERR_NOTEXCLUSIVEACQUIRED> the device is acquired,
* but not exclusively, or if the device is not acquired
* at all.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid.
*
* <c DIERR_EFFECTPLAYING>: The parameters were updated in
* memory but were not downloaded to the device because
* the device does not support updating an effect while
* it is still playing. In such case, you must stop the
* effect, change its parameters, and restart it.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_Download(PDIE pdie)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Download, (_ "p", pdie));
if (SUCCEEDED(hres = hresPv(pdie))) {
PDE this = _thisPvNm(pdie, def);
CDIEff_EnterCrit(this);
hres = CDIEff_DownloadWorker(this, &this->effDev, 0);
CDIEff_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Unload |
*
* Remove the effect from the device.
*
* If the effect is playing, it is automatically stopped before
* it is unloaded.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not yet been <mf IDirectInputEffect::Initialize>d.
*
* <c DIERR_INPUTLOST> if acquisition has been lost.
*
* <c DIERR_NOTEXCLUSIVEACQUIRED> the device is acquired,
* but not exclusively, or if the device is not acquired
* at all.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_Unload(PDIE pdie)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Unload, (_ "p", pdie));
if (SUCCEEDED(hres = hresPv(pdie))) {
PDE this = _thisPvNm(pdie, def);
CDIEff_EnterCrit(this);
hres = CDIEff_UnloadWorker(this);
CDIEff_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | hresFullValidWritePeff |
*
* Verify that the recipient buffer is valid to receive
* effect information.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm LPDIEFFECT | peff |
*
* Structure that receives effect information. It has
* already been validate in size and for general writeability.
*
* @parm DWORD | fl |
*
* Zero or more <c DIEP_*> flags specifying which
* portions of the effect information is to be retrieved.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
#ifndef XDEBUG
#define hresFullValidWritePeff_(this, peff, fl, z, i) \
_hresFullValidWritePeff_(this, peff, fl) \
#endif
#define hresFullValidWritePeff(this, peff, fl, iarg) \
hresFullValidWritePeff_(this, peff, fl, s_szProc, iarg) \
HRESULT INTERNAL
hresFullValidWritePeff_(PDE this, LPDIEFFECT peff, DWORD fl,
LPCSTR s_szProc, int iarg)
{
HRESULT hres;
AssertF(CDIDev_InCrit(this->pdev));
/*
* You can't get the parameters of a nonexistent effect.
*/
if (!this->fInitialized) {
hres = DIERR_NOTINITIALIZED;
goto done;
}
/*
* Flags are always validated.
*/
if (peff->dwFlags & ~DIEFF_VALID) {
RPF("ERROR %s: arg %d: Invalid value 0x%08x in DIEFFECT.dwFlags",
s_szProc, iarg, peff->dwFlags);
hres = E_INVALIDARG;
goto done;
}
/*
* If requesting something that requires object ids or offsets,
* make sure the caller picks one or the other.
*/
if (fl & DIEP_USESOBJECTS) {
switch (peff->dwFlags & DIEFF_OBJECTMASK) {
case DIEFF_OBJECTIDS:
case DIEFF_OBJECTOFFSETS:
break;
default:
RPF("ERROR %s: arg %d: Must specify one of "
"DIEFF_OBJECTIDS or DIEFF_OBJECTOFFSETS", s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
}
/*
* If requesting something that requires direction coordinates,
* make sure the caller picks something we can return.
*/
if (fl & DIEP_USESCOORDS) {
/*
* Polar coordinates require cAxes == 2. If not, then
* turn off DIEFF_POLAR so we won't return it.
*
* But the place where we check the number of axes is
* in the effect itself, not in the input buffer.
* The reason is that the caller might be passing cAxes=0
* intending to ping the number of axes, and I don't
* want to return an error or the app will get confused
* and panic.
*/
if (this->effDev.cAxes != 2 && (peff->dwFlags & DIEFF_POLAR)) {
RPF("WARNING %s: arg %d: DIEFF_POLAR requires DIEFFECT.cAxes=2",
s_szProc, 1);
peff->dwFlags &= ~DIEFF_POLAR;
}
/*
* There'd better be a coordinate system left.
*/
if ((peff->dwFlags & DIEFF_COORDMASK) == 0) {
RPF("ERROR %s: arg %d: No (valid) coordinate system "
"in DIEFFECT.dwFlags", s_szProc, 1);
hres = E_INVALIDARG;
goto done;
}
}
/*
* DIEP_DURATION
* DIEP_SAMPLEPERIOD
* DIEP_GAIN
* DIEP_TRIGGERBUTTON
* DIEP_TRIGGERREPEATINTERVAL
* - Simple dwords. No extra validation needed.
*/
/*
* DIEP_STARTDELAY
* - Although this is a simple DWORD, we do some
* sanity warnings because vendors will probably
* forget to initialize it. Also, you can't pass
* this flag if your structure isn't big enough.
*/
if (fl & DIEP_STARTDELAY) {
if (peff->dwSize < cbX(DIEFFECT_DX6)) {
RPF("ERROR %s: arg %d: Can't use DIEP_STARTDELAY with "
"DIEFFECT_DX5 structure", s_szProc, 1);
}
/*
* Sanity checks. A delay that isn't a multiple of 50ms is
* probably a bug.
*/
if (peff->dwStartDelay % 50000) {
RPF("WARNING %s: DIEFFECT.dwStartDelay = %d seems odd",
s_szProc, peff->dwStartDelay);
}
}
/*
* DIEP_TYPESPECIFICPARAMS
* - Validate that the buffer is big enough.
*/
AssertF(this->hresValidTsd);
if ((fl & DIEP_TYPESPECIFICPARAMS) &&
FAILED(hres = hresFullValidWritePvCb(peff->lpvTypeSpecificParams,
peff->cbTypeSpecificParams,
iarg))) {
RPF("ERROR %s: arg %d: Invalid pointer in "
"DIEFFECT.lpvTypeSpecificParams", s_szProc, iarg);
goto done;
}
/*
* DIEP_AXES
* DIEP_DIRECTION
* - The buffers must be of necessary size.
*/
if ((fl & DIEP_AXES) &&
FAILED(hres = hresFullValidWritePvCb(peff->rgdwAxes,
cbCdw(peff->cAxes), iarg))) {
RPF("ERROR %s: arg %d: Invalid pointer in DIEFFECT.rgdwAxes",
s_szProc, iarg);
goto done;
}
if ((fl & DIEP_DIRECTION) &&
FAILED(hres = hresFullValidWritePvCb(peff->rglDirection,
cbCdw(peff->cAxes), iarg))) {
RPF("ERROR %s: arg %d: Invalid pointer in DIEFFECT.rglDirection",
s_szProc, iarg);
goto done;
}
/*
* DIEP_ENVELOPE - The pointer must be valid to receive the envelope.
*/
if ((fl & DIEP_ENVELOPE) &&
FAILED(hres = hresFullValidWritePxCb(peff->lpEnvelope,
DIENVELOPE, iarg))) {
RPF("ERROR %s: arg %d: Invalid pointer in DIEFFECT.lpEnvelope",
s_szProc, iarg);
goto done;
}
hres = S_OK;
done:;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | MapDwords |
*
* Map a few <t DWORD>s based on the desired mapping mode
* of the caller.
*
* @cwrap PDE | this
*
* @parm DWORD | dwFlags |
*
* The mapping mode desired by the caller.
*
* @parm UINT | cdw |
*
* Number of items to convert.
*
* @parm LPDWORD | rgdwOut |
*
* Destination buffer.
*
* @parm LPCDWORD | rgdwIn |
*
* Source buffer.
*
* @parm UINT | devco |
*
* Conversion code describing what we're converting.
*
* @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 caller
* requested offsets but there is no data format selected.
*
*****************************************************************************/
#if 0
#ifndef XDEBUG
#define CDIEff_MapDwords_(this, fl, cdw, rgdwOut, rgdwIn, devco, z, i) \
_CDIEff_MapDwords_(this, fl, cdw, rgdwOut, rgdwIn, devco) \
#endif
#define CDIEff_MapDwords(this, fl, cdw, rgdwOut, rgdwIn, devco, i) \
CDIEff_MapDwords_(this, fl, cdw, rgdwOut, rgdwIn, devco, s_szProc, i) \
#endif
HRESULT INTERNAL
CDIEff_MapDwords(PDE this, DWORD dwFlags,
UINT cdw, LPDWORD rgdwOut, const DWORD *rgdwIn, UINT devco)
{
HRESULT hres;
AssertF(CDIDev_InCrit(this->pdev));
if (cdw) {
CopyMemory(rgdwOut, rgdwIn, cbCdw(cdw));
/*
* Okay, now things get weird. We internally keep the
* items as item IDs, because that's what drivers
* want. So we need to convert them to whatever the
* caller really wants.
*/
if (dwFlags & DIEFF_OBJECTOFFSETS) {
if (devco & DEVCO_FROMID) {
devco |= DEVCO_TOOFFSET;
} else {
AssertF(devco & DEVCO_TOID);
devco |= DEVCO_FROMOFFSET;
}
} else {
AssertF(dwFlags & DIEFF_OBJECTIDS);
if (devco & DEVCO_FROMID) {
devco |= DEVCO_TOID;
} else {
AssertF(devco & DEVCO_TOID);
devco |= DEVCO_FROMID;
}
}
hres = CDIDev_ConvertObjects(this->pdev, cdw, rgdwOut, devco);
} else {
/* Vacuous success */
hres = S_OK;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CDIEff | GetDirectionParameters |
*
* Retrieve information about the direction of an effect.
*
* If no direction has yet been set, then wipe out the
* direction pointer and erase the coordinate system.
*
* Always convert from the cached application coordinate
* system instead of the device coordinate system, in
* order to maximize fidelity.
*
* @cwrap PDE | this
*
* @parm LPDIEFFECT | peff |
*
* Structure to receive effect information.
*
* @parm DWORD | cAxes |
*
* Number of axes to return.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_GetDirectionParameters_(this, peff, cAxes, z, iarg) \
_CDIEff_GetDirectionParameters_(this, peff, cAxes) \
#endif
#define CDIEff_GetDirectionParameters(this, peff, cAxes, iarg) \
CDIEff_GetDirectionParameters_(this, peff, cAxes, s_szProc, iarg) \
void INTERNAL
CDIEff_GetDirectionParameters_(PDE this, LPDIEFFECT peff, DWORD cAxes,
LPCSTR s_szProc, int iarg)
{
AssertF(CDIDev_InCrit(this->pdev));
/*
* Make sure there are no non-coordinate bits in dwDirFlags.
* And validation should've made sure the app is asking for *something*.
*/
AssertF((this->dwDirFlags & ~DIEFF_COORDMASK) == 0);
AssertF(peff->dwFlags & DIEFF_COORDMASK);
AssertF(cAxes <= DIEFFECT_MAXAXES);
if (this->dwDirFlags) {
DWORD dieffRc;
LONG rgl[DIEFFECT_MAXAXES]; /* Holding buffer */
/*
* We must double-buffer in case the target is not big enough.
*/
/*
* Prefix does not like the lack of initialization of rgl (Manbugs 34566, Whistler 228280)
* but unfortunately we can't fix it without potentially breaking compatibility
* with some devices. See comment in CDIEff_CartToAngles() about this issue.
*/
dieffRc = CDIEff_ConvertDirection(
this->effDev.cAxes,
rgl, peff->dwFlags,
this->rglDirApp, this->dwDirFlags);
peff->dwFlags = (peff->dwFlags & ~DIEFF_COORDMASK) | dieffRc;
CopyMemory(peff->rglDirection, rgl, cbCdw(cAxes));
} else {
/*
* No direction set; vacuous success.
*/
RPF("Warning: %s: arg %d: Effect has no direction", s_szProc, iarg);
peff->rglDirection = 0;
peff->dwFlags &= ~DIEFF_COORDMASK;
}
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | GetParameters |
*
* Retrieve information about an effect.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm LPDIEFFECT | peff |
*
* Structure that receives effect information.
* The <e DIEFFECT.dwSize> field must be filled in by
* the application before calling this function.
*
* @parm DWORD | dwFlags |
*
* Zero or more <c DIEP_*> flags specifying which
* portions of the effect information is to be retrieved.
*
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has never had any effect parameters set into it.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid. Common errors include
* not setting the <e DIEFFECT.dwSize> field of the
* <t DIEFFECT> structure, passing invalid flags,
* or not setting up the fields in the <t DIEFFECT> structure
* properly in preparation for receiving the effect information.
*
*
*****************************************************************************/
STDMETHODIMP
CDIEff_GetParameters(PDIE pdie, LPDIEFFECT peff, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::GetParameters, (_ "ppx", pdie, peff, fl));
/*
* Note that we cannot use hresFullValidWritePxCb() because
* that will scramble the buffer, but we still need the values
* in it.
*/
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED( hres = ( IsBadReadPtr(&peff->dwSize, cbX(peff->dwSize)) ) ? E_POINTER : S_OK ) &&
( ( (peff->dwSize != cbX(DIEFFECT_DX5)) &&
SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb(peff, DIEFFECT_DX6, 1) ) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIEP_GETVALID, 2)) )
||
( SUCCEEDED(hres = hresFullValidWriteNoScramblePxCb(peff, DIEFFECT_DX5, 1)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIEP_GETVALID_DX5, 2)) )
) ) {
PDE this = _thisPvNm(pdie, def);
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = hresFullValidWritePeff(this, peff, fl, 1))) {
if (fl == 0) {
RPF("Warning: %s(dwFlags=0) is pretty useless",
s_szProc);
}
/*
* Assume everything is okay.
*/
hres = S_OK;
/*
* Pull out the effect parameters.
*/
if (fl & DIEP_DURATION) {
peff->dwDuration = this->effDev.dwDuration;
}
if (fl & DIEP_SAMPLEPERIOD) {
peff->dwSamplePeriod = this->effDev.dwSamplePeriod;
}
if (fl & DIEP_GAIN) {
peff->dwGain = this->effDev.dwGain;
}
if (fl & DIEP_STARTDELAY) {
peff->dwStartDelay = this->effDev.dwStartDelay;
}
if (fl & DIEP_TRIGGERBUTTON) {
peff->dwTriggerButton = this->effDev.dwTriggerButton;
if (peff->dwTriggerButton != DIEB_NOTRIGGER) {
hres = CDIEff_MapDwords(this, peff->dwFlags, 1,
&peff->dwTriggerButton,
&peff->dwTriggerButton,
DEVCO_BUTTON |
DEVCO_FROMID);
/*
* We should never allow a bad id to sneak in.
*/
AssertF(SUCCEEDED(hres));
if (FAILED(hres)) {
goto done;
}
}
}
if (fl & DIEP_TRIGGERREPEATINTERVAL) {
peff->dwTriggerRepeatInterval =
this->effDev.dwTriggerRepeatInterval;
}
if (fl & DIEP_TYPESPECIFICPARAMS) {
DWORD cb = this->effDev.cbTSP;
if (peff->cbTSP < this->effDev.cbTSP) {
cb = peff->cbTSP;
hres = DIERR_MOREDATA;
}
peff->cbTSP = this->effDev.cbTSP;
CopyMemory(peff->lpvTSP, this->effDev.lpvTSP, cb);
}
if (fl & DIEP_ENVELOPE) {
if (this->effDev.lpEnvelope) {
*peff->lpEnvelope = *this->effDev.lpEnvelope;
} else {
/*
* Zero out the envelope because apps are too
* *lazy* to check whether peff->lpEnvelope == 0;
* they're just going to peek at the envelope
* even if the effect doesn't have one.
*/
ZeroMemory(pvAddPvCb(peff->lpEnvelope,
cbX(peff->lpEnvelope->dwSize)),
cbX(DIENVELOPE) -
cbX(peff->lpEnvelope->dwSize));
peff->lpEnvelope = this->effDev.lpEnvelope;
}
}
/*
* Do axes and direction last because weird error
* codes can come out of here.
*/
if (fl & (DIEP_AXES | DIEP_DIRECTION)) {
DWORD cAxes = this->effDev.cAxes;
if (peff->cAxes < this->effDev.cAxes) {
cAxes = peff->cAxes;
peff->cAxes = this->effDev.cAxes;
hres = DIERR_MOREDATA;
}
peff->cAxes = this->effDev.cAxes;
if (fl & DIEP_AXES) {
HRESULT hresT;
hresT = CDIEff_MapDwords(this, peff->dwFlags, cAxes,
peff->rgdwAxes,
this->effDev.rgdwAxes,
DEVCO_AXIS |
DEVCO_FROMID);
if (FAILED(hresT)) {
RPF("ERROR: %s: arg %d: Axes not in data format",
s_szProc, 1);
hres = hresT;
goto done;
}
}
if (fl & DIEP_DIRECTION) {
CDIEff_GetDirectionParameters(this, peff, cAxes, 1);
}
}
}
done:;
CDIEff_LeaveCrit(this);
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidUnknownTsd |
*
* Verify that the buffer is a valid buffer for unknown
* type-specific data. Since we don't know what it is,
* the buffer is assumed valid because we can't validate it.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidUnknownTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
peff;
cAxes;
hres = S_OK;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidConstantTsd |
*
* Verify that the buffer is a valid
* <t DICONSTANTFORCE> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidConstantTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
if (peff->cbTypeSpecificParams == cbX(DICONSTANTFORCE)) {
hres = S_OK;
} else {
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidRampTsd |
*
* Verify that the buffer is a valid
* <t DIRAMPFORCE> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidRampTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
if (peff->cbTypeSpecificParams == cbX(DIRAMPFORCE)) {
hres = S_OK;
} else {
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidPeriodicTsd |
*
* Verify that the buffer is a valid
* <t DIPERIODIC> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidPeriodicTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
if (peff->cbTypeSpecificParams == cbX(DIPERIODIC)) {
hres = S_OK;
} else {
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidConditionTsd |
*
* Verify that the buffer is a valid
* <t DICONDITION> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidConditionTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
/*
* Conditions are weird. The size of the type-specific data
* must be equal to cAxes * cbX(DICONDITION) or equal to
* exactly one cbX(DICONDITION), depending on whether you want
* multiple conditions on multiple axes or a single condition
* rotated across multiple axes.
*
* Note that we do not enforce that the parameters are in range;
* this allows for "overgain"-type behaviors.
*/
if (peff->cbTypeSpecificParams == cbX(DICONDITION) ||
peff->cbTypeSpecificParams == cAxes * cbX(DICONDITION)) {
hres = S_OK;
} else {
RPF("IDirectInputEffect::SetParameters: "
"Size of type-specific data (%d) "
"not compatible with number of axes (%d)",
peff->cbTypeSpecificParams, cAxes);
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidCustomForceTsd |
*
* Verify that the buffer is a valid
* <t DICUSTOMFORCE> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidCustomForceTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
if (peff->cbTypeSpecificParams == cbX(DICUSTOMFORCE)) {
LPCDICUSTOMFORCE pcf = peff->lpvTypeSpecificParams;
if (pcf->cChannels == 0) {
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DICUSTOMFORCE.cChannels == 0 is invalid");
hres = E_INVALIDARG;
} else if (pcf->cSamples % pcf->cChannels != 0) {
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DICUSTOMFORCE.cSamples must be multiple of "
"DICUSTOMFORCE.cChannels");
hres = E_INVALIDARG;
} else if (IsBadReadPtr(pcf->rglForceData,
cbCxX((pcf->cSamples)*(pcf->cChannels), LONG))) {
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DICUSTOMFORCE.rglForceData invalid");
hres = E_INVALIDARG;
} else {
hres = S_OK;
}
} else {
hres = E_INVALIDARG;
}
return hres;
}
#if DIRECTINPUT_VERSION >= 0x0900
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidRandomTsd |
*
* Verify that the buffer is a valid
* <t DIRANDOM> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidRandomTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
if (peff->cbTypeSpecificParams == cbX(DIRANDOM)) {
hres = S_OK;
} else {
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidAbsoluteTsd |
*
* Verify that the buffer is a valid
* <t DIABSOLUTE> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidAbsoluteTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
/*
* Unlike other effects, "overgain" is not permitted for absolute effects.
*/
if (peff->cbTypeSpecificParams == cbX(DIABSOLUTE))
{
LPCDIABSOLUTE pabs = peff->lpvTypeSpecificParams;
if( fInOrder( -10000, pabs->lTarget, 10000 ) )
{
hres = S_OK;
}
else
{
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DIABSOLUTE.lTarget %d not in range -10000 to 100000",
pabs->lTarget );
hres = E_INVALIDARG;
}
} else {
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidBumpForceTsd |
*
* Verify that the buffer is a valid
* <t DIBUMPFORCE> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidBumpForceTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
cAxes;
if (peff->cbTypeSpecificParams == cbX(DIBUMPFORCE)) {
LPCDIBUMPFORCE pbf = peff->lpvTypeSpecificParams;
if (pbf->cChannels == 0) {
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DIBUMPFORCE.cChannels == 0 is invalid");
hres = E_INVALIDARG;
} else if (pbf->cSamples % pbf->cChannels != 0) {
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DIBUMPFORCE.cSamples must be multiple of "
"DIBUMPFORCE.cChannels");
hres = E_INVALIDARG;
} else if (IsBadReadPtr(pbf->rglForceData,
cbCxX(pbf->cSamples, LONG))) {
RPF("ERROR: IDirectInputEffect::SetParameters: "
"DIBUMPFORCE.rglForceData invalid");
hres = E_INVALIDARG;
} else {
hres = S_OK;
}
} else {
hres = E_INVALIDARG;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIEff_IsValidConditionExTsd |
*
* Verify that the buffer is a valid
* <t DICONDITIONEX> structure.
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The type-specific parameters have already been validated
* for access.
*
* @parm DWORD | cAxes |
*
* Number of axes associated with the type-specific parameters.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_IsValidConditionExTsd(LPCDIEFFECT peff, DWORD cAxes)
{
HRESULT hres;
/*
* Extended conditions, like conditions are weird.
* The size of the type-specific data
* must be equal to cAxes * cbX(DICONDITIONEX) or equal to
* exactly one cbX(DICONDITIONEX), depending on whether you want
* multiple conditions on multiple axes or a single condition
* rotated across multiple axes.
*
* Note that we do not enforce that the parameters are in range;
* this allows for "overgain"-type behaviors.
*/
if (peff->cbTypeSpecificParams == cbX(DICONDITIONEX) ||
peff->cbTypeSpecificParams == cAxes * cbX(DICONDITIONEX)) {
hres = S_OK;
} else {
RPF("IDirectInputEffect::SetParameters: "
"Size of type-specific data (%d) "
"not compatible with number of axes (%d)",
peff->cbTypeSpecificParams, cAxes);
hres = E_INVALIDARG;
}
return hres;
}
#endif /* DIRECTINPUT_VERSION >= 0x0900 */
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | hresFullValidPeff |
*
* Verify that the recipient buffer contains valid information.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information. It has
* already been validate in size and for general readability.
*
* @parm DWORD | fl |
*
* Zero or more <c DIEP_*> flags specifying which
* portions of the effect information should be validated.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
#ifndef XDEBUG
#define hresFullValidPeff_(this, peff, fl, z, i) \
_hresFullValidPeff_(this, peff, fl) \
#endif
#define hresFullValidPeff(this, peff, fl, iarg) \
hresFullValidPeff_(this, peff, fl, s_szProc, iarg) \
HRESULT INTERNAL
hresFullValidPeff_(PDE this, LPCDIEFFECT peff, DWORD fl,
LPCSTR s_szProc, int iarg)
{
HRESULT hres;
DWORD cAxes;
AssertF(CDIDev_InCrit(this->pdev));
/*
* You can't set the parameters of a nonexistent effect.
*/
if (!this->fInitialized) {
hres = DIERR_NOTINITIALIZED;
goto done;
}
/*
* Flags are always validated.
*/
if (peff->dwFlags & ~DIEFF_VALID) {
RPF("ERROR %s: arg %d: Invalid flags specific parms in DIEFFECT",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
/*
* If setting something that requires object ids or offsets,
* make sure the caller picks one or the other.
*/
if (fl & DIEP_USESOBJECTS) {
switch (peff->dwFlags & DIEFF_OBJECTMASK) {
case DIEFF_OBJECTIDS:
case DIEFF_OBJECTOFFSETS:
break;
default:
RPF("ERROR %s: arg %d: Must specify one of "
"DIEFF_OBJECTIDS or DIEFF_OBJECTOFFSETS", s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
}
/*
* If setting something that requires direction coordinates,
* make sure the caller picks exactly one.
*/
if (fl & DIEP_USESCOORDS) {
switch (peff->dwFlags & DIEFF_COORDMASK) {
case DIEFF_CARTESIAN:
case DIEFF_SPHERICAL:
break;
/*
* Polar coordinates mandate two (and only two) axes.
*/
case DIEFF_POLAR:
if (peff->cAxes != 2) {
RPF("ERROR %s: arg %d: DIEFF_POLAR requires DIEFFECT.cAxes=2",
s_szProc, 1);
hres = E_INVALIDARG;
goto done;
}
break;
default:
RPF("ERROR %s: arg %d: Must specify one of "
"DIEFF_CARTESIAN, DIEFF_POLAR, or DIEFF_SPHERICAL",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
}
/*
* DIEP_DURATION
* DIEP_SAMPLEPERIOD
* DIEP_GAIN
* DIEP_TRIGGERBUTTON
* - Simple dwords. No extra validation needed.
*/
/*
* DIEP_AXES
* DIEP_DIRECTION
* - The buffers must be of necessary size.
*
* We will validate the other goo later, because there
* are annoying interactions between them.
*/
AssertF(fLeqvFF(this->effDev.cAxes == 0, this->diepUnset & DIEP_AXES));
cAxes = this->effDev.cAxes;
if (fl & (DIEP_AXES | DIEP_DIRECTION)) {
/*
* The number of axes had better not be zero.
*/
if (peff->cAxes == 0) {
RPF("ERROR %s: arg %d: DIEFFECT.cAxes = 0 is invalid",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
/*
* And it better not be too big either.
*/
if (peff->cAxes > DIEFFECT_MAXAXES) {
RPF("ERROR %s: arg %d: DIEFFECT.cAxes = %d is too large (max %d)",
s_szProc, iarg, peff->cAxes, DIEFFECT_MAXAXES);
hres = E_INVALIDARG;
goto done;
}
if (fl & DIEP_AXES) {
/*
* If the axes have already been set (which we know because
* this->effDev.cAxes will be nonzero), then don't
* let the caller change them.
*/
if (this->effDev.cAxes) {
RPF("ERROR %s: arg %d: Cannot change axes once set",
s_szProc, iarg);
hres = DIERR_ALREADYINITIALIZED;
goto done;
}
cAxes = peff->cAxes;
if (IsBadReadPtr(peff->rgdwAxes, cbCdw(peff->cAxes))) {
RPF("ERROR %s: arg %d: Invalid rgdwAxes in DIEFFECT",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
}
if (fl & DIEP_DIRECTION) {
/*
* We want to disallow cAxes == 0 as well,
* but we get that for free because
* peff->cAxes != cAxes, and peff->cAxes is already
* validated as nonzero.
*/
if (peff->cAxes != cAxes) {
if (cAxes) {
RPF("ERROR %s: arg %d: Wrong number of DIEFFECT.cAxes",
s_szProc, 1);
} else {
RPF("ERROR %s: arg %d: "
"Must set number of axes before directions", s_szProc);
}
hres = E_INVALIDARG;
goto done;
}
/*
* Direction validation should've already checked above.
*/
AssertF(fLimpFF(peff->dwFlags & DIEFF_POLAR, peff->cAxes == 2));
if (IsBadReadPtr(peff->rglDirection, cbCdw(peff->cAxes))) {
RPF("ERROR %s: arg %d: Invalid rglDirection in DIEFFECT",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
}
}
/*
* DIEP_TYPESPECIFICPARAMS
* - Validate that the buffer is valid
* and passes type-specific tests.
*
* This must be done after axes so we know how many
* axes there are.
*/
AssertF(this->hresValidTsd);
if (fl & DIEP_TYPESPECIFICPARAMS) {
hres = hresFullValidReadPvCb(peff->lpvTypeSpecificParams,
peff->cbTypeSpecificParams, iarg);
if (FAILED(hres)) {
RPF("ERROR %s: arg %d: Invalid pointer in "
"DIEFFECT.lpvTypeSpecificParams",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
hres = this->hresValidTsd(peff, cAxes);
if (FAILED(hres)) {
RPF("ERROR %s: arg %d: Invalid type-specific data",
s_szProc, iarg);
goto done;
}
}
/*
* DIEP_ENVELOPE - The pointer must be valid if present.
*/
if ((fl & DIEP_ENVELOPE) &&
peff->lpEnvelope &&
FAILED(hres = hresFullValidReadPxCb(peff->lpEnvelope,
DIENVELOPE, iarg))) {
RPF("ERROR %s: arg %d: Invalid lpEnvelope in DIEFFECT",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
hres = S_OK;
done:;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | TryTriggerButton |
*
* Set information about the trigger button for an effect into the
* temporary buffer.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_TryTriggerButton_(this, peff, z, iarg) \
_CDIEff_TryTriggerButton_(this, peff) \
#endif
#define CDIEff_TryTriggerButton(this, peff, iarg) \
CDIEff_TryTriggerButton_(this, peff, s_szProc, iarg) \
STDMETHODIMP
CDIEff_TryTriggerButton_(PDE this, LPCDIEFFECT peff, LPCSTR s_szProc, int iarg)
{
HRESULT hres;
AssertF(CDIDev_InCrit(this->pdev));
/*
* We can copy directly in, because if something goes wrong,
* we just throw away effTry without damaging effDev.
*/
this->effTry.dwTriggerButton = peff->dwTriggerButton;
if (fLimpFF(this->effTry.dwTriggerButton != DIEB_NOTRIGGER,
SUCCEEDED(hres = CDIEff_MapDwords(this, peff->dwFlags, 1,
&this->effTry.dwTriggerButton,
&this->effTry.dwTriggerButton,
DEVCO_BUTTON |
DEVCO_FFEFFECTTRIGGER |
DEVCO_TOID)))) {
hres = S_OK;
} else {
RPF("ERROR %s: Invalid button identifier/offset "
"or button is not DIEB_NOTRIGGER",
s_szProc);
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | TryAxis |
*
* Set information about the axes of an effect into the
* temporary buffer. Note that since you can't change the
* axes once they've been set, we can put our try directly
* into the final buffer.
*
* The only tricky thing is making sure no axes are repeated
* in the array.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_TryAxis_(this, peff, z, iarg) \
_CDIEff_TryAxis_(this, peff) \
#endif
#define CDIEff_TryAxis(this, peff, iarg) \
CDIEff_TryAxis_(this, peff, s_szProc, iarg) \
STDMETHODIMP
CDIEff_TryAxis_(PDE this, LPCDIEFFECT peff, LPCSTR s_szProc, int iarg)
{
HRESULT hres;
UINT idw;
AssertF(CDIDev_InCrit(this->pdev));
/*
* You can change the axes only once. Therefore, rgdwAxes
* always points to this->rgdwAxes.
*/
AssertF(this->effDev.cAxes == 0);
AssertF(this->effTry.cAxes == 0);
AssertF(this->effDev.rgdwAxes == this->effTry.rgdwAxes);
AssertF(this->effTry.rgdwAxes == this->rgdwAxes);
hres = CDIEff_MapDwords(this, peff->dwFlags, peff->cAxes,
this->effTry.rgdwAxes, peff->rgdwAxes,
DEVCO_AXIS | DEVCO_FFACTUATOR | DEVCO_TOID);
if (FAILED(hres)) {
RPF("ERROR %s: Invalid axis identifiers/offsets"
"or axes are not all DIDFT_FFACTUATOR", s_szProc);
goto done;
}
/*
* Make sure there are no dups in the axis list.
*
* The outer loop starts at 1 because the 0'th axis
* can't possibly conflict with any others.
*/
for (idw = 1; idw < peff->cAxes; idw++) {
DWORD idwT;
for (idwT = 0; idwT < idw; idwT++) {
if (this->effTry.rgdwAxes[idw] == this->effTry.rgdwAxes[idwT]) {
RPF("ERROR %s: arg %d: Duplicate axes in axis array",
s_szProc, iarg);
hres = E_INVALIDARG;
goto done;
}
}
}
this->effTry.cAxes = peff->cAxes;
done:;
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIEff | TryDirection |
*
* Set information about the direction of an effect into the
* temporary buffer.
*
* This is particularly gruesome, because we need to keep
* two sets of books: The values passed by the app (which
* we regurgitate back when queried) and the values passed
* to the driver.
*
* We must keep two sets of books, because I just know
* that some apps are going to get confused if the
* parameters they read back do not <y exactly> match
* the values they set in. For example, they might
* read the value, subtract five, and write it back.
* Due to rounding, <y n> and <y n>-5 have the same
* value in the driver, so if we translated down and
* back, the value wouldn't change, and the app would
* get stuck in an infinite loop.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
*
* @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>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
#ifndef XDEBUG
#define CDIEff_TryDirection_(this, peff, z, iarg) \
_CDIEff_TryDirection_(this, peff) \
#endif
#define CDIEff_TryDirection(this, peff, iarg) \
CDIEff_TryDirection_(this, peff, s_szProc, iarg) \
STDMETHODIMP
CDIEff_TryDirection_(PDE this, LPCDIEFFECT peff, LPCSTR s_szProc, int iarg)
{
HRESULT hres;
DWORD dieffRc;
AssertF(CDIDev_InCrit(this->pdev));
/*
* These should've been caught by validation.
*/
AssertF(this->effTry.cAxes);
AssertF(peff->cAxes == this->effTry.cAxes);
AssertF(fLimpFF(peff->dwFlags & DIEFF_POLAR, peff->cAxes == 2));
/*
* Translate the coordinates into device coordinates.
*/
AssertF((this->dwCoords & ~DIEFF_COORDMASK) == 0);
AssertF(this->dwCoords);
this->effTry.rglDirection = this->rglDirTry;
dieffRc = CDIEff_ConvertDirection(
this->effTry.cAxes,
this->rglDirTry, this->dwCoords,
peff->rglDirection, peff->dwFlags & DIEFF_COORDMASK);
AssertF(dieffRc);
this->effTry.dwFlags = (this->effTry.dwFlags & ~DIEFF_COORDMASK) | dieffRc;
hres = S_OK;
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method void | CDIEff | TryParameters |
*
* Build the Try structure based on the new parameters.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* The original effect structure passed by the application.
*
* @parm DWORD | fl |
*
* The <c DIEP_*> flags which specify what changed.
*
*****************************************************************************/
HRESULT INTERNAL
CDIEff_TryParameters(PDE this, LPCDIEFFECT peff, DWORD fl)
{
HRESULT hres = S_OK;
EnterProcR(IDirectInputEffect::SetParameters, (_ "ppx", this, peff, fl));
AssertF(this->lpvTSP == 0);
/*
* Copy the current device parameters so we
* can modify the copy without damaging the original.
*/
this->effTry = this->effDev;
/*
* Install the appropriate effect parameters.
*/
if (fl & DIEP_DURATION) {
this->effTry.dwDuration = peff->dwDuration;
}
if (fl & DIEP_SAMPLEPERIOD) {
this->effTry.dwSamplePeriod = peff->dwSamplePeriod;
}
if (fl & DIEP_GAIN) {
this->effTry.dwGain = peff->dwGain;
}
if (fl & DIEP_STARTDELAY) {
this->effTry.dwStartDelay = peff->dwStartDelay;
}
if (fl & DIEP_TRIGGERBUTTON) {
hres = CDIEff_TryTriggerButton(this, peff, 1);
if (FAILED(hres)) {
goto done;
}
}
if (fl & DIEP_TRIGGERREPEATINTERVAL) {
this->effTry.dwTriggerRepeatInterval =
peff->dwTriggerRepeatInterval;
}
if (fl & DIEP_TYPESPECIFICPARAMS) {
this->effTry.cbTSP = peff->cbTSP;
this->effTry.lpvTSP = peff->lpvTSP;
/*
* Preallocate memory to hold the type-specific parameters
* to make sure we can proceed on success.
*/
if (this->effDev.cbTSP != this->effTry.cbTSP) {
hres = AllocCbPpv(this->effTry.cbTSP, &this->lpvTSP);
if (FAILED(hres)) {
goto done;
}
}
}
/*
* Must do axes before directions because directions
* depends on the number of axes.
*/
if (fl & DIEP_AXES) {
hres = CDIEff_TryAxis(this, peff, 1);
if (FAILED(hres)) {
goto done;
}
}
if (fl & DIEP_DIRECTION) {
hres = CDIEff_TryDirection(this, peff, 1);
if (FAILED(hres)) {
goto done;
}
}
if (fl & DIEP_ENVELOPE) {
this->effTry.lpEnvelope = peff->lpEnvelope;
}
done:;
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method void | CDIEff | SaveTry |
*
* A Try'd effect worked. Save its parameters in the
* driver parameter cache.
*
* @cwrap PDE | this
*
* @parm LPCDIEFFECT | peff |
*
* The original effect structure passed by the application.
*
* @parm DWORD | fl |
*
* The <c DIEP_*> flags which specify what changed.
*
*****************************************************************************/
void INTERNAL
CDIEff_SaveTry(PDE this, LPCDIEFFECT peff, DWORD fl)
{
/*
* For the easy stuff, just copy them blindly.
* It doesn't hurt to copy something that didn't change.
*/
this->effDev.dwDuration = this->effTry.dwDuration;
this->effDev.dwSamplePeriod = this->effTry.dwSamplePeriod;
this->effDev.dwGain = this->effTry.dwGain;
this->effDev.dwTriggerButton = this->effTry.dwTriggerButton;
this->effDev.dwTriggerRepeatInterval = this->effTry.dwTriggerRepeatInterval;
this->effDev.dwStartDelay = this->effTry.dwStartDelay;
/*
* Axes count as "easy" because CDIEff_TryAxes put the
* axis info directly into this->rgdwAxes.
*/
this->effDev.cAxes = this->effTry.cAxes;
/*
* Now the hard parts: The things that require
* memory allocation or block copying.
*/
if (fl & DIEP_TYPESPECIFICPARAMS) {
if (this->effDev.cbTSP == this->effTry.cbTSP) {
AssertF(this->lpvTSP == 0);
} else {
AssertF(this->lpvTSP);
this->effDev.cbTSP = this->effTry.cbTSP;
FreePpv(&this->effDev.lpvTSP);
this->effDev.lpvTSP = this->lpvTSP;
this->lpvTSP = 0;
}
CopyMemory(this->effDev.lpvTSP, this->effTry.lpvTSP,
this->effTry.cbTSP);
}
if (fl & DIEP_DIRECTION) {
/*
* Save the app coordinate and the coordinate system into our cache.
*/
this->dwDirFlags = peff->dwFlags & DIEFF_COORDMASK;
CopyMemory(this->rglDirApp, peff->rglDirection,
cbCdw(this->effDev.cAxes));
/*
* And propagate the Try'd coordinates into the Drv coordinates.
*/
this->effDev.dwFlags= this->effTry.dwFlags;
CopyMemory(this->rglDirDev, this->rglDirTry, cbX(this->rglDirTry));
}
if (fl & DIEP_ENVELOPE) {
if (this->effTry.lpEnvelope) {
this->effDev.lpEnvelope = &this->env;
this->env = *this->effTry.lpEnvelope;
} else {
this->effDev.lpEnvelope = 0;
}
}
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | SetParameters |
*
* Set information about an effect.
*
* It is valid to update the parameters of an effect while
* it is playing. The new parameters take effect immediately
* upon download if the device supports dynamic updating of effect
* parameters. The <mf IDirectInputEffect::SetParameters>
* method automatically downloads the effect, but this behavior
* can be suppressed by setting the <c DIEP_NODOWNLOAD> flag.
*
* If automatic download has been suppressed, then you can
* manually download the effect by invoking the
* <mf IDirectInputEffect::Download> method.
*
* If the effect is playing while the parameters are changed,
* then the new parameters take effect as if they were the
* parameters when the effect started.
*
* For example, suppose a periodic effect with a duration
* of three seconds is started.
* After two seconds, the direction of the effect is changed.
* The effect will then continue for one additional second
* in the new direction. The envelope, phase, amplitude,
* and other parameters of the effect continue smoothly
* as if the direction had not changed.
*
* In the same scenario, if after two seconds, the duration
* of the effect were changed to 1.5 seconds, then the effect
* would stop, because two seconds have already elapsed from
* the beginning of effect playback.
*
* Two additional flags control the download behavior.
* The <c DIEP_START> flag indicates that the effect should
* be started (or restarted if it is currently playing) after
* the parameters are updated. By default, the play state
* of the effect is not altered.
*
* Normally, if the driver cannot update the parameters
* of a playing effect, it is permitted to stop the effect,
* update the parameters, and then restart the effect.
* Passing the <c DIEP_NORESTART> flag suppresses this
* behavior. If the driver cannot update the parameters
* of an effect while it is playing, the error code
* <c DIERR_EFFECTPLAYING> is returned and the parameters
* are not updated.
*
* To summarize the behavior of the three flags that control
* download and playback behavior:
*
* If <c DIEP_NODOWNLOAD> is set, then the effect parameters
* are updated but not downloaded to the device.
*
* Otherwise, the <c DIEP_NODOWNLOAD> flag is clear.
*
* If the <c DIEP_START> flag is set, then the effect
* parameters are updated and downloaded to the device,
* and the effect is started,
* as if the <mf IDirectInputEffect::Start> method were
* called. Combining the update with <c DIEP_START> is
* slightly faster than calling
* <mf IDirectInputEffect::Start> separately, because
* it requires less information to be transmitted to the
* device.
*
* Otherwise, both the <c DIEP_NODOWNLOAD> and
* <c DIEP_START> flags are clear.
*
* If the effect is not playing, then the parameters
* are updated and downloaded to the device.
*
* Otherwise, both the <c DIEP_NODOWNLOAD> and
* <c DIEP_START> flags are clear, and the effect is
* already playing.
*
* If the parameters of the effect can be updated
* "on the fly", then the update is so performed.
*
* Otherwise, both the <c DIEP_NODOWNLOAD> and
* <c DIEP_START> flags are clear, and the effect is
* already playing, and the parameters cannot be updated
* while the effect is playing.
*
* If the <c DIEP_NORESTART> flag is set, then the
* error code <c DIERR_EFFECTPLAYING> is returned.
*
* Otherwise, all three of the flags
* <c DIEP_NODOWNLOAD>, <c DIEP_START> and
* <c DIEP_NORESTART> are clear, and the effect is
* already playing, and the parameters cannot be
* updated while the effect is playing.
*
* The effect is stopped, the parameters updated, and
* the effect is restarted. The return code is
* <c DI_EFFECTRESTARTED>.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm LPCDIEFFECT | peff |
*
* Structure that contains effect information.
* The <e DIEFFECT.dwSize> field must be filled in by
* the application before calling this function, as well
* as any fields specified by corresponding bits in
* the <p dwFlags> parameter.
*
* @parm DWORD | dwFlags |
*
* Zero or more <c DIEP_*> flags specifying which
* portions of the effect information is to be set
* and how the downloading of the effect parameters
* should be handled.
*
* @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 DI_NOTDOWNLOADED>: The parameters of the effect were
* successfully
* updated, but the effect could not be downloaded because
* the associated device is not acquired in exclusive mode.
* Note that this is a success code, because the
* parameters were successfully updated.
*
* <c DI_TRUNCATED>: The parameters of the effect were
* successfully updated,
* 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
* parameters were successfully updated.
*
* <c DI_EFFECTRESTARTED>: The parameters of the effect
* were successfully updated, and the effect was restarted.
* Note that this is a success code, because the
* parameters were successfully updated.
*
* <c DI_TRUNCATEDANDRESTARTED>: The parameters of the effect
* were successfully updated, but some of the effect parameters
* were truncated, and the effect was restarted. This code
* combines <c DI_TRUNCATED> and <c DI_EFFECTRESTARTED>.
* Note that this is a success code, because the
* parameters were successfully updated.
*
* <c DIERR_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not been initialized.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid.
*
* <c DIERR_EFFECTPLAYING>: The parameters were not updated
* because
* the device does not support updating an effect while
* it is still playing, and the <c DIEP_NORESTART> flag was
* passed, prohibiting the driver from stopping the effect,
* updating its parameters, and restarting it.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_SetParameters(PDIE pdie, LPCDIEFFECT peff, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::SetParameters, (_ "ppx", pdie, peff, fl));
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresFullValidReadPxCb2(peff,
DIEFFECT_DX6,
DIEFFECT_DX5, 1)) &&
SUCCEEDED(hres = hresFullValidFl(fl, ((peff->dwSize == cbX(DIEFFECT_DX6))
? DIEP_SETVALID : DIEP_SETVALID_DX5 ), 2))) {
PDE this = _thisPvNm(pdie, def);
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = hresFullValidPeff(this, peff, fl, 1))) {
BOOL fChangeEmulatedStartDelay = FALSE;
/*
* Note that if fl == 0 (or nearly so),
* TryParameters doesn't do anything,
* albeit rather inefficiently.
*/
hres = CDIEff_TryParameters(this, peff, fl);
if (FAILED(hres)) {
goto done;
}
/*
* Special case for DIEP_STARTDELAY.
*/
if (fl & DIEP_STARTDELAY)
{
if (this->dEffAttributes.dwStaticParams & DIEP_STARTDELAY)
{
/*
* Driver supports it, so don't worry.
*/
;
}
else
{
/*
* Driver doesn't support it.
* Take start delay out...
*/
fl &= ~(DIEP_STARTDELAY);
this->diepUnset &= ~(DIEP_STARTDELAY);
/*
* ...but remember that we may need to do something
*/
fChangeEmulatedStartDelay = ( this->effDev.dwStartDelay != this->effTry.dwStartDelay );
if (fl == 0)
{
hres = DI_OK;
goto save;
}
}
}
/*
* Now pass the effTry to the device driver for
* final validation.
*
* Passing fl=0 is a really slow way of downloading
* the effect, except that we return DI_DOWNLOADSKIPPED
* instead of an error code if the device is not exclusive
* acquired.
*
* Passing fl=DIEP_NODOWNLOAD is a really slow NOP.
*
* Note that inability to download due to lack of
* proper acquisition is not an error, merely a warning.
*/
hres = CDIEff_DownloadWorker_(this, &this->effTry, fl, 0);
AssertF(hres != DIERR_NOTDOWNLOADED);
/*
* If the driver approves, then make the changes permanent.
* but first a check on the emulated driver
*/
save:;
if( SUCCEEDED(hres) )
{
if( fChangeEmulatedStartDelay )
{
/*
* The start delay for parameter has been changed, so
* any future iteration is OK also the driver has
* SUCCEEDED any other changes.
*/
if( this->dwMessage != EFF_PLAY )
{
/*
* We're not in the delay, so declare everything OK.
*/
}
else
{
/*
* If the download was skipped don't bother.
*/
if( hres != DI_DOWNLOADSKIPPED )
{
if( fl & DIEP_NORESTART )
{
/*
* We don't support changing the delay during the
* delay. Since the driver has already had its
* parameters changed, only fail this if the
* delay was the only change requested.
*/
if( fl == 0 )
{
hres = DIERR_EFFECTPLAYING;
}
}
else
{
/*
* Since we don't support modifying the delay
* whilst we're in it, restart with the new
* delay.
* If we were being really smart, we could
* try to adjust the delay without restarting.
*/
if( this->hEventDelete && this->hEventGeneral )
{
this->dwMessage = EFF_PLAY;
ResetEvent(this->hEventGeneral);
SetEvent(this->hEventGeneral);
if( ( hres & ~DI_TRUNCATEDANDRESTARTED ) == 0 )
{
hres |= DI_EFFECTRESTARTED;
}
else if( hres == DI_NOEFFECT )
{
hres = DI_EFFECTRESTARTED;
}
}
else
{
AssertF( !"Effect synchronization event(s) NULL" );
}
}
}
}
}
else
{
/*
* If there was no change to an emulated start delay then
* the result we have is already what we need.
*/
}
}
if (SUCCEEDED(hres)) {
this->diepUnset &= ~fl; /* No longer unset */
/*
* If we didn't download, then the parameters are
* dirty and need to be downloaded later.
*/
if (hres == DI_DOWNLOADSKIPPED) {
this->diepDirty |= (fl & DIEP_ALLPARAMS);
}
CDIEff_SaveTry(this, peff, fl); /* Save permanently */
}
done:;
FreePpv(&this->lpvTSP);
}
CDIEff_LeaveCrit(this);
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffect | RealStart |
*
* Actually begin playing an effect. The parent device must
* be acquired.
*
* If the effect is already playing, then it is restarted
* from the beginning.
*
* If the effect has not been downloaded or has been
* modified since its last download, then it will be
* downloaded before being started. This default
* behavior can be suppressed by passing the
* <c DIES_NODOWNLOAD> flag.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm DWORD | dwIterations |
*
* Number of times to play the effect in sequence.
* The envelope is re-articulated with each iteration.
*
* To play the effect exactly once, pass 1.
*
* To play the effect repeatedly until explicitly stopped,
* pass <c INFINITE>.
*
* To play the effect until explicitly stopped without
* re-articulating the envelope, modify the effect
* parameters via <mf IDirectInputEffect::SetParameters>
* and change its <e DIEFFECT.dwDuration> to <c INFINITE>.
*
* @parm DWORD | dwFlags |
*
* Flags that describe how the effect should be played
* by the device. It can be zero or more of the
* <c DIES_*> flags.
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not been initialized or no effect parameters have been
* set.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_RealStart(PDIE pdie, DWORD dwcLoop, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Start, (_ "ppx", pdie, dwcLoop, fl));
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIES_VALID, 2))) {
PDE this = _thisPvNm(pdie, def);
if( SUCCEEDED( hres= (IsBadReadPtr(this, cbX(this))) ? E_POINTER : S_OK ) )
{
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = CDIEff_CanAccess(this))) {
if (fl & DIES_NODOWNLOAD) {
/*
* App wants fine control. Let him have it.
*/
hres = IDirectInputEffectShepherd_StartEffect(
this->pes, &this->sh, fl & DIES_DRIVER, dwcLoop);
} else {
/*
* App wants us to do the work. First thing to do
* is see if the effect needs to be downloaded.
*
* SyncShepHandle checks if the effect is downloaded.
*/
hres = CDIEff_SyncShepHandle(this);
if (this->diepDirty == 0 && this->sh.dwEffect) {
/*
* Effect is clean and downloaded.
* Just start it normally.
*/
hres = IDirectInputEffectShepherd_StartEffect(
this->pes, &this->sh,
fl & DIES_DRIVER, dwcLoop);
} else {
/*
* Effect needs to be downloaded. We can
* optimize it if no special flags are set
* and the loop count is exactly unity.
*/
if (fl == 0 && dwcLoop == 1) {
hres = CDIEff_DownloadWorker(this, &this->effDev,
DIEP_START);
} else {
/*
* Cannot optimize; must do separate download
* followed by Start.
*/
hres = CDIEff_DownloadWorker(this, &this->effDev, 0);
if (SUCCEEDED(hres)) {
hres = IDirectInputEffectShepherd_StartEffect(
this->pes, &this->sh,
fl & DIES_DRIVER, dwcLoop);
}
}
}
}
}
CDIEff_LeaveCrit(this);
}
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method DWORD | WINAPI | CDIEff_ThreadProc |
*
* Used to simulate dwStartDelay for drivers that do not support it.
* Begin playing an effect that has already been downloaded. The parent device must
* be acquired.
*
* If the effect is already playing, then it is restarted
* from the beginning.
*
* If the effect has not been downloaded or has been
* modified since its last download, then it will be
* downloaded before being started. This default
* behavior can be suppressed by passing the
* <c DIES_NODOWNLOAD> flag.
*
* After starting the effect, kills the timer whose event activated CDIEff_TimerProc
*
*
* @parm LPVOID | lpParameter |
*
* LPDIRECTINPUTEFFECT pointer.
*
* @returns
*
* 0 if succeeded in starting the effect, or if the effect has been deleted by the app.
* -1 otherwise
*
*****************************************************************************/
DWORD WINAPI CDIEff_ThreadProc(LPVOID lpParameter)
{
LPDIRECTINPUTEFFECT pdie = (LPDIRECTINPUTEFFECT) lpParameter;
HRESULT hres = E_FAIL;
DWORD dwWait;
HANDLE hArray[2];
BOOL startCalled = FALSE;
PDE this = _thisPvNm(pdie, def);
if( SUCCEEDED( hres= (IsBadReadPtr(this, cbX(this))) ? E_POINTER : S_OK ) )
{
if( this->hEventDelete != NULL && this->hEventThreadDead != NULL )
{
hArray[0] = this->hEventDelete;
hArray[1] = this->hEventGeneral;
ResetEvent( this->hEventThreadDead );
/*
* Wait till the timeout expires, or till one of the events happens --
* the effect is deleted by the app (hEventDelete) or the effect is started (hEventStart),
* or the effect is stopped (hEventStop).
*/
dwWait = WAIT_TIMEOUT;
while (dwWait != WAIT_OBJECT_0 && dwWait != WAIT_FAILED)
{
if (dwWait == WAIT_TIMEOUT)
{
if (startCalled)
{
/*
* Start have been called, and timeout has expired.
* Start the effect. And wait again.
*/
hres = CDIEff_RealStart(pdie, this->dwcLoop, this->dwFlags);
startCalled = FALSE;
this->dwMessage = EFF_DEFAULT;
}
}
else
{
if (dwWait == (WAIT_OBJECT_0 + 1))
{
/*
* App called Start on the effect.
* Set flag and start waiting anew.
*/
if (this->dwMessage == EFF_PLAY)
{
if ((this->effDev).dwStartDelay/1000 == 0)
{
/*
* If time delay is 0 ms, start immediately.
*/
hres = CDIEff_RealStart(pdie, this->dwcLoop, this->dwFlags);
startCalled = FALSE;
this->dwMessage = EFF_DEFAULT;
}
else
{
startCalled = TRUE;
}
}
else
{
if (this->dwMessage == EFF_STOP)
{
startCalled = FALSE;
this->dwMessage = EFF_DEFAULT;
}
}
ResetEvent(this->hEventGeneral);
}
}
/*
* And wait again.
*/
if (startCalled == TRUE) {
dwWait = WaitForMultipleObjects(2, hArray, FALSE, (this->effDev).dwStartDelay/1000);
} else {
dwWait = WaitForMultipleObjects(2, hArray, FALSE, INFINITE);
}
}
SetEvent( this->hEventThreadDead );
}
/*
* App has deleted the effect.
* Exit.
*/
hres = DI_OK;
}
if (SUCCEEDED(hres))
return 0;
else
return -1;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Start |
*
* Begin playing an effect. The parent device must
* be acquired.
*
* If the effect is already playing, then it is restarted
* from the beginning.
*
* If the effect has not been downloaded or has been
* modified since its last download, then it will be
* downloaded before being started. This default
* behavior can be suppressed by passing the
* <c DIES_NODOWNLOAD> flag.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm DWORD | dwIterations |
*
* Number of times to play the effect in sequence.
* The envelope is re-articulated with each iteration.
*
* To play the effect exactly once, pass 1.
*
* To play the effect repeatedly until explicitly stopped,
* pass <c INFINITE>.
*
* To play the effect until explicitly stopped without
* re-articulating the envelope, modify the effect
* parameters via <mf IDirectInputEffect::SetParameters>
* and change its <e DIEFFECT.dwDuration> to <c INFINITE>.
*
* @parm DWORD | dwFlags |
*
* Flags that describe how the effect should be played
* by the device. It can be zero or more of the
* <c DIES_*> flags.
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not been initialized or no effect parameters have been
* set.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_Start(PDIE pdie, DWORD dwcLoop, DWORD fl)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Start, (_ "ppx", pdie, dwcLoop, fl));
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresFullValidFl(fl, DIES_VALID, 2))) {
PDE this = _thisPvNm(pdie, def);
if( SUCCEEDED( hres= (IsBadReadPtr(this, cbX(this))) ? E_POINTER : S_OK ) )
{
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = CDIEff_CanAccess(this))) {
this->dwcLoop = dwcLoop;
this->dwFlags = fl;
if (this->hThread == NULL)
hres = CDIEff_RealStart(pdie, dwcLoop, fl);
else
{
/*
* Activate the thread's waiting period
*/
hres = CDIEff_DownloadWorker(this, &this->effDev, 0);
if (this->hEventGeneral != NULL)
{
this->dwMessage = EFF_PLAY;
ResetEvent(this->hEventGeneral);
SetEvent(this->hEventGeneral);
}
}
}
CDIEff_LeaveCrit(this);
}
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Stop |
*
* Stop playing an effect. The parent device must
* be acquired.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not been initialized or no effect parameters have been
* set.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_Stop(PDIE pdie)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Stop, (_ "p", pdie));
if (SUCCEEDED(hres = hresPv(pdie))) {
PDE this = _thisPvNm(pdie, def);
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = CDIEff_CanAccess(this))) {
hres = IDirectInputEffectShepherd_StopEffect(this->pes, &this->sh);
}
if (this->hEventGeneral != NULL)
{
this->dwMessage = EFF_STOP;
ResetEvent(this->hEventGeneral);
SetEvent(this->hEventGeneral);
}
CDIEff_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | GetEffectStatus |
*
* Retrieves the status of an effect.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm LPDWORD | pdwFlags |
*
* Receives the status flags for the effect. It may
* consist of zero or more <c DIEGES_*> flag values.
*
* @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_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not been initialized or no effect parameters have been
* set.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: At least one
* of the parameters is invalid.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_GetEffectStatus(PDIE pdie, LPDWORD pdwOut)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Stop, (_ "p", pdie));
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresFullValidPcbOut(pdwOut, cbX(*pdwOut), 1))) {
PDE this = _thisPvNm(pdie, def);
CAssertF(DEV_STS_EFFECT_RUNNING == DIEGES_PLAYING);
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = CDIEff_CanAccess(this))) {
/*
* Check the dwMessage first --
* if it says PLAYING, report DIEGES_PLAYING
*/
if (this->dwMessage == EFF_PLAY)
{
*pdwOut = DIEGES_PLAYING;
hres = DI_OK;
}
else
{
hres = IDirectInputEffectShepherd_GetEffectStatus(
this->pes, &this->sh, pdwOut);
}
}
CDIEff_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Escape |
*
* Send a hardware-specific command to the driver.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @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_NOTDOWNLOADED>: The effect is not downloaded.
*
* <c DIERR_NOTINITIALIZED>: The <i IDirectInputEffect> object
* has not yet been <mf IDirectInputEffect::Initialize>d.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_Escape(PDIE pdie, LPDIEFFESCAPE pesc)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Escape, (_ "p", pdie));
/*
* The output buffer is NoScramble because some people like
* to pass overlapping in and out buffers.
*/
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresFullValidPesc(pesc, 1))) {
PDE this = _thisPvNm(pdie, def);
CDIEff_EnterCrit(this);
/*
* Download the effect if it isn't downloaded yet,
* so we have a valid effect to Escape on.
*/
hres = CDIEff_DownloadWorker(this, &this->effDev, 0);
if (SUCCEEDED(hres)) {
hres = IDirectInputEffectShepherd_Escape(
this->pes, &this->sh, pesc);
} else {
hres = DIERR_NOTDOWNLOADED;
}
CDIEff_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffect | Initialize |
*
* Initialize a DirectInputEffect object.
*
* Note that if this method fails, the underlying object should
* be considered to be an an indeterminate state and needs to
* be reinitialized before it can be subsequently used.
*
* The <mf IDirectInputDevice::CreateEffect> method automatically
* initializes the device after creating it. Applications
* normally do not need to call this function.
*
* @cwrap LPDIRECTINPUTEFFECT | lpDirectInputEffect
*
* @parm IN HINSTANCE | hinst |
*
* Instance handle of the application or DLL that is creating
* the DirectInputEffect object.
*
* See the section titled "Initialization and Versions"
* for more information.
*
* @parm DWORD | dwVersion |
*
* Version number of the dinput.h header file that was used.
*
* See the section titled "Initialization and Versions"
* for more information.
*
* @parm IN REFGUID | rguid |
*
* Identifies the effect for which the interface
* should be associated.
* The <mf IDirectInputDevice::EnumEffects> method
* can be used to determine which effect GUIDs are supported by
* the device.
*
* @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_DEVICENOTREG>: The effect GUID does not exist
* on the current device.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
TSDPROC c_rgtsd[] = {
CDIEff_IsValidConstantTsd, /* DIEFT_CONSTANTFORCE */
CDIEff_IsValidRampTsd, /* DIEFT_RAMPFORCE */
CDIEff_IsValidPeriodicTsd, /* DIEFT_PERIODIC */
CDIEff_IsValidConditionTsd, /* DIEFT_CONDITION */
CDIEff_IsValidCustomForceTsd, /* DIEFT_CUSTOMFORCE */
#if DIRECTINPUT_VERSION >= 0x0900
CDIEff_IsValidRandomTsd, /* DIEFT_RANDOM */
CDIEff_IsValidAbsoluteTsd, /* DIEFT_ABSOLUTE */
CDIEff_IsValidBumpForceTsd, /* DIEFT_BUMPFORCE */
CDIEff_IsValidConditionExTsd, /* DIEFT_CONDITIONEX */
#endif /* DIRECTINPUT_VERSION >= 0x0900 */
};
STDMETHODIMP
CDIEff_Initialize(PDIE pdie, HINSTANCE hinst, DWORD dwVersion, REFGUID rguid)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::Initialize,
(_ "pxxG", pdie, hinst, dwVersion, rguid));
if (SUCCEEDED(hres = hresPv(pdie)) &&
SUCCEEDED(hres = hresValidInstanceVer(hinst, dwVersion)) &&
SUCCEEDED(hres = hresFullValidGuid(rguid, 1))) {
PDE this = _thisPv(pdie);
EFFECTMAPINFO emi;
AssertF(this->pes);
/*
* Don't let somebody mess with the effect while we're
* resetting it.
*/
CDIEff_EnterCrit(this);
if (SUCCEEDED(hres = CDIDev_FindEffectGUID(this->pdev, rguid,
&emi, 3)) &&
SUCCEEDED(hres = CDIEff_Reset(this))) {
/*
* ISSUE-2001/03/29-timgill Need to check for actual hardware FF effect support
*/
/*
* Initialize dEffAttributes
*/
this->fInitialized = 1;
this->dEffAttributes = emi.attr;
this->guid = *rguid;
/*
* Check if the driver supports dwStartDelay.
* If it does, no need for us to do anything in that respect.
*/
if( this->dEffAttributes.dwStaticParams & DIEP_STARTDELAY )
{
/*
* No need to emulate dwStartDelay.
*/
}
else
{
/*
* Driver doesn't support start delay.
* Start a thread that will emulate dwStartDelay.
*/
DWORD dwThreadId;
HANDLE hThread;
this->hEventDelete = CreateEvent(NULL, TRUE, FALSE, NULL);
this->hEventThreadDead = CreateEvent(NULL, TRUE, FALSE, NULL);
hThread = CreateThread(NULL, 0, CDIEff_ThreadProc, (LPVOID)pdie, 0, &dwThreadId);
if (hThread == NULL)
{
/* Failed to create the thread.
* Clean up all our preparations.
*/
CloseHandle(this->hEventDelete);
this->hEventDelete = NULL;
CloseHandle(this->hEventThreadDead);
this->hEventThreadDead = NULL;
hres = hresLe(GetLastError());
}
else
{
/*
* Create an event to signal effect started or stopped
*/
this->hThread = hThread;
this->hEventGeneral = CreateEvent(NULL, TRUE, FALSE, NULL);
}
}
this->dwCoords = emi.attr.dwCoords & DIEFF_COORDMASK;
AssertF(this->dwCoords);
/*
* Note, we allow non-hardware specific types that are not
* recognized to pass through untested. This effects to be run
* from a device which has newer effects than this version of
* DInput can check. However if this DInput recognizes the
* effect type, it will be checked, even if the application was
* written for a version that could not check it.
*/
if (fInOrder(DIEFT_PREDEFMIN, DIEFT_GETTYPE(emi.attr.dwEffType),
DIEFT_PREDEFMAX)) {
this->hresValidTsd = c_rgtsd[
DIEFT_GETTYPE(emi.attr.dwEffType) -
DIEFT_PREDEFMIN];
} else {
this->hresValidTsd = CDIEff_IsValidUnknownTsd;
}
hres = S_OK;
}
CDIEff_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffect | Init |
*
* Initialize the internal parts of the DirectInputEffect object.
*
* @parm LPDIRECTINPUTEFFECTSHEPHERD | pes |
*
* The shepherd that lets us talk to the driver.
*
*****************************************************************************/
HRESULT INLINE
CDIEff_Init(struct CDIDev *pdev, LPDIRECTINPUTEFFECTSHEPHERD pes, PDE this)
{
HRESULT hres;
/*
* The critical section must be the very first thing we do,
* because only Finalize checks for its existence.
*
* (We might be finalized without being initialized if the user
* passed a bogus interface to CDIEff_New.)
*/
this->pdev = pdev;
Common_Hold(this->pdev);
this->pes = pes;
OLE_AddRef(this->pes);
hres = CDIDev_NotifyCreateEffect(this->pdev, this);
if (SUCCEEDED(hres)) {
this->fDadNotified = 1;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffect | New |
*
* Create a new DirectInputEffect object, uninitialized.
*
* @parm IN struct CDIDev * | pdd |
*
* Parent device, which we keep a <f Common_Hold> on.
*
* @parm LPDIRECTINPUTEFFECTSHEPHERD | pes |
*
* The shepherd that lets us talk to the driver.
*
* @parm IN PUNK | punkOuter |
*
* Controlling unknown for aggregation.
*
* @parm IN RIID | riid |
*
* Desired interface to new object.
*
* @parm OUT PPV | ppvObj |
*
* Output pointer for new object.
*
*****************************************************************************/
STDMETHODIMP
CDIEff_New(struct CDIDev *pdev, LPDIRECTINPUTEFFECTSHEPHERD pes,
PUNK punkOuter, RIID riid, PPV ppvObj)
{
HRESULT hres;
EnterProcR(IDirectInputEffect::<constructor>,
(_ "ppGp", pdev, pes, riid, punkOuter));
if (SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 5)))
{
LPVOID pvTry = NULL;
hres = Common_NewRiid(CDIEff, punkOuter, riid, &pvTry);
if (SUCCEEDED(hres)) {
PDE this = _thisPv(pvTry);
hres = CDIEff_Init(pdev, pes, this);
if (SUCCEEDED(hres)) {
*ppvObj = pvTry;
} else {
Invoke_Release(&pvTry);
*ppvObj = NULL;
}
}
}
ExitOleProcPpvR(ppvObj);
return hres;
}
/*****************************************************************************
*
* The long-awaited vtbls and templates
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
#define CDIEff_Signature 0x20464643 /* "EFF " */
Primary_Interface_Begin(CDIEff, IDirectInputEffect)
CDIEff_Initialize,
CDIEff_GetEffectGuid,
CDIEff_GetParameters,
CDIEff_SetParameters,
CDIEff_Start,
CDIEff_Stop,
CDIEff_GetEffectStatus,
CDIEff_Download,
CDIEff_Unload,
CDIEff_Escape,
Primary_Interface_End(CDIEff, IDirectInputEffect)