|
|
/*****************************************************************************
* * DIEffJ.c * * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved. * * Abstract: * * Dummy effect driver for joystick. * * Contents: * * CJoyEff_CreateInstance * *****************************************************************************/
#include "dinputpr.h"
#ifdef DEMONSTRATION_FFDRIVER
/*****************************************************************************
* * The sqiffle for this file. * *****************************************************************************/
#define sqfl sqflJoyEff
/****************************************************************************
* * @doc DDK * * @topic DirectInput force feedback effect drivers | * * DirectInput instantiates the force feedback effect driver * by creating the object named by the CLSID stored in the * OEMForceFeedback registry subkey of the joystick type * key. * * Note, however, that since applications using DirectInput * need not load OLE, the effect driver should be careful * not to rely on OLE-specific behavior. * For example, applications using DirectInput cannot be * relied upon to call <f CoFreeUnusedLibraries>. * DirectInput will perform the standard COM operations to * instantiate the effect driver object. The only visible * effect this should have on the implementation of the * effect driver is as follows: * * When DirectInput has released the last effect driver * object, it will manually perform a <f FreeLibrary> of * the effect driver DLL. Consequently, if the effect * driver DLL creates additional resources that are not * associated with the effect driver object, it should * manually <f LoadLibrary> itself to artificially * increase its DLL reference count, thereby preventing * the <f FreeLibrary> from DirectInput from unloading * the DLL prematurely. * * In particular, if the effect driver DLL creates a worker * thread, the effect driver must perform this artificial * <f LoadLibrary> for as long as the worker thread exists. * When the worker thread is no longer needed (for example, upon * notification from the last effect driver object as it * is being destroyed), the worker thread should call * <f FreeLibraryAndExitThread> to decrement the DLL reference * count and terminate the thread. * * All magnitude and gain values used by DirectInput * are uniform and linear across the range. Any * nonlinearity in the physical device must be * handled by the device driver so that the application * sees a linear device. * *****************************************************************************/
/*****************************************************************************
* * Declare the interfaces we will be providing. * * WARNING! If you add a secondary interface, you must also change * CJoyEff_New! * *****************************************************************************/
Primary_Interface(CJoyEff, IDirectInputEffectDriver);
/*****************************************************************************
* * @doc INTERNAL * * @struct JEFFECT | * * Dummy structure that records information about an effect. * * @field DWORD | tmDuration | * * Putative duration for effect. * * @field DWORD | tmStart | * * Time the effect started, or zero if not playing. * * @field BOOL | fInUse | * * Nonzero if this effect is allocated. * *****************************************************************************/
typedef struct JEFFECT { DWORD tmDuration; DWORD tmStart; BOOL fInUse; } JEFFECT, *PJEFFECT;
/*****************************************************************************
* * @doc INTERNAL * * @struct CJoyEff | * * A dummy <i IDirectInputEffectDriver> object for the * generic joystick. * * @field IDirectInputEffectDriver | didc | * * The object (containing vtbl). * * @field BOOL | fCritInited:1 | * * Set if the critical section has been initialized. * * @field DWORD | state | * * The current device state. * * @field LONG | cCrit | * * Number of times the critical section has been taken. * Used only in XDEBUG to check whether the caller is * releasing the object while another method is using it. * * @field DWORD | thidCrit | * * The thread that is currently in the critical section. * Used only in DEBUG for internal consistency checking. * * @field CRITICAL_SECTION | crst | * * Object critical section. Must be taken when accessing * volatile member variables. * * @field JEFFECT | rgjeff[cjeffMax] | * * Information for each effect. * *****************************************************************************/
#define cjeffMax 8 /* Up to 8 simultaneous effects */
typedef struct CJoyEff {
/* Supported interfaces */ IDirectInputEffectDriver ded;
BOOL fCritInited;
DWORD state; DWORD dwGain;
RD(LONG cCrit;) D(DWORD thidCrit;) CRITICAL_SECTION crst;
JEFFECT rgjeff[cjeffMax];
} CJoyEff, DJE, *PDJE;
typedef IDirectInputEffectDriver DED, *PDED;
#define ThisClass CJoyEff
#define ThisInterface IDirectInputEffectDriver
#define riidExpected &IID_IDirectInputEffectDriver
/*****************************************************************************
* * CJoyEff::QueryInterface (from IUnknown) * CJoyEff::AddRef (from IUnknown) * CJoyEff::Release (from IUnknown) * *****************************************************************************/
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | QueryInterface | * * Gives a client access to other interfaces on an object. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @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 DDK * * @method HRESULT | IDirectInputEffectDriver | AddRef | * * Increments the reference count for the interface. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @returns * * Returns the object reference count. * * @xref OLE documentation for <mf IUnknown::AddRef>. * ***************************************************************************** * * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | 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 LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @returns * * Returns the object reference count. * * @xref OLE documentation for <mf IUnknown::Release>. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyEff | QIHelper | * * We don't have any dynamic interfaces and simply forward * to <f Common_QIHelper>. * * @parm IN REFIID | riid | * * The requested interface's IID. * * @parm OUT LPVOID * | ppvObj | * * Receives a pointer to the obtained interface. * ***************************************************************************** * * @doc INTERNAL * * @method HRESULT | CJoyEff | AppFinalize | * * We don't have any weak pointers, so we can just * forward to <f Common_Finalize>. * * @parm PV | pvObj | * * Object being released from the application's perspective. * *****************************************************************************/
#ifdef DEBUG
Default_QueryInterface(CJoyEff) Default_AddRef(CJoyEff) Default_Release(CJoyEff)
#else
#define CJoyEff_QueryInterface Common_QueryInterface
#define CJoyEff_AddRef Common_AddRef
#define CJoyEff_Release Common_Release
#endif
#define CJoyEff_QIHelper Common_QIHelper
#define CJoyEff_AppFinalize Common_AppFinalize
/*****************************************************************************
* * @doc INTERNAL * * @func void | CJoyEff_Finalize | * * Releases the resources of the device. * * @parm PV | pvObj | * * Object being released. Note that it may not have been * completely initialized, so everything should be done * carefully. * *****************************************************************************/
void INTERNAL CJoyEff_Finalize(PV pvObj) { PDJE this = pvObj;
if (this->fCritInited) { DeleteCriticalSection(&this->crst); } }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoyEff | EnterCrit | * * Enter the object critical section. * * @cwrap PDJE | this * *****************************************************************************/
void EXTERNAL CJoyEff_EnterCrit(PDJE this) { EnterCriticalSection(&this->crst); D(this->thidCrit = GetCurrentThreadId()); RD(InterlockedIncrement(&this->cCrit)); }
/*****************************************************************************
* * @doc INTERNAL * * @method void | CJoyEff | LeaveCrit | * * Leave the object critical section. * * @cwrap PDJE | this * *****************************************************************************/
void EXTERNAL CJoyEff_LeaveCrit(PDJE this) { #ifdef XDEBUG
AssertF(this->cCrit); AssertF(this->thidCrit == GetCurrentThreadId()); if (InterlockedDecrement(&this->cCrit) == 0) { D(this->thidCrit = 0); } #endif
LeaveCriticalSection(&this->crst); }
/*****************************************************************************
* * @doc INTERNAL * * @method BOOL | CJoyEff | InCrit | * * Nonzero if we are in the critical section. * *****************************************************************************/
#ifdef DEBUG
BOOL INTERNAL CJoyEff_InCrit(PDJE this) { return this->cCrit && this->thidCrit == GetCurrentThreadId(); }
#endif
/*****************************************************************************
* * @doc INTERNAL * * @method HRESULT | CJoyEff | IsValidId | * * Determine whether the effect pseudo-handle is valid. * If so, returns a pointer to the <t JEFFECT>. * * @cwrap PDJE | this * * @parm DWORD | dwId | * * Putative ID number. * * @parm PJEFFECT * | ppjeff | * * Receives pointer to the <t JEFFECT> on success. * *****************************************************************************/
HRESULT INTERNAL CJoyEff_IsValidId(PDJE this, DWORD dwId, PJEFFECT *ppjeff) { HRESULT hres;
AssertF(CJoyEff_InCrit(this));
if (dwId) { PJEFFECT pjeff = &this->rgjeff[dwId - 1]; if (pjeff->fInUse) { *ppjeff = pjeff; hres = S_OK; } else { hres = E_HANDLE; } } else { hres = E_HANDLE; }
return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | DeviceID | * * Inform the driver of the identity of the device. * * For example, if a device driver is passed * <p dwExternalID> = 2 and <p dwInteralID> = 1, * then this means that unit 1 on the device * corresponds to joystick ID number 2. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwDirectInputVersion | * * The version of DirectInput that loaded the * effect driver. * * @parm DWORD | dwExternalID | * * The joystick ID number being used. * The Windows joystick subsystem allocates external IDs. * * If the <p lpHIDInfo> field is non-<c NULL> then this * parameter should be ignored. * * @parm DWORD | fBegin | * * Nonzero if access to the device is beginning. * Zero if the access to the device is ending. * * @parm DWORD | dwInternalID | * * Internal joystick id. The device driver manages * internal IDs. * * If the <p lpHIDInfo> field is non-<c NULL> then this * parameter should be ignored. * * @parm LPVOID | lpHIDInfo | * * If the underlying device is not a HID device, then this * parameter is <c NULL>. * * If the underlying device is a HID device, then this * parameter points to a <t DIHIDFFINITINFO> structure * which informs the driver of HID information. * * @returns * * <c S_OK> if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/
STDMETHODIMP CJoyEff_DeviceID(PDED pded, DWORD dwDIVer, DWORD dwExternalID, DWORD fBegin, DWORD dwInternalID, LPVOID pvReserved) { PDJE this; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::DeviceID, (_ "pxuuu", pded, dwDIVer, dwExternalID, fBegin, dwInternalID));
this = _thisPvNm(pded, ded);
dwDIVer; dwExternalID; fBegin; dwInternalID; pvReserved;
hres = S_OK;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | Escape | * * Escape to the driver. This method is called * in response to an application invoking the * <mf IDirectInputDevice8::Escape> method. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The joystick ID number being used. * * @parm DWORD | dwEffect | * * If the application invoked the * <mf IDirectInputEffect::Escape> method, then * <p dwEffect> contains the handle (returned by * <mf IDirectInputEffectDriver::DownloadEffect>) * of the effect at which the command is directed. * * If the application invoked the * <mf IDirectInputDevice8::Escape> method, then * <p dwEffect> is zero. * * @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. * * DirectInput has already validated that the * <e DIEFFESCAPE.lpvOutBuffer> and * <e DIEFFESCAPE.lpvInBuffer> and fields * point to valid memory. * * @returns * * <c S_OK> if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/
STDMETHODIMP CJoyEff_Escape(PDED pded, DWORD dwId, DWORD dwEffect, LPDIEFFESCAPE pesc) { PDJE this; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::Escape, (_ "puxx", pded, dwId, dwEffect, pesc->dwCommand));
this = _thisPvNm(pded, ded);
dwId; dwEffect; pesc;
hres = E_NOTIMPL;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | SetGain | * * Set the overall device gain. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The joystick ID number being used. * * @parm DWORD | dwGain | * * The new gain value. * * If the value is out of range for the device, the device * should use the nearest supported value and return * <c DI_TRUNCATED>. * * @returns * * <c S_OK> if the operation completed successfully. * * An error code if something is wrong. * *****************************************************************************/
STDMETHODIMP CJoyEff_SetGain(PDED pded, DWORD dwId, DWORD dwGain) { PDJE this; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::SetGain, (_ "puu", pded, dwId, dwGain));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this);
dwId; this->dwGain = dwGain;
CJoyEff_LeaveCrit(this);
hres = S_OK;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | SendForceFeedbackCommand | * * Send a command to the device. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm DWORD | dwCommand | * * Command, one of the <c DISFFC_*> values. * * @returns * <c S_OK> on success. * * @devnote * * Semantics unclear. * *****************************************************************************/
STDMETHODIMP CJoyEff_SendForceFeedbackCommand(PDED pded, DWORD dwId, DWORD dwCmd) { PDJE this; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::SendForceFeedbackCommand, (_ "pux", pded, dwId, dwCmd));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this);
dwId; dwCmd;
this->state = dwCmd;
/*
* On a reset, all effects are destroyed. */ if (dwCmd & DISFFC_RESET) { DWORD ijeff;
for (ijeff = 0; ijeff < cjeffMax; ijeff++) { this->rgjeff[ijeff].fInUse = FALSE; }
}
CJoyEff_LeaveCrit(this);
hres = S_OK;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | GetForceFeedbackState | * * Retrieve the force feedback state for the device. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm LPDEVICESTATE | pds | * * Receives device state. * * DirectInput will set the <e DIDEVICESTATE.dwSize> field * to sizeof(DIDEVICESTATE) before calling this method. * @returns * <c S_OK> on success. * * @devnote * * Semantics unclear. * *****************************************************************************/
STDMETHODIMP CJoyEff_GetForceFeedbackState(PDED pded, DWORD dwId, LPDIDEVICESTATE pds) { PDJE this; HRESULT hres; DWORD ijeff, cjeff, cjeffPlaying; EnterProcI(IDirectInputEffectDriver::Joy::GetForceFeedbackState, (_ "pup", pded, dwId, pds));
this = _thisPvNm(pded, ded);
dwId; pds;
if (pds->dwSize == cbX(*pds)) { CJoyEff_EnterCrit(this);
/*
* Count how many effects are in use, and return it as a percentage. */ cjeff = cjeffPlaying = 0; for (ijeff = 0; ijeff < cjeffMax; ijeff++) { PJEFFECT pjeff = &this->rgjeff[ijeff]; if (pjeff->fInUse) { cjeff++; if (pjeff->tmStart && GetTickCount() - pjeff->tmStart < pjeff->tmDuration) { cjeffPlaying++; } } }
pds->dwLoad = MulDiv(100, cjeff, cjeffMax);
/*
* If there are no effects downloaded, then we are empty. */ pds->dwState = 0;
if (cjeff == 0) { pds->dwState |= DIGFFS_EMPTY; } else
/*
* If there are no effects playing, then we are stopped. */ if (cjeffPlaying == 0) { pds->dwState |= DIGFFS_STOPPED; }
/*
* Actuators are always on (dumb fake hardware) */ pds->dwState |= DIGFFS_ACTUATORSON;
CJoyEff_LeaveCrit(this); hres = S_OK; } else { hres = E_INVALIDARG; }
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | DownloadEffect | * * Send an effect to the device. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm DWORD | dwEffectId | * * Internal identifier for the effect, taken from * the <t DIEFFECTATTRIBUTES> structure for the effect * as stored in the registry. * * @parm IN OUT LPDWORD | pdwEffect | * * On entry, contains the handle of the effect being * downloaded. If the value is zero, then a new effect * is downloaded. If the value is nonzero, then an * existing effect is modified. * * On exit, contains the new effect handle. * * On failure, set to zero if the effect is lost, * or left alone if the effect is still valid with * its old parameters. * * Note that zero is never a valid effect handle. * * * @parm LPCDIEFFECT | peff | * * The new parameters for the effect. The axis and button * values have been converted to object identifiers * as follows: * * - One type specifier: * <c DIDFT_RELAXIS>, * <c DIDFT_ABSAXIS>, * <c DIDFT_PSHBUTTON>, * <c DIDFT_TGLBUTTON>, * <c DIDFT_POV>. * * - One instance specifier: * <c DIDFT_MAKEINSTANCE>(n). * * Other bits are reserved and should be ignored. * * For example, the value 0x0200104 corresponds to * the type specifier <c DIDFT_PSHBUTTON> and * the instance specifier <c DIDFT_MAKEINSTANCE>(1), * which together indicate that the effect should * be associated with button 1. Axes, buttons, and POVs * are each numbered starting from zero. * * @parm DWORD | dwFlags | * * Zero or more <c DIEP_*> flags specifying which * portions of the effect information has changed from * the effect already on the device. * * This information is passed to drivers to allow for * optimization of effect modification. If an effect * is being modified, a driver may be able to update * the effect <y in situ> and transmit to the device * only the information that has changed. * * Drivers are not, however, required to implement this * optimization. All fields in the <t DIEFFECT> structure * pointed to by the <p peff> parameter are valid, and * a driver may choose simply to update all parameters of * the effect at each download. * * @returns * <c S_OK> on success. * * @devnote * * This implies that 0 is never a valid effect handle value. * *****************************************************************************/
STDMETHODIMP CJoyEff_DownloadEffect(PDED pded, DWORD dwId, DWORD dwEffectId, LPDWORD pdwEffect, LPCDIEFFECT peff, DWORD fl) { PDJE this; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::DownloadEffect, (_ "puxxpx", pded, dwId, dwEffectId, *pdwEffect, peff, fl));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this);
dwId; fl;
if (dwEffectId == 1) {
PJEFFECT pjeff; DWORD dwGain;
/*
* Parameter validation goes here, if any. * * Envelope parameter is ignored. */
if (peff->cAxes == 0) { /* Zero axes? Nice try */ hres = E_INVALIDARG; goto done; }
/*
* Pin above-nominal values to DI_FFNOMINALMAX because * we don't support overgain. */ dwGain = min(peff->dwGain, DI_FFNOMINALMAX);
/*
* We do not support triggers. */ if (peff->dwTriggerButton != DIEB_NOTRIGGER) { hres = E_NOTIMPL; goto done; }
/*
* If no downloading in effect, then we're done. */ if (fl & DIEP_NODOWNLOAD) { hres = S_OK; goto done; }
if (*pdwEffect) { hres = CJoyEff_IsValidId(this, *pdwEffect, &pjeff); if (FAILED(hres)) { goto done; } } else { DWORD ijeff;
for (ijeff = 0; ijeff < cjeffMax; ijeff++) { if (!this->rgjeff[ijeff].fInUse) { this->rgjeff[ijeff].fInUse = TRUE; pjeff = &this->rgjeff[ijeff]; goto haveEffect; } } hres = DIERR_DEVICEFULL; goto done; }
haveEffect:;
SquirtSqflPtszV(sqfl, TEXT("dwFlags=%08x"), peff->dwFlags); SquirtSqflPtszV(sqfl, TEXT("cAxes=%d"), peff->cAxes); for (fl = 0; fl < peff->cAxes; fl++) { SquirtSqflPtszV(sqfl, TEXT(" Axis%2d=%08x Direction=%5d"), fl, peff->rgdwAxes[fl], peff->rglDirection[fl]); }
SquirtSqflPtszV(sqfl, TEXT("dwTrigger=%08x"), peff->dwTriggerButton);
pjeff->tmDuration = peff->dwDuration / 1000;
*pdwEffect = (DWORD)(pjeff - this->rgjeff) + 1; //we are sure this cast will not cause problem
hres = S_OK;
} else { hres = E_NOTIMPL; }
done:; CJoyEff_LeaveCrit(this);
ExitOleProc(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | DestroyEffect | * * Remove an effect from the device. * * If the effect is playing, the driver should stop it * before unloading it. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm DWORD | dwEffect | * * The effect to be destroyed. * * @returns * <c S_OK> on success. * *****************************************************************************/
STDMETHODIMP CJoyEff_DestroyEffect(PDED pded, DWORD dwId, DWORD dwEffect) { PDJE this; PJEFFECT pjeff; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::DestroyEffect, (_ "pux", pded, dwId, dwEffect));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this); dwId;
hres = CJoyEff_IsValidId(this, dwEffect, &pjeff); if (SUCCEEDED(hres)) { pjeff->fInUse = 0; }
CJoyEff_LeaveCrit(this);
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | StartEffect | * * Begin playback of an effect. * * If the effect is already playing, then it is restarted * from the beginning. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm DWORD | dwEffect | * * The effect to be played. * * @parm DWORD | dwMode | * * How the effect is to affect other effects. * * This parameter consists of zero or more * <c DIES_*> flags. Note, however, that the driver * will never receive the <c DIES_NODOWNLOAD> flag; * the <c DIES_NODOWNLOAD> flag is managed by * DirectInput and not the driver. * * @parm DWORD | dwCount | * * Number of times the effect is to be played. * * @returns * <c S_OK> on success. * *****************************************************************************/
STDMETHODIMP CJoyEff_StartEffect(PDED pded, DWORD dwId, DWORD dwEffect, DWORD dwMode, DWORD dwCount) { PDJE this; PJEFFECT pjeff; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::StartEffect, (_ "puxxu", pded, dwId, dwEffect, dwMode, dwCount));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this);
dwId; hres = CJoyEff_IsValidId(this, dwEffect, &pjeff); if (SUCCEEDED(hres)) { if (pjeff->tmStart) { if (GetTickCount() - pjeff->tmStart < pjeff->tmDuration) { /* Already playing */ hres = hresLe(ERROR_BUSY); } else { pjeff->tmStart = GetTickCount(); hres = S_OK; } } else { pjeff->tmStart = GetTickCount(); hres = S_OK; } }
CJoyEff_LeaveCrit(this);
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | StopEffect | * * Halt playback of an effect. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm DWORD | dwEffect | * * The effect to be stopped. * * @returns * <c S_OK> on success. * *****************************************************************************/
STDMETHODIMP CJoyEff_StopEffect(PDED pded, DWORD dwId, DWORD dwEffect) { PDJE this; PJEFFECT pjeff; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::StopEffect, (_ "pux", pded, dwId, dwEffect));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this);
dwId; hres = CJoyEff_IsValidId(this, dwEffect, &pjeff); if (SUCCEEDED(hres)) { if (pjeff->tmStart) { if (GetTickCount() - pjeff->tmStart < pjeff->tmDuration) { /* It is still playing; stop it */ hres = S_OK; } else { hres = S_FALSE; /* It already stopped on its own */ } pjeff->tmStart = 0; } else { hres = S_FALSE; /* It was never started */ } }
CJoyEff_LeaveCrit(this);
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | GetEffectStatus | * * Obtain information about an effect. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm DWORD | dwId | * * The external joystick number being addressed. * * @parm DWORD | dwEffect | * * The effect to be queried. * * @parm LPDWORD | pdwStatus | * * Receives the effect status. * * @returns * <c S_OK> on success. * *****************************************************************************/
STDMETHODIMP CJoyEff_GetEffectStatus(PDED pded, DWORD dwId, DWORD dwEffect, LPDWORD pdwStatus) { PDJE this; PJEFFECT pjeff; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::StopEffect, (_ "pux", pded, dwId, dwEffect));
this = _thisPvNm(pded, ded);
CJoyEff_EnterCrit(this);
dwId; hres = CJoyEff_IsValidId(this, dwEffect, &pjeff); if (SUCCEEDED(hres)) { DWORD dwStatus;
dwStatus = 0; if (pjeff->tmStart && GetTickCount() - pjeff->tmStart < pjeff->tmDuration) { dwStatus |= DEV_STS_EFFECT_RUNNING; } *pdwStatus = dwStatus; hres = S_OK; }
CJoyEff_LeaveCrit(this);
ExitOleProcR(); return hres; }
/*****************************************************************************
* * @doc DDK * * @method HRESULT | IDirectInputEffectDriver | GetVersions | * * Obtain version information about the force feedback * hardware and driver. * * @cwrap LPDIRECTINPUTEFFECTDRIVER | lpEffectDriver * * @parm LPDIDRIVERVERSIONS | pvers | * * A structure which should be filled in with version information * describing the hardware, firmware, and driver. * * DirectInput will set the <e DIDRIVERVERSIONS.dwSize> field * to sizeof(DIDRIVERVERSIONS) before calling this method. * * @returns * <c S_OK> on success. * *****************************************************************************/
STDMETHODIMP CJoyEff_GetVersions(PDED pded, LPDIDRIVERVERSIONS pvers) { PDJE this; HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::GetVersions, (_ "pux", pded));
this = _thisPvNm(pded, ded);
/*
* Returning E_NOTIMPL causes DirectInput to ask the VxD for the same * information. */ hres = E_NOTIMPL;
ExitOleProcR(); return hres; }
/*****************************************************************************
* * CJoyEff_New (constructor) * *****************************************************************************/
STDMETHODIMP CJoyEff_New(PUNK punkOuter, RIID riid, PPV ppvObj) { HRESULT hres; EnterProcI(IDirectInputEffectDriver::Joy::<constructor>, (_ "Gp", riid, ppvObj));
hres = Common_NewRiid(CJoyEff, punkOuter, riid, ppvObj);
if (SUCCEEDED(hres)) { /* Must use _thisPv if multiple interfaces supported */ PDJE this = _thisPvNm(*ppvObj, ded);
/*
* 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 CJoyEff_New.) */ this->fCritInited = fInitializeCriticalSection(&this->crst); if( !this->fCritInited ) { Common_Unhold(this); *ppvObj = NULL; hres = E_OUTOFMEMORY; } }
ExitOleProcPpvR(ppvObj); return hres; }
/*****************************************************************************
* * The long-awaited vtbls and templates * *****************************************************************************/
#pragma BEGIN_CONST_DATA
#define CJoyEff_Signature 0x4645454B /* "JEFF" */
Interface_Template_Begin(CJoyEff) Primary_Interface_Template(CJoyEff, IDirectInputEffectDriver) Interface_Template_End(CJoyEff)
Primary_Interface_Begin(CJoyEff, IDirectInputEffectDriver) CJoyEff_DeviceID, CJoyEff_GetVersions, CJoyEff_Escape, CJoyEff_SetGain, CJoyEff_SendForceFeedbackCommand, CJoyEff_GetForceFeedbackState, CJoyEff_DownloadEffect, CJoyEff_DestroyEffect, CJoyEff_StartEffect, CJoyEff_StopEffect, CJoyEff_GetEffectStatus, Primary_Interface_End(CJoyEff, IDirectInputEffectDriver)
#endif
|