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.
 
 
 
 
 
 

1316 lines
34 KiB

/*****************************************************************************
*
* DIEShep.c
*
* Copyright (c) 1997 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* The IDirectInputEffectDriver shepherd.
*
* The shepherd does the annoying work of babysitting the
* external IDirectInputDriver.
*
* It makes sure nobody parties on bad handles.
*
* It handles cross-process (or even intra-process) effect
* management.
*
* It caches the joystick ID so you don't have to.
*
* Contents:
*
* CEShep_New
*
*****************************************************************************/
#include "dinputpr.h"
/*****************************************************************************
*
* The sqiffle for this file.
*
*****************************************************************************/
#define sqfl sqflEShep
#pragma BEGIN_CONST_DATA
/*****************************************************************************
*
* Declare the interfaces we will be providing.
*
*****************************************************************************/
Primary_Interface(CEShep, IDirectInputEffectShepherd);
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct CEShep |
*
* The <i IDirectInputEffectShepherd> object, which
* babysits an <i IDirectInputEffectDriver>.
*
* @field IDirectInputEffectShepherd | des |
*
* DirectInputEffectShepherd object (containing vtbl).
*
* @field IDirectInputEffectDriver * | pdrv |
*
* Delegated effect driver interface.
*
* @field UINT | idJoy |
*
* Joystick identification number.
*
* @field HINSTANCE | hinst |
*
* The instance handle of the DLL that contains the effect
* driver.
*
*****************************************************************************/
typedef struct CEShep {
/* Supported interfaces */
IDirectInputEffectShepherd des;
IDirectInputEffectDriver *pdrv;
UINT idJoy;
HINSTANCE hinst;
} CEShep, ES, *PES;
typedef IDirectInputEffectShepherd DES, *PDES;
#define ThisClass CEShep
#define ThisInterface IDirectInputEffectShepherd
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | QueryInterface |
*
* Gives a client access to other interfaces on an object.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @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 | IDirectInputEffectShepherd | AddRef |
*
* Increments the reference count for the interface.
*
* @cwrap LPDIRECTINPUT | lpDirectInput
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::AddRef>.
*
*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | 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 LPDIRECTINPUT | lpDirectInput
*
* @returns
*
* Returns the object reference count.
*
* @xref OLE documentation for <mf IUnknown::Release>.
*
*//**************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | 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 | IDirectInputEffectShepherd | 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(CEShep)
Default_AddRef(CEShep)
Default_Release(CEShep)
#else
#define CEShep_QueryInterface Common_QueryInterface
#define CEShep_AddRef Common_AddRef
#define CEShep_Release Common_Release
#endif
#define CEShep_QIHelper Common_QIHelper
#define CEShep_AppFinalize Common_AppFinalize
/*****************************************************************************
*
* @doc INTERNAL
*
* @func void | CEShep_Finalize |
*
* Clean up our instance data.
*
* @parm PV | pvObj |
*
* Object being released. Note that it may not have been
* completely initialized, so everything should be done
* carefully.
*
*****************************************************************************/
void INTERNAL
CEShep_Finalize(PV pvObj)
{
PES this = pvObj;
Invoke_Release(&this->pdrv);
if (this->hinst) {
FreeLibrary(this->hinst);
}
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CEShep | UnlockDevice |
*
* Unlock the joystick table after we are finished messing
* with the device.
*
*****************************************************************************/
void INLINE
CEShep_UnlockDevice(void)
{
ReleaseMutex(g_hmtxJoy);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CEShep | LockDevice |
*
* Validate that the the device access token is still valid.
*
* If so, then take the joystick mutex to prevent someone
* from dorking with the device while we're using it.
* Call <f CEShep_UnlockDevice> when done.
*
* If not, then try to steal ownership if requested.
*
* Else, fail.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Handle to lock.
*
* @parm DWORD | dwAccess |
*
* If <c DISFFC_FORCERESET>, then force ownership of the device.
* This is done as part of device acquisition to kick out the
* previous owner.
*
* Otherwise, if the device belongs to somebody else, then
* leave it alone.
*
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* <c DIERR_NOTEXCLUSIVEACQUIRED> if the lock failed.
* Note that
* <mf IDirectInputEffectDevice2::SetForceFeedbackState>
* and
* <mf IDirectInputEffectDevice2::GetForceFeedbackState>
* are particularly keen on this error code.
*
*****************************************************************************/
STDMETHODIMP
CEShep_LockDevice(PES this, PSHEPHANDLE psh, DWORD dwAccess)
{
HRESULT hres;
EnterProc(CEShep_LockDevice, (_ "puu", this, psh->dwTag, dwAccess));
WaitForSingleObject(g_hmtxJoy, INFINITE);
/*
* Note that DISFFC_FORCERESET allows unconditional access.
* DISFFC_FORCERESET is used when we perform the initial reset
* after acquiring, so we can legitimately steal the device
* from the previous owner.
*/
if (dwAccess & DISFFC_FORCERESET) {
hres = S_OK;
} else if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag) {
hres = S_OK;
} else {
ReleaseMutex(g_hmtxJoy);
hres = DIERR_NOTEXCLUSIVEACQUIRED;
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | CEShep | UnlockEffect |
*
* Unlock the joystick table after we are finished messing
* with an effect.
*
*****************************************************************************/
void INLINE
CEShep_UnlockEffect(void)
{
ReleaseMutex(g_hmtxJoy);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CEShep | LockEffect |
*
* Validate that the the effect handle is still valid.
*
* If so, then take the joystick mutex to prevent someone
* from dorking with the device while we're using the handle.
* Call <f CEShep_UnlockEffect> when done.
*
* If not, then set the effect handle to zero to indicate
* that it's bogus. The
* <mf IDirectInputEffectShepherd::DownloadEffect>
* method relies on the zero-ness.
* It is also asserted in <i IDirectInputEffect> to make
* sure we don't accidentally leave effects on the device
* when we leave.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Handle to lock.
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* <c DIERR_NOTDOWNLOADED> if the lock failed.
* Note that
* <mf IDirectInputEffectShepherd::DownloadEffect> and
* <mf IDirectInputEffectShepherd::DestroyEffect> assume
* that this is the only possible error code.
*
*****************************************************************************/
STDMETHODIMP
CEShep_LockEffect(PES this, PSHEPHANDLE psh)
{
HRESULT hres;
EnterProc(CEShep_LockEffect, (_ "pux", this, psh->dwTag, psh->dwEffect));
WaitForSingleObject(g_hmtxJoy, INFINITE);
if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag && psh->dwEffect) {
hres = S_OK;
} else {
psh->dwEffect = 0;
ReleaseMutex(g_hmtxJoy);
hres = DIERR_NOTDOWNLOADED;
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | 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 LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm DWORD | dwExternalID |
*
* The joystick ID number being used.
* The Windows joystick subsystem allocates external IDs.
*
* @parm DWORD | fBegin |
*
* Nonzero if access to the device is begining.
* Zero if the access to the device is ending.
*
* @parm LPVOID | lpReserved |
*
* Reserved for future use (HID).
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* An error code if something is wrong.
*
*****************************************************************************/
STDMETHODIMP
CEShep_DeviceID(PDES pdes, DWORD dwExternalID, DWORD fBegin, LPVOID pvReserved)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::DeviceID,
(_ "puu", pdes, dwExternalID, fBegin));
this = _thisPvNm(pdes, des);
AssertF(dwExternalID < cJoyMax);
if (dwExternalID < cJoyMax) {
VXDINITPARMS vip;
/*
* If this device has never been used before,
* go grab its global gain.
*/
WaitForSingleObject(g_hmtxJoy, INFINITE);
if (g_psoh->rggjs[dwExternalID].dwTag == 0) {
DIJOYCONFIG cfg;
g_psoh->rggjs[dwExternalID].dwTag = 1;
hres = JoyReg_GetConfig(dwExternalID, &cfg, DIJC_GAIN);
if (SUCCEEDED(hres)) {
SquirtSqflPtszV(sqfl,
TEXT("Joystick %d global gain = %d"),
dwExternalID, cfg.dwGain);
g_psoh->rggjs[dwExternalID].dwCplGain = cfg.dwGain;
} else {
g_psoh->rggjs[dwExternalID].dwCplGain = DI_FFNOMINALMAX;
}
/*
* Set to DI_FFNOMINALMAX until we learn better.
*/
g_psoh->rggjs[dwExternalID].dwDevGain = DI_FFNOMINALMAX;
}
ReleaseMutex(g_hmtxJoy);
/*
* Ask the HEL for the internal ID.
*/
hres = Hel_Joy_GetInitParms(dwExternalID, &vip);
if (SUCCEEDED(hres)) {
this->idJoy = dwExternalID;
hres = this->pdrv->lpVtbl->DeviceID(this->pdrv,
DIRECTINPUT_VERSION,
dwExternalID,
fBegin, vip.dwId,
pvReserved);
}
} else {
hres = E_FAIL;
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | Escape |
*
* Escape to the driver. This method is called
* in response to an application invoking the
* <mf IDirectInputEffect::Escape> method.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about the effect at which the command is directed.
*
* @parm LPDIEFFESCAPE | pesc |
*
* Command block.
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* <c DIERR_NOTDOWNLOADED> if the effect is not downloaded.
*
*****************************************************************************/
STDMETHODIMP
CEShep_Escape(PDES pdes, PSHEPHANDLE psh, LPDIEFFESCAPE pesc)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::Escape,
(_ "puxx", pdes, psh->dwTag, psh->dwEffect, pesc->dwCommand));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) {
if (psh->dwEffect) {
hres = this->pdrv->lpVtbl->Escape(this->pdrv, this->idJoy,
psh->dwEffect, pesc);
} else {
hres = DIERR_NOTDOWNLOADED;
}
CEShep_UnlockEffect();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | DeviceEscape |
*
* Escape to the driver. This method is called
* in response to an application invoking the
* <mf IDirectInputDevice8::Escape> method.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about the ownership of the device.
*
* @parm LPDIEFFESCAPE | pesc |
*
* Command block.
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* An error code if something is wrong.
*
*****************************************************************************/
STDMETHODIMP
CEShep_DeviceEscape(PDES pdes, PSHEPHANDLE psh, LPDIEFFESCAPE pesc)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::DeviceEscape,
(_ "pux", pdes, psh->dwTag, pesc->dwCommand));
this = _thisPvNm(pdes, des);
AssertF(psh->dwEffect == 0);
WaitForSingleObject(g_hmtxJoy, INFINITE);
if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag) {
hres = this->pdrv->lpVtbl->Escape(this->pdrv, this->idJoy,
0, pesc);
} else {
hres = DIERR_NOTEXCLUSIVEACQUIRED;
}
ReleaseMutex(g_hmtxJoy);
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CEShep | SetPhysGain |
*
* Set the physical gain based on the global gain
* and the local gain.
*
* The caller must already have the global joystick lock.
*
*
* @cwrap PES | this
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* An error code if something is wrong.
*
*****************************************************************************/
STDMETHODIMP
CEShep_SetPhysGain(PES this)
{
HRESULT hres;
hres = this->pdrv->lpVtbl->SetGain(
this->pdrv, this->idJoy,
MulDiv(g_psoh->rggjs[this->idJoy].dwDevGain,
g_psoh->rggjs[this->idJoy].dwCplGain,
DI_FFNOMINALMAX));
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | SetGlobalGain |
*
* Set the global gain.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm DWORD | dwCplGain |
*
* The new global gain value.
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* An error code if something is wrong.
*
*****************************************************************************/
STDMETHODIMP
CEShep_SetGlobalGain(PDES pdes, DWORD dwCplGain)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::SetGlobalGain,
(_ "pu", pdes, dwCplGain));
this = _thisPvNm(pdes, des);
WaitForSingleObject(g_hmtxJoy, INFINITE);
g_psoh->rggjs[this->idJoy].dwCplGain = dwCplGain;
hres = CEShep_SetPhysGain(this);
ReleaseMutex(g_hmtxJoy);
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | SetGain |
*
* Set the overall device gain.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about device ownership.
*
* @parm DWORD | dwDevGain |
*
* The new local gain value.
*
* @returns
*
* <c S_OK> if the operation completed successfully.
*
* An error code if something is wrong.
*
*****************************************************************************/
STDMETHODIMP
CEShep_SetGain(PDES pdes, PSHEPHANDLE psh, DWORD dwDevGain)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::SetGain,
(_ "puu", pdes, psh->dwTag, dwDevGain));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockDevice(this, psh, DISFFC_NULL))) {
g_psoh->rggjs[this->idJoy].dwDevGain = dwDevGain;
hres = CEShep_SetPhysGain(this);
CEShep_UnlockDevice();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | SendForceFeedbackCommand |
*
* Send a command to the device.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about device ownership.
*
* @parm DWORD | dwCmd |
*
* Command, one of the <c DISFFC_*> values.
*
* @returns
* <c S_OK> on success.
*
* @devnote
*
* Semantics unclear.
*
*****************************************************************************/
STDMETHODIMP
CEShep_SetForceFeedbackState(PDES pdes, PSHEPHANDLE psh, DWORD dwCmd)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::SetForceFeedbackState,
(_ "pux", pdes, psh->dwTag, dwCmd));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockDevice(this, psh, dwCmd))) {
if (dwCmd & DISFFC_FORCERESET) {
dwCmd &= ~DISFFC_FORCERESET;
dwCmd |= DISFFC_RESET;
}
hres = this->pdrv->lpVtbl->SendForceFeedbackCommand(
this->pdrv, this->idJoy, dwCmd);
if (SUCCEEDED(hres) && (dwCmd & DISFFC_RESET)) {
psh->dwTag = ++g_psoh->rggjs[this->idJoy].dwTag;
}
CEShep_UnlockDevice();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | GetForceFeedbackState |
*
* Retrieve the force feedback state for the device.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about device ownership.
*
* @parm LPDIDEVICESTATE | pds |
*
* Receives device state.
*
* @returns
* <c S_OK> on success.
*
* @devnote
*
* Semantics unclear.
*
*****************************************************************************/
STDMETHODIMP
CEShep_GetForceFeedbackState(PDES pdes, PSHEPHANDLE psh, LPDIDEVICESTATE pds)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::GetForceFeedbackState,
(_ "pup", pdes, psh->dwTag, pds));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockDevice(this, psh, DISFFC_NULL))) {
hres = this->pdrv->lpVtbl->GetForceFeedbackState(
this->pdrv, this->idJoy, pds);
CEShep_UnlockDevice();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | DownloadEffect |
*
* Send an effect to the device.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm DWORD | dwEffectId |
*
* Magic cookie dword that identifies the effect.
*
* @parm IN OUT PSHEPHANDLE | psh |
*
* 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.
*
* @parm LPCDIEFFECT | peff |
*
* The new parameters for the effect. The axis and button
* values have been converted to axis/button indexes.
*
* @returns
* <c S_OK> on success.
*
* <c S_FALSE> if no change was made.
*
*****************************************************************************/
STDMETHODIMP
CEShep_DownloadEffect(PDES pdes, DWORD dwEffectId,
PSHEPHANDLE psh, LPCDIEFFECT peff, DWORD fl)
{
PES this;
HRESULT hres = S_OK;
EnterProcI(IDirectInputEffectShepherd::DownloadEffect,
(_ "pxuppx", pdes, dwEffectId, psh->dwTag,
psh->dwEffect, peff, fl));
this = _thisPvNm(pdes, des);
/*
* Downloading an effect is sufficiently different from all
* other methods that we do the locking manually.
*/
WaitForSingleObject(g_hmtxJoy, INFINITE);
/*
* If not downloading, then it doesn't matter whether or not
* the tag matches. However, if the tag doesn't match, then
* we must wipe out the download handle because it's dead.
*/
if (g_psoh->rggjs[this->idJoy].dwTag == psh->dwTag) {
} else {
psh->dwEffect = 0;
if (fl & DIEP_NODOWNLOAD) { /* It's okay if not downloading */
} else {
hres = DIERR_NOTEXCLUSIVEACQUIRED;
goto done;
}
}
/*
* If downloading and creating a new effect,
* then all parameters need to be downloaded.
*/
if (!(fl & DIEP_NODOWNLOAD) && psh->dwEffect == 0) {
fl |= DIEP_ALLPARAMS;
}
if (fl) {
hres = this->pdrv->lpVtbl->DownloadEffect(
this->pdrv, this->idJoy, dwEffectId,
&psh->dwEffect, peff, fl);
} else {
hres = S_FALSE;
}
done:;
ReleaseMutex(g_hmtxJoy);
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | DestroyEffect |
*
* Remove an effect from the device.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about the effect to be destroyed. On exit,
* the <e SHEPHANDLE.dwEffect> is zero'd so nobody will use
* it any more.
*
* @returns
* <c S_OK> on success.
*
* <c S_FALSE> if the effect was already destroyed.
*
*****************************************************************************/
STDMETHODIMP
CEShep_DestroyEffect(PDES pdes, PSHEPHANDLE psh)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::DestroyEffect,
(_ "pux", pdes, psh->dwTag, psh->dwEffect));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) {
DWORD dwEffect = psh->dwEffect;
psh->dwEffect = 0;
hres = this->pdrv->lpVtbl->DestroyEffect(
this->pdrv, this->idJoy, dwEffect);
CEShep_UnlockEffect();
} else {
hres = S_FALSE;
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | StartEffect |
*
* Begin playback of an effect.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm PSHEPHANDLE | psh |
*
* Information about the effect to be played.
*
* @parm DWORD | dwMode |
*
* How the effect is to affect other effects.
*
* @parm DWORD | dwCount |
*
* Number of times the effect is to be played.
*
* @returns
* <c S_OK> on success.
*
*****************************************************************************/
STDMETHODIMP
CEShep_StartEffect(PDES pdes, PSHEPHANDLE psh, DWORD dwMode, DWORD dwCount)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::StartEffect,
(_ "puxxu", pdes, psh->dwTag, psh->dwEffect, dwMode, dwCount));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) {
hres = this->pdrv->lpVtbl->StartEffect(this->pdrv, this->idJoy,
psh->dwEffect, dwMode, dwCount);
CEShep_UnlockEffect();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | StopEffect |
*
* Halt playback of an effect.
*
* ISSUE-2001/03/29-timgill There is no way to pause an effect
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm DWORD | dwId |
*
* The external joystick number being addressed.
*
* @parm PSHEPHANDLE | psh |
*
* Information about the effect to be stopped.
*
* @returns
* <c S_OK> on success.
*
*****************************************************************************/
STDMETHODIMP
CEShep_StopEffect(PDES pdes, PSHEPHANDLE psh)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::StopEffect,
(_ "pux", pdes, psh->dwTag, psh->dwEffect));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) {
hres = this->pdrv->lpVtbl->StopEffect(this->pdrv, this->idJoy,
psh->dwEffect);
CEShep_UnlockEffect();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | GetEffectStatus |
*
* Obtain information about an effect.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm DWORD | dwId |
*
* The external joystick number being addressed.
*
* @parm PSHEPHANDLE | psh |
*
* Information about the effect to be queried.
*
* @parm LPDWORD | pdwStatus |
*
* Receives the effect status.
*
* @returns
* <c S_OK> on success.
*
*****************************************************************************/
STDMETHODIMP
CEShep_GetEffectStatus(PDES pdes, PSHEPHANDLE psh, LPDWORD pdwStatus)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::GetEffectStatus,
(_ "pux", pdes, psh->dwTag, psh->dwEffect));
this = _thisPvNm(pdes, des);
if (SUCCEEDED(hres = CEShep_LockEffect(this, psh))) {
hres = this->pdrv->lpVtbl->GetEffectStatus(this->pdrv, this->idJoy,
psh->dwEffect, pdwStatus);
CEShep_UnlockEffect();
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | GetVersions |
*
* Obtain version information about the force feedback
* hardware and driver.
*
* @cwrap LPDIRECTINPUTEFFECTSHEPHERD | lpShepherd
*
* @parm LPDIDRIVERVERSIONS | pvers |
*
* A structure which will be filled in with version information
* describing the hardware, firmware, and driver.
*
* @returns
* <c S_OK> on success.
*
*****************************************************************************/
STDMETHODIMP
CEShep_GetVersions(PDES pdes, LPDIDRIVERVERSIONS pvers)
{
PES this;
HRESULT hres;
EnterProcI(IDirectInputEffectShepherd::GetVersions, (_ "p", pdes));
this = _thisPvNm(pdes, des);
AssertF(pvers->dwSize == cbX(*pvers));
hres = this->pdrv->lpVtbl->GetVersions(this->pdrv, pvers);
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CEShep | InitInstance |
*
* Initialize a new instance of
* an IDirectInputEffectShepherd object.
*
* If an in-proc OLE server is needed, then load it.
*
* Otherwise, use our private interface that goes down
* to our helper driver.
*
* @parm IN HKEY | hkFF |
*
* Force feedback registry key.
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
STDMETHODIMP
CEShep_InitInstance(PES this, HKEY hkFF)
{
LONG lRc;
HRESULT hres;
TCHAR tszClsid[ctchGuid];
EnterProcI(IDirectInputEffectShepherd::InitInstance, (_ "x", hkFF));
if( hkFF == 0x0 )
{
TCHAR tszName[ctchNameGuid];
NameFromGUID(tszName, &IID_IDirectInputPIDDriver );
memcpy(tszClsid, &tszName[ctchNamePrefix], cbX(tszClsid) );
lRc = ERROR_SUCCESS;
}else
{
lRc = RegQueryString(hkFF, TEXT("CLSID"), tszClsid, cA(tszClsid));
/*
* Prefix warns that tszClsid could be uninitialized through this
* path (mb:35346) however RegQueryString only returns ERROR_SUCCESS
* if a nul terminated string has been read into tszClsid.
*/
}
if (lRc == ERROR_SUCCESS) {
hres = DICoCreateInstance(tszClsid, 0,
&IID_IDirectInputEffectDriver,
&this->pdrv,
&this->hinst);
/*
* If anything went wrong, change the error to
* E_NOTIMPL so the app won't see a wacky CoCreateInstance
* error code.
*/
if (FAILED(hres)) {
SquirtSqflPtszV(sqfl | sqflBenign,
TEXT("Substituting E_NOTIMPL for FF driver CoCreateInstance error 0x%08x"),
hres );
hres = E_NOTIMPL;
}
} else {
#ifdef WINNT
hres = E_NOTIMPL;
#else
{
DWORD cb = 0;
lRc = RegQueryValueEx(hkFF, TEXT("VJoyD"), 0, 0, 0, &cb);
if (lRc == ERROR_SUCCESS || lRc == ERROR_MORE_DATA) {
hres = CEffVxd_New(0, &IID_IDirectInputEffectDriver, &this->pdrv);
} else {
hres = E_NOTIMPL;
}
}
#endif
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | IDirectInputEffectShepherd | New |
*
* Create a new instance of an IDirectInputEffectShepherd object.
*
* @parm IN HKEY | hkFF |
*
* Force feedback registry key.
*
* @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.
*
* @returns
*
* Standard OLE <t HRESULT>.
*
*****************************************************************************/
STDMETHODIMP
CEShep_New(HKEY hkFF, PUNK punkOuter, RIID riid, PPV ppvObj)
{
HRESULT hres;
EnterProcR(IDirectInputEffectShepherd::<constructor>, (_ "G", riid));
AssertF(g_hmtxJoy);
hres = Common_NewRiid(CEShep, punkOuter, riid, ppvObj);
if (SUCCEEDED(hres)) {
/* Must use _thisPv in case of aggregation */
PES this = _thisPv(*ppvObj);
if (SUCCEEDED(hres = CEShep_InitInstance(this, hkFF))) {
} else {
Invoke_Release(ppvObj);
}
}
ExitOleProcPpvR(ppvObj);
return hres;
}
/*****************************************************************************
*
* The long-awaited vtbls and templates
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
#define CEShep_Signature 0x50454853 /* "SHEP" */
Interface_Template_Begin(CEShep)
Primary_Interface_Template(CEShep, IDirectInputEffectShepherd)
Interface_Template_End(CEShep)
Primary_Interface_Begin(CEShep, IDirectInputEffectShepherd)
CEShep_DeviceID,
CEShep_GetVersions,
CEShep_Escape,
CEShep_DeviceEscape,
CEShep_SetGain,
CEShep_SetForceFeedbackState,
CEShep_GetForceFeedbackState,
CEShep_DownloadEffect,
CEShep_DestroyEffect,
CEShep_StartEffect,
CEShep_StopEffect,
CEShep_GetEffectStatus,
CEShep_SetGlobalGain,
Primary_Interface_End(CEShep, IDirectInputEffectShepherd)