1342 lines
35 KiB
C
1342 lines
35 KiB
C
/*****************************************************************************
|
|
*
|
|
* DIEffJ.c
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Dummy effect driver for joystick.
|
|
*
|
|
* Contents:
|
|
*
|
|
* CJoyEff_CreateInstance
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "dinputpr.h"
|
|
|
|
#if defined(IDirectInputDevice2Vtbl) && defined(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 IDirectInputDevice2::Escape> or
|
|
* <mf IDirectInputEffect::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 IDirectInputDevice2::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
|