1317 lines
34 KiB
C
1317 lines
34 KiB
C
/*****************************************************************************
|
|
*
|
|
* 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)
|
|
|