5646 lines
165 KiB
C
5646 lines
165 KiB
C
/*****************************************************************************
|
|
*
|
|
* DIDev.c
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
* Abstract:
|
|
*
|
|
* The standard implementation of IDirectInputDevice.
|
|
*
|
|
* This is the device-independent part. the device-dependent
|
|
* part is handled by the IDirectInputDeviceCallback.
|
|
*
|
|
* And the IDirectInputEffect support lives in didevef.c.
|
|
*
|
|
* Contents:
|
|
*
|
|
* CDIDev_CreateInstance
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "dinputpr.h"
|
|
#define INCLUDED_BY_DIDEV
|
|
#include "didev.h"
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Declare the interfaces we will be providing.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
Interface_Template_Begin(CDIDev)
|
|
Primary_Interface_Template(CDIDev, TFORM(ThisInterfaceT))
|
|
Secondary_Interface_Template(CDIDev, SFORM(ThisInterfaceT))
|
|
Interface_Template_End(CDIDev)
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @global PDD * | g_rgpddForeground |
|
|
*
|
|
* A list of all devices which have obtained foreground
|
|
* acquisition. Items on the list have been
|
|
* held (not AddRef'd).
|
|
*
|
|
* @global UINT | g_cpddForeground |
|
|
*
|
|
* The number of entries in <p g_rgpddForeground>. When foreground
|
|
* activation is lost, all objects in the array are unacquired.
|
|
*
|
|
*
|
|
* @global UINT | g_cpddForegroundMax |
|
|
*
|
|
* The size of the <p g_rgpddForeground> array, including dummy
|
|
* spaces that are not yet in use.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
|
|
/*
|
|
* ISSUE-2001/03/29-timgill We assume that all-zeros is a valid initialization.
|
|
*/
|
|
GPA g_gpaExcl;
|
|
#define g_hgpaExcl (&g_gpaExcl)
|
|
|
|
#else
|
|
|
|
//
|
|
// ISSUE-2001/03/29-timgill The following variables should go into diexcl.c or didevex.c
|
|
//
|
|
|
|
PDD *g_rgpddForeground;
|
|
UINT g_cpddForeground;
|
|
UINT g_cpddForegroundMax;
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | NotAcquired |
|
|
*
|
|
* Check that the device is not acquired.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns
|
|
* <c S_OK> if all is well, or <c DIERR_ACQUIRED> if
|
|
* the device is acquired.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef XDEBUG
|
|
|
|
#define IDirectInputDevice_NotAcquired_(pdd, z) \
|
|
_IDirectInputDevice_NotAcquired_(pdd) \
|
|
|
|
#endif
|
|
|
|
HRESULT INLINE
|
|
IDirectInputDevice_NotAcquired_(PDD this, LPCSTR s_szProc)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(!this->fAcquired)
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("ERROR %s: May not be called while device is acquired", s_szProc);
|
|
hres = DIERR_ACQUIRED;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
#define IDirectInputDevice_NotAcquired(pdd) \
|
|
IDirectInputDevice_NotAcquired_(pdd, s_szProc) \
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | IsExclAcquired |
|
|
*
|
|
* Check that the device is acquired exclusively.
|
|
*
|
|
* The device critical section must already be held.
|
|
*
|
|
* @cwrap PDD | this
|
|
*
|
|
* @returns
|
|
*
|
|
* <c S_OK> if the device is exclusively acquired.
|
|
*
|
|
* <c DIERR_INPUTLOST> if acquisition has been lost.
|
|
*
|
|
* <c DIERR_NOTEXCLUSIVEACQUIRED> the device is acquired,
|
|
* but not exclusively, or if the device is not acquired
|
|
* at all.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_IsExclAcquired_(PDD this, LPCSTR s_szProc)
|
|
{
|
|
HRESULT hres;
|
|
|
|
AssertF(CDIDev_InCrit(this));
|
|
|
|
if(this->discl & DISCL_EXCLUSIVE)
|
|
{
|
|
if(this->fAcquired)
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
hres = this->hresNotAcquired;
|
|
if(hres == DIERR_NOTACQUIRED)
|
|
{
|
|
hres = DIERR_NOTEXCLUSIVEACQUIRED;
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
hres = DIERR_NOTEXCLUSIVEACQUIRED;
|
|
}
|
|
|
|
if(s_szProc && hres == DIERR_NOTEXCLUSIVEACQUIRED)
|
|
{
|
|
RPF("ERROR %s: Device is not acquired in exclusive mode", s_szProc);
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputDevice | EnterCrit |
|
|
*
|
|
* Enter the object critical section.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void EXTERNAL
|
|
CDIDev_EnterCrit_(struct CDIDev *this, LPCTSTR lptszFile, UINT line)
|
|
{
|
|
#ifdef XDEBUG
|
|
if( ! _TryEnterCritSec(&this->crst) )
|
|
{
|
|
SquirtSqflPtszV(sqflCrit, TEXT("Device CritSec blocked @%s,%d"), lptszFile, line);
|
|
EnterCriticalSection(&this->crst);
|
|
}
|
|
|
|
SquirtSqflPtszV(sqflCrit, TEXT("Device CritSec Entered @%s,%d"), lptszFile, line);
|
|
#else
|
|
EnterCriticalSection(&this->crst);
|
|
#endif
|
|
|
|
this->thidCrit = GetCurrentThreadId();
|
|
InterlockedIncrement(&this->cCrit);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputDevice | LeaveCrit |
|
|
*
|
|
* Leave the object critical section.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void EXTERNAL
|
|
CDIDev_LeaveCrit_(struct CDIDev *this, LPCTSTR lptszFile, UINT line)
|
|
{
|
|
#ifdef XDEBUG
|
|
AssertF(this->cCrit);
|
|
AssertF(this->thidCrit == GetCurrentThreadId());
|
|
SquirtSqflPtszV(sqflCrit | sqflVerbose, TEXT("Device CritSec Leaving @%s,%d"), lptszFile, line);
|
|
#endif
|
|
|
|
if(InterlockedDecrement(&this->cCrit) == 0)
|
|
{
|
|
this->thidCrit = 0;
|
|
}
|
|
LeaveCriticalSection(&this->crst);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputDevice | SetNotifyEvent |
|
|
*
|
|
* Set the event associated with the device, if any.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void EXTERNAL
|
|
CDIDev_SetNotifyEvent(PDD this)
|
|
{
|
|
if(this->hNotify)
|
|
{
|
|
SetEvent(this->hNotify);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputDevice | SetForcedUnacquiredFlag |
|
|
*
|
|
* When forced unacquired happens, set the fOnceForcedUnacquired flag.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void EXTERNAL
|
|
CDIDev_SetForcedUnacquiredFlag(PDD this)
|
|
{
|
|
/*
|
|
* This is an internal interface, so we can skimp on validation.
|
|
*/
|
|
this->fOnceForcedUnacquired = 1;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method BOOL | CDIDev | InCrit |
|
|
*
|
|
* Nonzero if we are in the critical section.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
BOOL INTERNAL
|
|
CDIDev_InCrit(PDD this)
|
|
{
|
|
return this->cCrit && this->thidCrit == GetCurrentThreadId();
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method BOOL | IDirectInputDevice | IsConsistent |
|
|
*
|
|
* Check that various state variables are consistent.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#define VerifyF(f) if (!(f)) fRc = 0
|
|
|
|
BOOL INTERNAL
|
|
CDIDev_IsConsistent(PDD this)
|
|
{
|
|
BOOL fRc = 1;
|
|
|
|
/*
|
|
* If acquired, then we must have a translation table, a state manager,
|
|
* and a device callback.
|
|
*/
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
VerifyF(this->pdix && this->GetState && this->pdcb != c_pdcbNil);
|
|
}
|
|
|
|
/*
|
|
* If buffering, then must have a device callback.
|
|
*/
|
|
if(this->celtBuf)
|
|
{
|
|
VerifyF(this->pdcb != c_pdcbNil);
|
|
}
|
|
|
|
/*
|
|
* If managing an instance, then make sure buffer variables are
|
|
* consistent.
|
|
*/
|
|
if(this->pvi)
|
|
{
|
|
if(this->celtBuf)
|
|
{
|
|
VerifyF( this->pvi->pBuffer
|
|
&& this->pvi->pEnd
|
|
&& this->pvi->pHead
|
|
&& this->pvi->pTail);
|
|
VerifyF(this->pvi->pBuffer < this->pvi->pEnd);
|
|
VerifyF(fInOrder((DWORD)(UINT_PTR)this->pvi->pBuffer,
|
|
(DWORD)(UINT_PTR)this->pvi->pHead,
|
|
(DWORD)(UINT_PTR)this->pvi->pEnd));
|
|
VerifyF(fInOrder((DWORD)(UINT_PTR)this->pvi->pBuffer,
|
|
(DWORD)(UINT_PTR)this->pvi->pTail,
|
|
(DWORD)(UINT_PTR)this->pvi->pEnd));
|
|
} else
|
|
{
|
|
VerifyF( this->pvi->pBuffer == 0
|
|
&& this->pvi->pEnd == 0
|
|
&& this->pvi->pHead == 0
|
|
&& this->pvi->pTail == 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The cooperative levels must match the cached window handle.
|
|
*/
|
|
VerifyF(fLimpFF(this->discl & (DISCL_FOREGROUND | DISCL_EXCLUSIVE),
|
|
this->hwnd));
|
|
|
|
return fRc;
|
|
}
|
|
|
|
#undef VerifyF
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | QueryInterface |
|
|
*
|
|
* Gives a client access to other interfaces on an object.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @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 | IDirectInputDevice | AddRef |
|
|
*
|
|
* Increments the reference count for the interface.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns the object reference count.
|
|
*
|
|
* @xref OLE documentation for <mf IUnknown::AddRef>.
|
|
*
|
|
*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | 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 LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns the object reference count.
|
|
*
|
|
* @xref OLE documentation for <mf IUnknown::Release>.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
Default_QueryInterface(CDIDev)
|
|
Default_AddRef(CDIDev)
|
|
Default_Release(CDIDev)
|
|
|
|
#else
|
|
|
|
#define CDIDev_QueryInterface Common_QueryInterface
|
|
#define CDIDev_AddRef Common_AddRef
|
|
#define CDIDev_Release Common_Release
|
|
#endif
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInput | QIHelper |
|
|
*
|
|
* Support the original IDirectInputDevice interfaces as well
|
|
* as the new IDirectInputDevice2 interfaces.
|
|
*
|
|
* @parm IN REFIID | riid |
|
|
*
|
|
* The requested interface's IID.
|
|
*
|
|
* @parm OUT LPVOID * | ppvObj |
|
|
*
|
|
* Receives a pointer to the obtained interface.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_QIHelper(PDD this, RIID riid, PPV ppvObj)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcI(CDIDev_QIHelper, (_ "pG", this, riid));
|
|
|
|
if(IsEqualIID(riid, &IID_IDirectInputDeviceA))
|
|
{
|
|
|
|
*ppvObj = &this->ddA;
|
|
OLE_AddRef(this);
|
|
hres = S_OK;
|
|
|
|
} else if(IsEqualIID(riid, &IID_IDirectInputDeviceW))
|
|
{
|
|
|
|
*ppvObj = &this->ddW;
|
|
OLE_AddRef(this);
|
|
hres = S_OK;
|
|
|
|
#ifdef IDirectInputDevice7Vtbl
|
|
} else if(IsEqualIID(riid, &IID_IDirectInputDevice2A))
|
|
{
|
|
|
|
*ppvObj = &this->ddA;
|
|
OLE_AddRef(this);
|
|
hres = S_OK;
|
|
|
|
} else if(IsEqualIID(riid, &IID_IDirectInputDevice2W))
|
|
{
|
|
|
|
*ppvObj = &this->ddW;
|
|
OLE_AddRef(this);
|
|
hres = S_OK;
|
|
#endif
|
|
} else
|
|
{
|
|
hres = Common_QIHelper(this, riid, ppvObj);
|
|
}
|
|
|
|
ExitOleProcPpv(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
#else
|
|
|
|
#define CDIDev_QIHelper Common_QIHelper
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func HRESULT | CDIDev_Reset |
|
|
*
|
|
* Releases all the resources of a generic device that
|
|
* are associated with a particular device instance.
|
|
*
|
|
* This method is called in preparation for reinitialization.
|
|
*
|
|
* It is the responsibility of the caller to have taken
|
|
* any necessary critical sections.
|
|
*
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* Object being reset. Note that it may not have been
|
|
* completely initialized, so everything should be done
|
|
* carefully.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_Reset(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(!this->fAcquired)
|
|
{
|
|
|
|
/*
|
|
* Note! that we must release the driver before releasing
|
|
* the callback, because the callback will unload the
|
|
* driver DLL.
|
|
*
|
|
* We cannot allow people to reset the device
|
|
* while there are still effects outstanding,
|
|
* because that would cause us to throw away the
|
|
* callback while the effects are still using
|
|
* the effect driver!
|
|
*/
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
if(this->gpaEff.cpv == 0)
|
|
{
|
|
Invoke_Release(&this->pes);
|
|
#endif
|
|
Invoke_Release(&this->pdcb);
|
|
this->pdcb = c_pdcbNil;
|
|
FreePpv(&this->pdix);
|
|
FreePpv(&this->rgiobj);
|
|
#ifdef BUGGY_DX7_WINNT
|
|
FreePpv(&this->pdix2);
|
|
FreePpv(&this->rgiobj2);
|
|
#endif //BUGGY_DX7_WINNT
|
|
FreePpv(&this->pvBuffer);
|
|
FreePpv(&this->pvLastBuffer);
|
|
FreePpv(&this->rgdwAxesOfs);
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
FreePpv(&this->rgemi);
|
|
#endif
|
|
FreePpv(&this->rgdwPOV);
|
|
|
|
AssertF(!this->fAcquired);
|
|
AssertF(!this->fAcquiredInstance);
|
|
|
|
if(this->hNotify)
|
|
{
|
|
CloseHandle(this->hNotify);
|
|
}
|
|
|
|
ZeroBuf(&this->hwnd, FIELD_OFFSET(DD, celtBufMax) -
|
|
FIELD_OFFSET(DD, hwnd));
|
|
ZeroX(this->guid);
|
|
this->celtBufMax = 1023;
|
|
this->GetDeviceState = CDIDev_GetAbsDeviceState;
|
|
this->hresNotAcquired = DIERR_NOTACQUIRED;
|
|
this->fCook = 0;
|
|
|
|
AssertF(this->hNotify == 0);
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
AssertF(this->cemi == 0);
|
|
AssertF(this->didcFF == 0);
|
|
this->dwGain = 10000; /* Default to full gain */
|
|
this->dwAutoCenter = DIPROPAUTOCENTER_ON; /* Default to centered */
|
|
GPA_InitFromZero(&this->gpaEff);
|
|
#endif
|
|
|
|
/*
|
|
* If the app does not initialize, then assume DX 3.
|
|
*/
|
|
this->didftInstance = 0x0000FF00;
|
|
|
|
hres = S_OK;
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
} else
|
|
{
|
|
RPF("IDirectInputDevice::Initialize: Device still has effects");
|
|
hres = DIERR_HASEFFECTS;
|
|
}
|
|
#endif
|
|
} else
|
|
{
|
|
RPF("IDirectInputDevice::Initialize: Device is busy");
|
|
hres = DIERR_ACQUIRED;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | CDIDev_AppFinalize |
|
|
*
|
|
* The application has performed its final release.
|
|
*
|
|
* If the device is acquired, then unacquire it.
|
|
*
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
* Release the holds on all the created effects that
|
|
* we have been hanging onto for enumeration purposes.
|
|
#endif
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* Object being released. Note that it may not have been
|
|
* completely initialized, so everything should be done
|
|
* carefully.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INTERNAL
|
|
CDIDev_AppFinalize(PV pvObj)
|
|
{
|
|
PDD this = pvObj;
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
RPF("IDirectInputDevice::Release: Forgot to call Unacquire()");
|
|
CDIDev_InternalUnacquire(pvObj);
|
|
}
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
if(this->fCritInited)
|
|
{
|
|
/*
|
|
* Stop all the effects, if they are playing.
|
|
*
|
|
* Then unhold them (because we're done).
|
|
*
|
|
* ISSUE-2001/03/29-timgill Need to totally remove all effects created by a destroyed device
|
|
* We also need to neuter them so they don't
|
|
* do anything any more. Otherwise, an app might
|
|
* destroy the parent device and then try to mess with
|
|
* an effect created by that device after the device
|
|
* is gone.
|
|
*
|
|
* Note that we cannot call the effect while inside our
|
|
* private little critical section, because the effect
|
|
* may need to do crazy things to stop itself.
|
|
*
|
|
* (It will almost certainly call back up into the device
|
|
* to remove itself from the list of created effects.)
|
|
*/
|
|
UINT ipdie;
|
|
PPDIE rgpdie;
|
|
UINT cpdie;
|
|
|
|
CDIDev_EnterCrit(this);
|
|
|
|
rgpdie = (PV)this->gpaEff.rgpv;
|
|
cpdie = this->gpaEff.cpv;
|
|
GPA_Init(&this->gpaEff);
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
for(ipdie = 0; ipdie < cpdie; ipdie++)
|
|
{
|
|
AssertF(rgpdie[ipdie]);
|
|
// 7/19/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
|
|
SquirtSqflPtszV(sqfl | sqflError,
|
|
TEXT("Device %p forgot to destroy effect %08x"),
|
|
this, rgpdie[ipdie]);
|
|
|
|
IDirectInputEffect_Stop(rgpdie[ipdie]);
|
|
Common_Unhold(rgpdie[ipdie]);
|
|
}
|
|
FreePpv(&rgpdie);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func void | CDIDev_Finalize |
|
|
*
|
|
* Releases the resources of a generic device.
|
|
*
|
|
* @parm PV | pvObj |
|
|
*
|
|
* Object being released. Note that it may not have been
|
|
* completely initialized, so everything should be done
|
|
* carefully.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INTERNAL
|
|
CDIDev_Finalize(PV pvObj)
|
|
{
|
|
HRESULT hres;
|
|
PDD this = pvObj;
|
|
|
|
#ifdef XDEBUG
|
|
if(this->cCrit)
|
|
{
|
|
RPF("IDirectInputDevice::Release: Another thread is using the object; crash soon!");
|
|
}
|
|
#endif
|
|
|
|
AssertF(!this->fAcquired);
|
|
|
|
/*
|
|
* Note that we cannot take the critical section because it
|
|
* might not exist. (We might've died during initialization.)
|
|
* Fortunately, we finalize only after every possible client
|
|
* (both internal and external) has done its final Release(),
|
|
* so it's impossible for any other method to get called at
|
|
* this point.
|
|
*/
|
|
hres = CDIDev_Reset(this);
|
|
AssertF(SUCCEEDED(hres));
|
|
|
|
if(this->fCritInited)
|
|
{
|
|
DeleteCriticalSection(&this->crst);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | CDIDev | GetVersions |
|
|
*
|
|
* Obtain version information from the device.
|
|
*
|
|
* First try to get it from the effect driver. If
|
|
* that doesn't work, then get it from the VxD.
|
|
* And if that doesn't work, then tough.
|
|
*
|
|
* @parm IN OUT LPDIDRIVERVERSIONS | pvers |
|
|
*
|
|
* Receives version information.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INLINE
|
|
CDIDev_GetVersions(PDD this, LPDIDRIVERVERSIONS pvers)
|
|
{
|
|
HRESULT hres;
|
|
|
|
/*
|
|
* Pre-fill with zeros in case nobody implements GetVersions.
|
|
*/
|
|
pvers->dwSize = cbX(*pvers);
|
|
pvers->dwFirmwareRevision = 0;
|
|
pvers->dwHardwareRevision = 0;
|
|
pvers->dwFFDriverVersion = 0;
|
|
|
|
hres = CDIDev_CreateEffectDriver(this);
|
|
if(SUCCEEDED(hres) &&
|
|
SUCCEEDED(hres = this->pes->lpVtbl->
|
|
GetVersions(this->pes, pvers)))
|
|
{
|
|
} else
|
|
{
|
|
hres = this->pdcb->lpVtbl->GetVersions(this->pdcb, pvers);
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetCapabilities |
|
|
*
|
|
* Obtains information about the device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN OUT LPDIDEVCAPS | lpdc |
|
|
*
|
|
* Points to a <t DIDEVCAPS> structure that is filled in
|
|
* by the function. The <e DIDEVCAPS.dwSize>
|
|
* field "must" be filled in
|
|
* by the application before calling this method.
|
|
* See the documentation of the <t DIDEVCAPS> structure
|
|
* for additional information.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpDirectInputDevice> or
|
|
* <p lpdc> parameter is invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetCapabilities(PV pdd, LPDIDEVCAPS pdc _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::GetCapabilities, (_ "pp", pdd, pdc));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb2(pdc,
|
|
DIDEVCAPS_DX5,
|
|
DIDEVCAPS_DX3, 1)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
|
|
CDIDev_EnterCrit(this);
|
|
|
|
/*
|
|
* For the convenience of the callback, zero out all the fields,
|
|
* save for the dwSize.
|
|
*/
|
|
ZeroBuf(pvAddPvCb(pdc, cbX(DWORD)), pdc->dwSize - cbX(DWORD));
|
|
hres = this->pdcb->lpVtbl->GetCapabilities(this->pdcb, pdc);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
/*
|
|
* We'll handle the DIDC_EMULATED and
|
|
* DIDC_POLLEDDATAFORMAT bits to save the callback
|
|
* some trouble.
|
|
*/
|
|
AssertF(this->pvi);
|
|
if(this->pvi->fl & VIFL_EMULATED)
|
|
{
|
|
pdc->dwFlags |= DIDC_EMULATED;
|
|
}
|
|
if(this->fPolledDataFormat)
|
|
{
|
|
pdc->dwFlags |= DIDC_POLLEDDATAFORMAT;
|
|
}
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
/*
|
|
* Add in the force feedback flags, too.
|
|
*/
|
|
pdc->dwFlags |= this->didcFF;
|
|
|
|
/*
|
|
* If the caller wants force feedback parameters, then
|
|
* set them, too.
|
|
*/
|
|
if(pdc->dwSize >= cbX(DIDEVCAPS_DX5))
|
|
{
|
|
DIDRIVERVERSIONS vers;
|
|
|
|
pdc->dwFFSamplePeriod = this->ffattr.dwFFSamplePeriod;
|
|
pdc->dwFFMinTimeResolution = this->ffattr.dwFFMinTimeResolution;
|
|
|
|
CDIDev_GetVersions(this, &vers);
|
|
pdc->dwFirmwareRevision = vers.dwFirmwareRevision;
|
|
pdc->dwHardwareRevision = vers.dwHardwareRevision;
|
|
pdc->dwFFDriverVersion = vers.dwFFDriverVersion;
|
|
|
|
}
|
|
#endif
|
|
hres = S_OK;
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
ScrambleBit(&pdc->dwDevType, DIDEVTYPE_RANDOM);
|
|
ScrambleBit(&pdc->dwFlags, DIDC_RANDOM);
|
|
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(GetCapabilities, (PV pdd, LPDIDEVCAPS pdc), (pdd, pdc THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_GetCapabilitiesA CDIDev_GetCapabilities
|
|
#define CDIDev_GetCapabilitiesW CDIDev_GetCapabilities
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | GetDataFormat |
|
|
*
|
|
* Get the data format for the device if we don't have it already.
|
|
*
|
|
* @parm PDD | this |
|
|
*
|
|
* Device object.
|
|
*
|
|
* @returns
|
|
*
|
|
* COM return code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDataFormat(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
LPDIDATAFORMAT pdf;
|
|
|
|
/*
|
|
* If the DIDATAFORMAT structure changes, you also need to invent
|
|
* a new DCB message (DIDM_GETDATAFORMAT2), and then do
|
|
* the right thing when faced with a mix of old and new.
|
|
*/
|
|
|
|
hres = this->pdcb->lpVtbl->GetDataFormat(this->pdcb, &pdf);
|
|
|
|
|
|
/*
|
|
* Note! We don't support external drivers in this release,
|
|
* so it's okay to treat these are Assert's and not try to recover.
|
|
*/
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
AssertF(pdf->dwSize == sizeof(this->df));
|
|
this->df = *pdf;
|
|
|
|
AssertF(!IsBadReadPtr(pdf->rgodf, cbCxX(pdf->dwNumObjs, ODF)));
|
|
|
|
/*
|
|
* Prepare the axis goo in case the app sets relative mode.
|
|
*/
|
|
if(SUCCEEDED(hres = ReallocCbPpv(pdf->dwDataSize,
|
|
&this->pvLastBuffer)) &&
|
|
SUCCEEDED(hres = ReallocCbPpv(cbCdw(pdf->dwNumObjs),
|
|
&this->rgdwAxesOfs)))
|
|
{
|
|
|
|
UINT iobj;
|
|
this->cAxes = 0;
|
|
|
|
for(iobj = 0; iobj < pdf->dwNumObjs; iobj++)
|
|
{
|
|
AssertF(pdf->rgodf[iobj].dwOfs < pdf->dwDataSize);
|
|
if(pdf->rgodf[iobj].dwType & DIDFT_AXIS)
|
|
{
|
|
this->rgdwAxesOfs[this->cAxes++] = pdf->rgodf[iobj].dwOfs;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | GetPolled |
|
|
*
|
|
* Determine whether the device is polled.
|
|
*
|
|
* @parm PDD | this |
|
|
*
|
|
* Device object.
|
|
*
|
|
* @returns
|
|
*
|
|
* COM result code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetPolled(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
DIDEVCAPS_DX3 dc;
|
|
|
|
/*
|
|
* We intentionally use a DIDEVCAPS_DX3 because going for
|
|
* a full DIDEVCAPS_DX5 requires us to load the force
|
|
* feedback driver which is pointless for our current
|
|
* goal.
|
|
*/
|
|
ZeroX(dc);
|
|
dc.dwSize = cbX(dc);
|
|
|
|
hres = this->pdcb->lpVtbl->GetCapabilities(this->pdcb, (PV)&dc);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
if(dc.dwFlags & DIDC_POLLEDDEVICE)
|
|
{
|
|
this->hresPolled = DI_POLLEDDEVICE;
|
|
} else
|
|
{
|
|
this->hresPolled = S_OK;
|
|
}
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | GetObjectInfoHelper |
|
|
*
|
|
* Set up all the information we can deduce ourselves and
|
|
* have the callback finish.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPCDIPROPINFO | ppropi |
|
|
*
|
|
* Object descriptor.
|
|
*
|
|
* @parm LPDIDEVICEOBJECTINSTANCEW | pdoiW |
|
|
*
|
|
* Structure to receive result.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetObjectInfoHelper(PDD this, LPCDIPROPINFO ppropi,
|
|
LPDIDEVICEOBJECTINSTANCEW pdoiW)
|
|
{
|
|
HRESULT hres;
|
|
|
|
AssertF(IsValidSizeDIDEVICEOBJECTINSTANCEW(pdoiW->dwSize));
|
|
pdoiW->guidType = *this->df.rgodf[ppropi->iobj].pguid;
|
|
pdoiW->dwType = this->df.rgodf[ppropi->iobj].dwType;
|
|
pdoiW->dwFlags = this->df.rgodf[ppropi->iobj].dwFlags;
|
|
ScrambleBit(&pdoiW->dwFlags, DIDOI_RANDOM);
|
|
if( this->pdix )
|
|
{
|
|
/* User data format offset */
|
|
pdoiW->dwOfs = this->pdix[ppropi->iobj].dwOfs;
|
|
} else
|
|
{
|
|
#ifdef BUGGY_DX7_WINNT
|
|
if( (this->dwVersion < 0x700) && (this->dwVersion != 0x5B2) &&
|
|
this->pdix2 )
|
|
{
|
|
pdoiW->dwOfs = this->pdix2[ppropi->iobj].dwOfs;
|
|
} else
|
|
#endif //BUGGY_DX7_WINNT
|
|
{
|
|
/* Internal data format offset */
|
|
pdoiW->dwOfs = this->df.rgodf[ppropi->iobj].dwOfs;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Wipe out everything starting at tszName.
|
|
*/
|
|
ZeroBuf(&pdoiW->tszName,
|
|
pdoiW->dwSize - FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, tszName));
|
|
|
|
hres = this->pdcb->lpVtbl->GetObjectInfo(this->pdcb, ppropi, pdoiW);
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | EnumObjects |
|
|
*
|
|
* Enumerate the input sources (buttons, axes)
|
|
* available on a device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN LPDIENUMDEVICEOBJECTSCALLBACK | lpCallback |
|
|
*
|
|
* Callback function.
|
|
*
|
|
* @parm IN LPVOID | pvRef |
|
|
*
|
|
* Reference data (context) for callback.
|
|
*
|
|
* @parm IN DWORD | fl |
|
|
*
|
|
* Flags specifying the type(s) of objects to be enumerated.
|
|
* See the section "DirectInput Data Format Types" for a
|
|
* list of flags that can be passed.
|
|
*
|
|
* Furthermore, the enumeration can be restricted to objects
|
|
* from a single HID link collection by using the
|
|
* <f DIDFT_ENUMCOLLECTION> macro.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
* Note that if the callback stops the enumeration prematurely,
|
|
* the enumeration is considered to have succeeded.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p fl> parameter contains invalid flags, or the callback
|
|
* procedure returned an invalid status code.
|
|
*
|
|
* @cb BOOL CALLBACK | DIEnumDeviceObjectsProc |
|
|
*
|
|
* An application-defined callback function that receives
|
|
* DirectInputDevice objects as a result of a call to the
|
|
* <om IDirectInputDevice::EnumObjects> method.
|
|
*
|
|
* @parm IN LPCDIDEVICEOBJECTINSTANCE | lpddoi |
|
|
*
|
|
* A <t DIDEVICEOBJECTINSTANCE> structure which describes
|
|
* the object being enumerated.
|
|
*
|
|
* @parm IN OUT LPVOID | pvRef |
|
|
* Specifies the application-defined value given in the
|
|
* <mf IDirectInputDevice::EnumObjects> function.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns <c DIENUM_CONTINUE> to continue the enumeration
|
|
* or <c DIENUM_STOP> to stop the enumeration.
|
|
*
|
|
* @ex
|
|
*
|
|
* To enumerate all axis objects:
|
|
*
|
|
* |
|
|
*
|
|
* // C++
|
|
* HRESULT hr = pDevice->EnumObjects(EnumProc, RefData, DIDFT_AXIS);
|
|
*
|
|
* // C
|
|
* hr = IDirectInputDevice_EnumObjects(pDevice, EnumProc, RefData,
|
|
* DIDFT_AXIS);
|
|
*
|
|
* @ex
|
|
*
|
|
* To enumerate all objects in the third HID link collection:
|
|
*
|
|
* |
|
|
*
|
|
* // C++
|
|
* HRESULT hr = pDevice->EnumObjects(EnumProc, RefData,
|
|
* DIDFT_ENUMCOLLECTION(3));
|
|
*
|
|
* // C
|
|
* hr = IDirectInputDevice_EnumObjects(pDevice, EnumProc, RefData,
|
|
* DIDFT_ENUMCOLLECTION(3));
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_EnumObjectsW
|
|
(PV pddW, LPDIENUMDEVICEOBJECTSCALLBACKW pec, LPVOID pvRef, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::EnumObjectsW, (_ "ppx", pddW, pec, fl));
|
|
|
|
if(SUCCEEDED(hres = hresPvW(pddW)) &&
|
|
SUCCEEDED(hres = hresFullValidPfn(pec, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidFl(fl, DIDFT_ENUMVALID, 3)))
|
|
{
|
|
PDD this = _thisPvNm(pddW, ddW);
|
|
DWORD flExclude;
|
|
WORD wCollection;
|
|
|
|
/*
|
|
* We snapshot the object information underneath the critical
|
|
* section so we don't blow up if another thread Reset()s
|
|
* the device in the middle of an enumeration. The DIDATAFORMAT
|
|
* contains pointers to the dcb, so we need to AddRef the
|
|
* dcb as well.
|
|
*/
|
|
AssertF(!CDIDev_InCrit(this));
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if(this->pdcb != c_pdcbNil)
|
|
{
|
|
DIPROPINFO propi;
|
|
DIDATAFORMAT df;
|
|
IDirectInputDeviceCallback *pdcb;
|
|
|
|
pdcb = this->pdcb;
|
|
OLE_AddRef(pdcb);
|
|
df = this->df;
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
AssertF(!CDIDev_InCrit(this));
|
|
|
|
/*
|
|
* If the client is DX3, don't enumerate things that
|
|
* didn't exist in DX3.
|
|
*/
|
|
if(this->dwVersion <= 0x0300)
|
|
{
|
|
flExclude = DIDFT_ALLOBJS & ~DIDFT_ALLOBJS_DX3;
|
|
} else
|
|
{
|
|
flExclude = 0;
|
|
|
|
/* Exclude alises if necessary */
|
|
if( !(fl & DIDFT_ALIAS) )
|
|
{
|
|
flExclude |= DIDFT_ALIAS;
|
|
} else
|
|
{
|
|
fl &= ~DIDFT_ALIAS;
|
|
}
|
|
|
|
/* Exclude Vendor Defined fields */
|
|
if( !(fl & DIDFT_VENDORDEFINED) )
|
|
{
|
|
flExclude |= DIDFT_VENDORDEFINED;
|
|
} else
|
|
{
|
|
fl &= ~DIDFT_VENDORDEFINED;
|
|
}
|
|
}
|
|
|
|
if(fl == DIDFT_ALL)
|
|
{
|
|
fl = DIDFT_ALLOBJS;
|
|
}
|
|
|
|
/*
|
|
* Pull out the link collection we are enumerating.
|
|
* Note: Backwards compatibility hack. We can't
|
|
* use link collection 0 to mean "no parent" because
|
|
* 0 means "don't care". So instead, we use 0xFFFF
|
|
* to mean "no parent". This means that we need to
|
|
* interchange 0 and 0xFFFF before entering the main
|
|
* loop.
|
|
*/
|
|
wCollection = DIDFT_GETCOLLECTION(fl);
|
|
switch(wCollection)
|
|
{
|
|
|
|
case 0:
|
|
wCollection = 0xFFFF;
|
|
break;
|
|
|
|
case DIDFT_GETCOLLECTION(DIDFT_NOCOLLECTION):
|
|
wCollection = 0;
|
|
break;
|
|
}
|
|
|
|
propi.pguid = 0;
|
|
|
|
#ifdef BUGGY_DX7_WINNT
|
|
//Whistler bug 151479 - for pre-DX7 apps on WINNT only, make sure that the
|
|
//enumeration order is same as the winmm one
|
|
if( (this->dwVersion < 0x700) && (this->dwVersion != 0x5B2) &&
|
|
(this->pdix2) && (this->rgiobj2))
|
|
{
|
|
UINT iobj;
|
|
|
|
for(iobj = 0; iobj < this->dwDataSize2; iobj++)
|
|
{
|
|
propi.iobj = this->rgiobj2[iobj];
|
|
if (propi.iobj == -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
propi.dwDevType = df.rgodf[propi.iobj].dwType;
|
|
if((propi.dwDevType & fl & DIDFT_TYPEMASK) &&
|
|
fHasAllBitsFlFl(propi.dwDevType, fl & DIDFT_ATTRMASK) &&
|
|
!(propi.dwDevType & flExclude))
|
|
{
|
|
DIDEVICEOBJECTINSTANCEW doiW;
|
|
doiW.dwSize = cbX(doiW);
|
|
|
|
hres = CDIDev_GetObjectInfoHelper(this, &propi, &doiW);
|
|
if(SUCCEEDED(hres) &&
|
|
fLimpFF(wCollection != 0xFFFF,
|
|
doiW.wCollectionNumber == wCollection))
|
|
{
|
|
BOOL fRc = Callback(pec, &doiW, pvRef);
|
|
|
|
switch(fRc)
|
|
{
|
|
case DIENUM_STOP: goto enumdoneok;
|
|
case DIENUM_CONTINUE: break;
|
|
default:
|
|
RPF("IDirectInputDevice::EnumObjects: Invalid return value from enumeration callback");
|
|
ValidationException();
|
|
break;
|
|
}
|
|
} else
|
|
{
|
|
goto enumdonefail;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif //BUGGY_DX7_WINNT
|
|
{
|
|
for(propi.iobj = 0; propi.iobj < df.dwNumObjs; propi.iobj++)
|
|
{
|
|
propi.dwDevType = df.rgodf[propi.iobj].dwType;
|
|
if((propi.dwDevType & fl & DIDFT_TYPEMASK) &&
|
|
fHasAllBitsFlFl(propi.dwDevType, fl & DIDFT_ATTRMASK) &&
|
|
!(propi.dwDevType & flExclude))
|
|
{
|
|
DIDEVICEOBJECTINSTANCEW doiW;
|
|
doiW.dwSize = cbX(doiW);
|
|
|
|
hres = CDIDev_GetObjectInfoHelper(this, &propi, &doiW);
|
|
if(SUCCEEDED(hres) &&
|
|
fLimpFF(wCollection != 0xFFFF,
|
|
doiW.wCollectionNumber == wCollection))
|
|
{
|
|
BOOL fRc = Callback(pec, &doiW, pvRef);
|
|
|
|
switch(fRc)
|
|
{
|
|
case DIENUM_STOP: goto enumdoneok;
|
|
case DIENUM_CONTINUE: break;
|
|
default:
|
|
RPF("IDirectInputDevice::EnumObjects: Invalid return value from enumeration callback");
|
|
ValidationException();
|
|
break;
|
|
}
|
|
} else
|
|
{
|
|
goto enumdonefail;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
enumdoneok:;
|
|
hres = S_OK;
|
|
enumdonefail:;
|
|
|
|
OLE_Release(pdcb);
|
|
|
|
} else
|
|
{
|
|
CDIDev_LeaveCrit(this);
|
|
RPF("ERROR: IDirectInputDevice: Not initialized");
|
|
hres = DIERR_NOTINITIALIZED;
|
|
}
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#define CDIDev_EnumObjects2W CDIDev_EnumObjectsW
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDeviceA | EnumObjectsCallbackA |
|
|
*
|
|
* Custom callback that wraps
|
|
* <mf IDirectInputDeviceW::EnumObjects> which
|
|
* translates the UNICODE string to ANSI.
|
|
*
|
|
* @parm IN LPCDIENUMDEVICEOBJECTINSTANCE | pdoiW |
|
|
*
|
|
* Structure to be translated to ANSI.
|
|
*
|
|
* @parm IN LPVOID | pvRef |
|
|
*
|
|
* Pointer to <t struct ENUMDEVICEOBJECTINFO> which describes
|
|
* the original callback.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns whatever the original callback returned.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef struct ENUMOBJECTSINFO
|
|
{
|
|
LPDIENUMDEVICEOBJECTSCALLBACKA pecA;
|
|
PV pvRef;
|
|
} ENUMOBJECTSINFO, *PENUMOBJECTSINFO;
|
|
|
|
BOOL CALLBACK
|
|
CDIDev_EnumObjectsCallbackA(LPCDIDEVICEOBJECTINSTANCEW pdoiW, PV pvRef)
|
|
{
|
|
PENUMOBJECTSINFO peoi = pvRef;
|
|
BOOL fRc;
|
|
DIDEVICEOBJECTINSTANCEA doiA;
|
|
EnterProc(CDIDev_EnumObjectsCallbackA,
|
|
(_ "GxxWp", &pdoiW->guidType,
|
|
pdoiW->dwOfs,
|
|
pdoiW->dwType,
|
|
pdoiW->tszName, pvRef));
|
|
|
|
doiA.dwSize = cbX(doiA);
|
|
ObjectInfoWToA(&doiA, pdoiW);
|
|
|
|
fRc = peoi->pecA(&doiA, peoi->pvRef);
|
|
|
|
ExitProcX(fRc);
|
|
return fRc;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDeviceA | EnumObjects |
|
|
*
|
|
* Enumerate the input sources (buttons, axes)
|
|
* available on a device, in ANSI.
|
|
* See <mf IDirectInputDevice::EnumObjects> for more information.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN LPDIENUMDEVICEOBJECTSCALLBACK | lpCallback |
|
|
*
|
|
* Same as <mf IDirectInputDeviceW::EnumObjects>, except in ANSI.
|
|
*
|
|
* @parm IN LPVOID | pvRef |
|
|
*
|
|
* Same as <mf IDirectInputDeviceW::EnumObjects>.
|
|
*
|
|
* @parm IN DWORD | fl |
|
|
*
|
|
* Same as <mf IDirectInputDeviceW::EnumObjects>.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_EnumObjectsA
|
|
(PV pddA, LPDIENUMDEVICEOBJECTSCALLBACKA pec, LPVOID pvRef, DWORD fl)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDeviceA::EnumDevices,
|
|
(_ "pppx", pddA, pec, pvRef, fl));
|
|
|
|
/*
|
|
* EnumObjectsW will validate the rest.
|
|
*/
|
|
if(SUCCEEDED(hres = hresPvA(pddA)) &&
|
|
SUCCEEDED(hres = hresFullValidPfn(pec, 1)))
|
|
{
|
|
ENUMOBJECTSINFO eoi = { pec, pvRef};
|
|
PDD this = _thisPvNm(pddA, ddA);
|
|
|
|
hres = CDIDev_EnumObjectsW(&this->ddW, CDIDev_EnumObjectsCallbackA,
|
|
&eoi, fl);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#define CDIDev_EnumObjects2A CDIDev_EnumObjectsA
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetEventNotification |
|
|
*
|
|
* Specify the event that should be set when the device
|
|
* state changes, or turns off such notifications.
|
|
*
|
|
* A device state change is defined as any of the following:
|
|
*
|
|
* - A change in the position of an axis.
|
|
*
|
|
* - A change in the state (pressed or released) of a button.
|
|
*
|
|
* - A change in the direction of a POV control.
|
|
*
|
|
* - Loss of acquisition.
|
|
*
|
|
* "It is an error" to call <f CloseHandle> on the event
|
|
* while it has been selected into an <i IDirectInputDevice>
|
|
* object. You must call
|
|
* <mf IDirectInputDevice::SetEventNotification> with the
|
|
* <p hEvent> parameter set to NULL before closing the
|
|
* event handle.
|
|
*
|
|
* The event notification handle cannot be changed while the
|
|
* device is acquired.
|
|
*
|
|
* If the function is successful, then the application can
|
|
* use the event handle in the same manner as any other
|
|
* Win32 event handle. Examples of usage are shown below.
|
|
* For additional information on using Win32 wait functions,
|
|
* see the Win32 SDK and related documentation.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN HANDLE | hEvent |
|
|
*
|
|
* Specifies the event handle which will be set when the
|
|
* device state changes. It "must" be an event
|
|
* handle. DirectInput will <f SetEvent> the handle when
|
|
* the state of the device changes.
|
|
*
|
|
* The application should create the handle via the
|
|
* <f CreateEvent> function. If the event is created as
|
|
* an automatic-reset event, then the operating system will
|
|
* automatically reset the event once a wait has been
|
|
* satisfied. If the event is created as a manual-reset
|
|
* event, then it is the application's responsibility to
|
|
* call <f ResetEvent> to reset it. DirectInput will not
|
|
* call <f ResetEvent> for event notification handles.
|
|
*
|
|
* If the <p hEvent> is zero, then notification is disabled.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_HANDLEEXISTS>: The <i IDirectInputDevice> object
|
|
* already has an event notification handle
|
|
* associated with it. DirectInput supports only one event
|
|
* notification handle per <i IDirectInputDevice> object.
|
|
*
|
|
* <c DIERR_ACQUIRED>: The <i IDirectInputDevice> object
|
|
* has been acquired. You must <mf IDirectInputDevice::Unacquire>
|
|
* the device before you can change the notification state.
|
|
*
|
|
* <c E_INVALIDARG>: The thing isn't an event handle.
|
|
*
|
|
* @ex
|
|
*
|
|
* To check if the handle is currently set without blocking:
|
|
*
|
|
* |
|
|
*
|
|
* dwResult = WaitForSingleObject(hEvent, 0);
|
|
* if (dwResult == WAIT_OBJECT_0) {
|
|
* // Event is set. If the event was created as
|
|
* // automatic-reset, then it has also been reset.
|
|
* }
|
|
*
|
|
* @ex
|
|
*
|
|
* The following example illustrates blocking
|
|
* indefinitely until the event is set. Note that this
|
|
* behavior is <y strongly> discouraged because the thread
|
|
* will not respond to the system until the wait is
|
|
* satisfied. (In particular, the thread will not respond
|
|
* to Windows messages.)
|
|
*
|
|
* |
|
|
*
|
|
* dwResult = WaitForSingleObject(hEvent, INFINITE);
|
|
* if (dwResult == WAIT_OBJECT_0) {
|
|
* // Event has been set. If the event was created as
|
|
* // automatic-reset, then it has also been reset.
|
|
* }
|
|
*
|
|
* @ex
|
|
*
|
|
* The following example illustrates a typical message loop
|
|
* for a message-based application that uses two events.
|
|
*
|
|
* |
|
|
*
|
|
* HANDLE ah[2] = { hEvent1, hEvent2 };
|
|
*
|
|
* while (TRUE) {
|
|
*
|
|
* dwResult = MsgWaitForMultipleObjects(2, ah, FALSE,
|
|
* INFINITE, QS_ALLINPUT);
|
|
* switch (dwResult) {
|
|
* case WAIT_OBJECT_0:
|
|
* // Event 1 has been set. If the event was created as
|
|
* // automatic-reset, then it has also been reset.
|
|
* ProcessInputEvent1();
|
|
* break;
|
|
*
|
|
* case WAIT_OBJECT_0 + 1:
|
|
* // Event 2 has been set. If the event was created as
|
|
* // automatic-reset, then it has also been reset.
|
|
* ProcessInputEvent2();
|
|
* break;
|
|
*
|
|
* case WAIT_OBJECT_0 + 2:
|
|
* // A Windows message has arrived. Process messages
|
|
* // until there aren't any more.
|
|
* while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
* if (msg.message == WM_QUIT) {
|
|
* goto exitapp;
|
|
* }
|
|
* TranslateMessage(&msg);
|
|
* DispatchMessage(&msg);
|
|
* }
|
|
* break;
|
|
*
|
|
* default:
|
|
* // Unexpected error.
|
|
* Panic();
|
|
* break;
|
|
* }
|
|
* }
|
|
*
|
|
* @ex
|
|
*
|
|
* The following example illustrates a typical application loop
|
|
* for a non-message-based application that uses two events.
|
|
*
|
|
* |
|
|
*
|
|
* HANDLE ah[2] = { hEvent1, hEvent2 };
|
|
* DWORD dwWait = 0;
|
|
*
|
|
* while (TRUE) {
|
|
*
|
|
* dwResult = MsgWaitForMultipleObjects(2, ah, FALSE,
|
|
* dwWait, QS_ALLINPUT);
|
|
* dwWait = 0;
|
|
*
|
|
* switch (dwResult) {
|
|
* case WAIT_OBJECT_0:
|
|
* // Event 1 has been set. If the event was created as
|
|
* // automatic-reset, then it has also been reset.
|
|
* ProcessInputEvent1();
|
|
* break;
|
|
*
|
|
* case WAIT_OBJECT_0 + 1:
|
|
* // Event 2 has been set. If the event was created as
|
|
* // automatic-reset, then it has also been reset.
|
|
* ProcessInputEvent2();
|
|
* break;
|
|
*
|
|
* case WAIT_OBJECT_0 + 2:
|
|
* // A Windows message has arrived. Process messages
|
|
* // until there aren't any more.
|
|
* while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|
* if (msg.message == WM_QUIT) {
|
|
* goto exitapp;
|
|
* }
|
|
* TranslateMessage(&msg);
|
|
* DispatchMessage(&msg);
|
|
* }
|
|
* break;
|
|
*
|
|
* default:
|
|
* // No input or messages waiting.
|
|
* // Do a frame of the game.
|
|
* // If the game is idle, then tell the next wait
|
|
* // to wait indefinitely for input or a message.
|
|
* if (!DoGame()) {
|
|
* dwWait = INFINITE;
|
|
* }
|
|
* // Poll for data in case the device is not
|
|
* // interrupt-driven.
|
|
* IDirectInputDevice2_Poll(pdev);
|
|
* break;
|
|
* }
|
|
* }
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetEventNotification(PV pdd, HANDLE h _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::SetEventNotification, (_ "px", pdd, h));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* acquiring or setting a new event handle while we're changing it.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if(!this->fAcquired)
|
|
{
|
|
/*
|
|
* Don't operate on the original handle because the app
|
|
* might decide to do something to it on another thread.
|
|
*/
|
|
|
|
hres = DupEventHandle(h, &h);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
/*
|
|
* Resetting the event serves two purposes.
|
|
*
|
|
* 1. It performs parameter validation for us, and
|
|
* 2. The event must be reset while the device is
|
|
* not acquired.
|
|
*/
|
|
if(fLimpFF(h, ResetEvent(h)))
|
|
{
|
|
|
|
if(!this->hNotify || !h)
|
|
{
|
|
hres = this->pdcb->lpVtbl->
|
|
SetEventNotification(this->pdcb, h);
|
|
|
|
/*
|
|
* All dcb's use default handling for now so
|
|
* assert the callback returns S_FALSE
|
|
* so we are reminded to change this if need be.
|
|
* An uninitialized device would fail but don't
|
|
* break if we hit one of those, just assert that
|
|
* we won't accidentally call a HEL on it.
|
|
*/
|
|
AssertF( ( hres == S_FALSE )
|
|
|| ( ( hres == DIERR_NOTINITIALIZED ) && !this->pvi ) );
|
|
|
|
if(this->pvi)
|
|
{
|
|
VXDDWORDDATA vhd;
|
|
vhd.pvi = this->pvi;
|
|
vhd.dw = (DWORD)(UINT_PTR)h;
|
|
hres = Hel_SetNotifyHandle(&vhd);
|
|
AssertF(SUCCEEDED(hres)); /* Should never fail */
|
|
h = (HANDLE)(UINT_PTR)pvExchangePpvPv64(&this->hNotify, (UINT_PTR)h);
|
|
hres = this->hresPolled;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* ISSUE-2001/03/29-timgill Is this actually an error case?
|
|
* Can this ever validly occur?
|
|
* if yes, don't we need any of the above?
|
|
*/
|
|
RPF( "Device internal data missing on SetEventNotification" );
|
|
}
|
|
|
|
} else
|
|
{
|
|
/*
|
|
* Note, this is a change in behavior as 3.0 didn't
|
|
* mind if you replaced an existing handle .
|
|
* Nobody seems to have noticed yet though.
|
|
*/
|
|
hres = DIERR_HANDLEEXISTS;
|
|
}
|
|
} else
|
|
{
|
|
RPF( "Handle not for Event in SetEventNotification" );
|
|
hres = E_HANDLE;
|
|
}
|
|
|
|
/*
|
|
* Close the old handle if we swapped one out
|
|
* or our duplicate if something went wrong
|
|
*/
|
|
if(h != 0)
|
|
{
|
|
CloseHandle(h);
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
hres = DIERR_ACQUIRED;
|
|
}
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(SetEventNotification, (PV pdd, HANDLE h), (pdd, h THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_SetEventNotificationA CDIDev_SetEventNotification
|
|
#define CDIDev_SetEventNotificationW CDIDev_SetEventNotification
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | hresMapHow |
|
|
*
|
|
* Map the <p dwObj> and <p dwHow> fields into an object index.
|
|
*
|
|
* @parm DWORD | dwObj |
|
|
*
|
|
* Object identifier.
|
|
*
|
|
* @parm DWORD | dwHow |
|
|
*
|
|
* How <p dwObj> should be interpreted.
|
|
*
|
|
* @parm OUT LPDIPROPINFO | ppropi |
|
|
*
|
|
* Receives object index and <p dwDevType>.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#ifndef XDEBUG
|
|
|
|
#define CDIDev_hresMapHow_(this, dwObj, dwHow, ppropi, z) \
|
|
_CDIDev_hresMapHow_(this, dwObj, dwHow, ppropi) \
|
|
|
|
#endif
|
|
|
|
STDMETHODIMP
|
|
CDIDev_hresMapHow_(PDD this, DWORD dwObj, DWORD dwHow,
|
|
LPDIPROPINFO ppropi, LPCSTR s_szProc)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(this->pdcb != c_pdcbNil)
|
|
{
|
|
int iobj = 0;
|
|
|
|
switch(dwHow)
|
|
{
|
|
|
|
case DIPH_DEVICE:
|
|
if(dwObj == 0)
|
|
{
|
|
ppropi->iobj = 0xFFFFFFFF;
|
|
ppropi->dwDevType = 0;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("%s: dwObj must be zero if DIPH_DEVICE", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case DIPH_BYOFFSET:
|
|
if(this->pdix && this->rgiobj)
|
|
{
|
|
if(dwObj < this->dwDataSize)
|
|
{
|
|
iobj = this->rgiobj[dwObj];
|
|
if(iobj >= 0)
|
|
{
|
|
AssertF(this->pdix[iobj].dwOfs == dwObj);
|
|
ppropi->iobj = iobj;
|
|
ppropi->dwDevType = this->df.rgodf[iobj].dwType;
|
|
hres = S_OK;
|
|
goto done;
|
|
} else
|
|
{
|
|
AssertF(iobj == -1);
|
|
}
|
|
}
|
|
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("%S: Invalid offset in dwObj. You may use DIPH_BYID to enum it."), s_szProc);
|
|
|
|
//RPF("%s: Invalid offset in dwObj", s_szProc);
|
|
|
|
} else
|
|
{
|
|
RPF("%s: Must have a data format to use if DIPH_BYOFFSET", s_szProc);
|
|
}
|
|
hres = DIERR_OBJECTNOTFOUND;
|
|
goto done;
|
|
|
|
case DIPH_BYID:
|
|
for(iobj = this->df.dwNumObjs; --iobj >= 0; )
|
|
{
|
|
if(DIDFT_FINDMATCH(this->df.rgodf[iobj].dwType, dwObj))
|
|
{
|
|
ppropi->iobj = iobj;
|
|
ppropi->dwDevType = this->df.rgodf[iobj].dwType;
|
|
hres = S_OK;
|
|
goto done;
|
|
}
|
|
}
|
|
RPF("%s: Invalid ID in dwObj", s_szProc);
|
|
hres = DIERR_OBJECTNOTFOUND;
|
|
break;
|
|
|
|
case DIPH_BYUSAGE:
|
|
hres = IDirectInputDeviceCallback_MapUsage(this->pdcb,
|
|
dwObj, &ppropi->iobj);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
ppropi->dwDevType = this->df.rgodf[ppropi->iobj].dwType;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
RPF("%s: Invalid dwHow", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
done:;
|
|
} else
|
|
{
|
|
RPF("ERROR: IDirectInputDevice: Not initialized");
|
|
hres = DIERR_NOTINITIALIZED;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
#define CDIDev_hresMapHow(this, dwObj, dwHow, pdwOut) \
|
|
CDIDev_hresMapHow_(this, dwObj, dwHow, pdwOut, s_szProc) \
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetObjectInfo |
|
|
*
|
|
* Obtains information about an object.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm OUT LPDIDEVICEOBJECTINSTANCE | pdidoi |
|
|
*
|
|
* Receives information about the object.
|
|
* The caller "must" initialize the <e DIDEVICEOBJECTINSTANCE.dwSize>
|
|
* field before calling this method.
|
|
*
|
|
* @parm DWORD | dwObj |
|
|
*
|
|
* Identifies the object for which the property is to be
|
|
* accessed. The meaning of this value depends on the
|
|
* value of the <p dwHow> parameter.
|
|
* See the documentation of the <t DIPROPHEADER>
|
|
* structure for additional information.
|
|
*
|
|
* @parm DWORD | dwHow |
|
|
*
|
|
* Identifies how <p dwObj> is to be interpreted.
|
|
* It must be one of the <c DIPH_*> values.
|
|
* See the documentation of the <t DIPROPHEADER>
|
|
* structure for additional information.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
* <c DIERR_OBJECTNOTFOUND>: The specified object does not
|
|
* exist.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetObjectInfoW(PV pddW, LPDIDEVICEOBJECTINSTANCEW pdoiW,
|
|
DWORD dwObj, DWORD dwHow)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::GetObjectInfo,
|
|
(_ "ppxx", pddW, pdoiW, dwObj, dwHow));
|
|
|
|
if(SUCCEEDED(hres = hresPvW(pddW)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb2(pdoiW,
|
|
DIDEVICEOBJECTINSTANCE_DX5W,
|
|
DIDEVICEOBJECTINSTANCE_DX3W, 1)))
|
|
{
|
|
PDD this = _thisPvNm(pddW, ddW);
|
|
DIPROPINFO propi;
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent
|
|
* another thread from Reset()ing behind our back.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
propi.pguid = 0;
|
|
hres = CDIDev_hresMapHow(this, dwObj, dwHow, &propi);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
if(dwHow != DIPH_DEVICE)
|
|
{
|
|
hres = CDIDev_GetObjectInfoHelper(this, &propi, pdoiW);
|
|
} else
|
|
{
|
|
RPF("%s: Invalid dwHow", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
if(FAILED(hres))
|
|
{
|
|
ScrambleBuf(&pdoiW->guidType,
|
|
pdoiW->dwSize -
|
|
FIELD_OFFSET(DIDEVICEOBJECTINSTANCEW, guidType));
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitBenignOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#define CDIDev_GetObjectInfo2W CDIDev_GetObjectInfoW
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDeviceA | GetObjectInfo |
|
|
*
|
|
* ANSI version of same.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm OUT LPDIDEVICEOBJECTINSTANCEA | pdoiA |
|
|
*
|
|
* Receives information about the device's identity.
|
|
*
|
|
* @parm DWORD | dwObj |
|
|
*
|
|
* Identifies the object for which the property is to be
|
|
* accessed.
|
|
*
|
|
* @parm DWORD | dwHow |
|
|
*
|
|
* Identifies how <p dwObj> is to be interpreted.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetObjectInfoA(PV pddA, LPDIDEVICEOBJECTINSTANCEA pdoiA,
|
|
DWORD dwObj, DWORD dwHow)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::GetObjectInfo,
|
|
(_ "ppxx", pddA, pdoiA, dwObj, dwHow));
|
|
|
|
if(SUCCEEDED(hres = hresPvA(pddA)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb2(pdoiA,
|
|
DIDEVICEOBJECTINSTANCE_DX5A,
|
|
DIDEVICEOBJECTINSTANCE_DX3A, 1)))
|
|
{
|
|
PDD this = _thisPvNm(pddA, ddA);
|
|
DIDEVICEOBJECTINSTANCEW doiW;
|
|
|
|
doiW.dwSize = cbX(DIDEVICEOBJECTINSTANCEW);
|
|
|
|
hres = CDIDev_GetObjectInfoW(&this->ddW, &doiW, dwObj, dwHow);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
ObjectInfoWToA(pdoiA, &doiW);
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
ExitBenignOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#define CDIDev_GetObjectInfo2A CDIDev_GetObjectInfoA
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetDeviceInfo |
|
|
*
|
|
* Obtains information about the device's identity.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm OUT LPDIDEVICEINSTANCE | pdidi |
|
|
*
|
|
* Receives information about the device's identity.
|
|
* The caller "must" initialize the <e DIDEVICEINSTANCE.dwSize>
|
|
* field before calling this method.
|
|
*
|
|
* If <e DIDEVICEINSTANCE.dwSize> is equal to the size of
|
|
* the <t DIDEVICEINSTANCE_DX3> structure, then a
|
|
* DirectX 3.0-compatible structure is returned instead of
|
|
* a DirectX 5.0 structure.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceInfoW(PV pddW, LPDIDEVICEINSTANCEW pdidiW)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::GetDeviceInfo, (_ "pp", pddW, pdidiW));
|
|
|
|
if(SUCCEEDED(hres = hresPvW(pddW)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb2(pdidiW,
|
|
DIDEVICEINSTANCE_DX5W,
|
|
DIDEVICEINSTANCE_DX3W, 1)))
|
|
{
|
|
PDD this = _thisPvNm(pddW, ddW);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent
|
|
* another thread from Reset()ing behind our back.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
pdidiW->guidInstance = this->guid;
|
|
pdidiW->guidProduct = this->guid;
|
|
|
|
/*
|
|
* Don't overwrite the dwSize, guidInstance, or guidProduct.
|
|
* Start at the dwDevType.
|
|
*/
|
|
|
|
ZeroBuf(&pdidiW->dwDevType,
|
|
pdidiW->dwSize - FIELD_OFFSET(DIDEVICEINSTANCEW, dwDevType));
|
|
|
|
hres = this->pdcb->lpVtbl->GetDeviceInfo(this->pdcb, pdidiW);
|
|
|
|
if(FAILED(hres))
|
|
{
|
|
ScrambleBuf(&pdidiW->guidInstance,
|
|
cbX(DIDEVICEINSTANCEW) -
|
|
FIELD_OFFSET(DIDEVICEINSTANCEW, guidInstance));
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDeviceA | GetDeviceInfo |
|
|
*
|
|
* ANSI version of same.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm OUT LPDIDEVICEINSTANCEA | pdidiA |
|
|
*
|
|
* Receives information about the device's identity.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
|
|
* parameters was invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetDeviceInfoA(PV pddA, LPDIDEVICEINSTANCEA pdidiA)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::GetDeviceInfo, (_ "pp", pddA, pdidiA));
|
|
|
|
if(SUCCEEDED(hres = hresPvA(pddA)) &&
|
|
SUCCEEDED(hres = hresFullValidWritePxCb2(pdidiA,
|
|
DIDEVICEINSTANCE_DX5A,
|
|
DIDEVICEINSTANCE_DX3A, 1)))
|
|
{
|
|
PDD this = _thisPvNm(pddA, ddA);
|
|
DIDEVICEINSTANCEW diW;
|
|
|
|
diW.dwSize = cbX(DIDEVICEINSTANCEW);
|
|
|
|
hres = CDIDev_GetDeviceInfoW(&this->ddW, &diW);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
DeviceInfoWToA(pdidiA, &diW);
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | CDIDev | UnhookCwp |
|
|
*
|
|
* Remove the CallWndProc handler.
|
|
*
|
|
* See <mf CDIDev::InstallCwp> for details.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HWND g_hwndExclusive;
|
|
HHOOK g_hhkCwp;
|
|
|
|
void INTERNAL
|
|
CDIDev_UnhookCwp(void)
|
|
{
|
|
DllEnterCrit();
|
|
|
|
if(g_hhkCwp)
|
|
{
|
|
UnhookWindowsHookEx(g_hhkCwp);
|
|
g_hhkCwp = 0;
|
|
g_hwndExclusive = 0;
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | AddForegroundDevice |
|
|
*
|
|
* Add ourselves to the list of devices that need to be
|
|
* unacquired when the window loses foreground activation.
|
|
*
|
|
* @parm PDD | this |
|
|
*
|
|
* Device to be added.
|
|
*
|
|
* @devnote
|
|
*
|
|
* Note that we do not need to AddRef the device, because
|
|
* <f CDIDev_Finalize> will unacquire the device for us
|
|
* automatically, so we will never be freed while still
|
|
* acquired.
|
|
*
|
|
* (Note that if we did choose to AddRef, it must be done
|
|
* outside the DLL critical
|
|
* section, in order to preserve the semaphore hierarchy.)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_AddForegroundDevice(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
DllEnterCrit();
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
hres = GPA_Append(g_hgpaExcl, this);
|
|
Common_Hold(this);
|
|
#else
|
|
|
|
if(g_cpddForeground >= g_cpddForegroundMax)
|
|
{
|
|
AssertF(g_cpddForeground == g_cpddForegroundMax);
|
|
hres = ReallocCbPpv(cbCxX(g_cpddForeground + 10, PDD),
|
|
&g_rgpddForeground);
|
|
if(FAILED(hres))
|
|
{
|
|
goto failed;
|
|
}
|
|
g_cpddForegroundMax = g_cpddForeground + 10;
|
|
}
|
|
|
|
AssertF(g_cpddForeground < g_cpddForegroundMax);
|
|
|
|
g_rgpddForeground[g_cpddForeground++] = this;
|
|
Common_Hold(this);
|
|
|
|
hres = S_OK;
|
|
failed:;
|
|
|
|
#endif
|
|
DllLeaveCrit();
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | CDIDev | DelForegroundDevice |
|
|
*
|
|
* Remove ourselves from the list, if we're there.
|
|
* It is not an error if we aren't on the list, because
|
|
* in the case of forced unacquire, the list is blanked
|
|
* out in order to avoid race conditions where somebody
|
|
* tries to acquire a device immediately upon receiving
|
|
* foreground activation, before we get a chance to
|
|
* unacquire all the old guys completely.
|
|
*
|
|
* @parm PDD | this |
|
|
*
|
|
* Device to be removed.
|
|
*
|
|
* @devnote
|
|
*
|
|
* Note that the Unhold must be done outside the DLL critical
|
|
* section, in order to preserve the semaphore hierarchy.
|
|
*
|
|
* Theoretically, the unhold will never drop the reference count
|
|
* to zero (because the latest it could be called is during
|
|
* the AppFinalize, where there is stll the outstanding refcount
|
|
* from the external ref that hasn't been released yet).
|
|
*
|
|
* But it's better to play it safe and always release the
|
|
* object outside.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INTERNAL
|
|
CDIDev_DelForegroundDevice(PDD this)
|
|
{
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
HRESULT hres;
|
|
#else
|
|
UINT idd;
|
|
#endif
|
|
|
|
DllEnterCrit();
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
hres = GPA_DeletePtr(g_hgpaExcl, this);
|
|
if(hres == hresUs(0))
|
|
{ /* If the last one went away */
|
|
GPA_Term(g_hgpaExcl); /* Free the tracking memory */
|
|
CDIDev_UnhookCwp(); /* Then unhook ourselves */
|
|
}
|
|
#else
|
|
|
|
for(idd = 0; idd < g_cpddForeground; idd++)
|
|
{
|
|
if(g_rgpddForeground[idd] == this)
|
|
{
|
|
g_rgpddForeground[idd] = g_rgpddForeground[--g_cpddForeground];
|
|
if(g_cpddForeground == 0)
|
|
{
|
|
CDIDev_UnhookCwp();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
DllLeaveCrit();
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
Common_Unhold(this);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @func LRESULT | CDIDev_CallWndProc |
|
|
*
|
|
* Thread-specific CallWndProc handler.
|
|
*
|
|
* Note that we need only one of these, since only the foreground
|
|
* window will require a hook.
|
|
*
|
|
* @parm int | nCode |
|
|
*
|
|
* Notification code.
|
|
*
|
|
* @parm WPARAM | wp |
|
|
*
|
|
* "Specifies whether the message is sent by the current process."
|
|
* We don't care.
|
|
*
|
|
* @parm LPARAM | lp |
|
|
*
|
|
* Points to a <t CWPSTRUCT> which describes the message.
|
|
*
|
|
* @returns
|
|
*
|
|
* Always chains to the next hook.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
LRESULT CALLBACK
|
|
CDIDev_CallWndProc(int nCode, WPARAM wp, LPARAM lp)
|
|
{
|
|
LPCWPSTRUCT pcwp = (LPCWPSTRUCT)lp;
|
|
#ifdef WINNT
|
|
static BOOL fKillFocus = FALSE;
|
|
static BOOL fIconic = FALSE;
|
|
|
|
fIconic = FALSE;
|
|
|
|
/*
|
|
* This part of code is to fix Windows bug 430051.
|
|
* The logic is: if WM_KILLFOCUS is followed by WM_SIZE(minimized),
|
|
* then the app is minimized from full screen mode.
|
|
* This combination should only happen to full screen mode game using DDraw.
|
|
*/
|
|
if(pcwp->message == WM_KILLFOCUS)
|
|
{
|
|
fKillFocus = TRUE;
|
|
} else if(pcwp->message == WM_SETFOCUS)
|
|
{
|
|
fKillFocus = FALSE;
|
|
} else if (pcwp->message == WM_SIZE)
|
|
{
|
|
if(pcwp->wParam == SIZE_MINIMIZED){
|
|
if( fKillFocus ) {
|
|
fIconic = TRUE;
|
|
fKillFocus = FALSE;
|
|
}else{
|
|
fKillFocus = FALSE;
|
|
}
|
|
} else {
|
|
fKillFocus = FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if( nCode == HC_ACTION && (pcwp->message == WM_ACTIVATE
|
|
#ifdef WINNT
|
|
|| fIconic
|
|
#endif
|
|
)
|
|
)
|
|
{
|
|
PDD *rgpdid;
|
|
UINT ipdid, cpdid;
|
|
HHOOK hhk;
|
|
|
|
#ifdef WINNT
|
|
fIconic = FALSE;
|
|
#endif
|
|
|
|
/*
|
|
* We cannot mess with items while inside the DLL critical section,
|
|
* because that would violate our semaphore hierarchy.
|
|
*
|
|
* Instead, we stash the active item list and replace it with
|
|
* an empty list. Then, outside the DLL critical section, we
|
|
* calmly operate on each item.
|
|
*/
|
|
DllEnterCrit();
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
rgpdid = (PV)g_hgpaExcl->rgpv;
|
|
cpdid = g_hgpaExcl->cpv;
|
|
GPA_Init(g_hgpaExcl);
|
|
#else
|
|
rgpdid = g_rgpddForeground;
|
|
cpdid = g_cpddForeground;
|
|
g_rgpddForeground = 0;
|
|
g_cpddForeground = 0;
|
|
g_cpddForegroundMax = 0;
|
|
#endif
|
|
|
|
/*
|
|
* Some sanity checking here.
|
|
*/
|
|
|
|
for(ipdid = 0; ipdid < cpdid; ipdid++)
|
|
{
|
|
AssertF(rgpdid[ipdid]);
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
|
|
/*
|
|
* Note that InternalUnacquire will set the notification
|
|
* event so the app knows that input was lost.
|
|
*/
|
|
for(ipdid = 0; ipdid < cpdid; ipdid++)
|
|
{
|
|
AssertF(rgpdid[ipdid]);
|
|
SquirtSqflPtszV(sqfl,
|
|
TEXT("Forcing unacquire of %08x due to focus loss"),
|
|
rgpdid[ipdid]);
|
|
CDIDev_InternalUnacquire(rgpdid[ipdid]);
|
|
Common_Unhold(rgpdid[ipdid]);
|
|
}
|
|
FreePpv(&rgpdid);
|
|
|
|
hhk = g_hhkCwp;
|
|
CDIDev_UnhookCwp();
|
|
return CallNextHookEx(hhk, nCode, wp, lp);
|
|
}
|
|
return CallNextHookEx(g_hhkCwp, nCode, wp, lp);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | CanAcquire |
|
|
*
|
|
* Determine whether the device may be acquired exclusively.
|
|
*
|
|
* If exclusive access is not requested, then the function
|
|
* succeeds vacuously.
|
|
*
|
|
* If exclusive access is requested, then the window must
|
|
* be the foreground window and must belong to the current
|
|
* process.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_CanAcquire(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
|
|
AssertF(CDIDev_IsConsistent(this));
|
|
if(this->discl & DISCL_FOREGROUND)
|
|
{
|
|
HWND hwndForeground = GetForegroundWindow();
|
|
|
|
AssertF(this->hwnd);
|
|
/*
|
|
* Note that we don't have to do an IsWindow() on this->hwnd,
|
|
* because GetForegroundWindow() will always return a valid
|
|
* window or NULL. Since we already tested this->hwnd != 0
|
|
* above, the only way the equality can occur is if the window
|
|
* handle is indeed valid.
|
|
*/
|
|
|
|
if(this->hwnd == hwndForeground && !IsIconic(this->hwnd))
|
|
{
|
|
|
|
/*
|
|
* Need to make sure that the window "still" belongs to
|
|
* this process, in case the window handle got recycled.
|
|
*/
|
|
DWORD idProcess;
|
|
GetWindowThreadProcessId(this->hwnd, &idProcess);
|
|
if(idProcess == GetCurrentProcessId())
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
/*
|
|
* Put a permanently invalid handle here so that we
|
|
* won't accidentally take a new window that happens
|
|
* to get a recycled handle value.
|
|
*/
|
|
this->hwnd = INVALID_HANDLE_VALUE;
|
|
RPF("Error: Window destroyed while associated with a device");
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else
|
|
{
|
|
hres = DIERR_OTHERAPPHASPRIO;
|
|
}
|
|
} else
|
|
{ /* No window; vacuous success */
|
|
hres = S_OK;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | InstallCwp |
|
|
*
|
|
* Install the CallWndProc handler.
|
|
*
|
|
* There is a bit of subtlety in the way this works.
|
|
* Since only foreground windows may acquire exclusive access,
|
|
* we need only one hook (for there is but one foreground
|
|
* window in the system).
|
|
*
|
|
* _NT_: Does NT handle this correctly in the face of
|
|
* multiple window stations?
|
|
*
|
|
* The tricky part is that input loss occurs asynchronously.
|
|
* So a device that registers a <f CallWindowProc> hook doesn't
|
|
* find out that the input has been lost until the app next
|
|
* calls <f Unacquire>.
|
|
*
|
|
* So the way this is done is via a collection of global
|
|
* variables (which must be accessed atomically).
|
|
*
|
|
* <p g_hhkCwp> is the hook handle itself. It is zero when
|
|
* no hook is installed.
|
|
*
|
|
* Note that we install the windows hook while inside both the
|
|
* object critical section and the DLL critical section.
|
|
* You might think we'd risk deadlocking with the hook procedure,
|
|
* in case the window asynchronously deactivates while we're
|
|
* installing the hook. But your worries are unfounded:
|
|
* If the window is on the current thread, then window messages
|
|
* won't be dispatched because we never call <f GetMessage> or
|
|
* go into a modal loop. And if the window is on another thread,
|
|
* then that other thread will simply have to wait until we're done.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_InstallCwp(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
|
|
if(this->discl & DISCL_FOREGROUND)
|
|
{
|
|
AssertF(this->hwnd);
|
|
hres = CDIDev_CanAcquire(this);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
hres = CDIDev_AddForegroundDevice(this);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
DllEnterCrit();
|
|
if(!g_hhkCwp)
|
|
{ /* We're the first one */
|
|
g_hwndExclusive = this->hwnd;
|
|
g_hhkCwp = SetWindowsHookEx(WH_CALLWNDPROC,
|
|
CDIDev_CallWndProc, g_hinst,
|
|
GetWindowThreadProcessId(this->hwnd, 0));
|
|
} else
|
|
{
|
|
AssertF(g_hwndExclusive == this->hwnd);
|
|
}
|
|
|
|
DllLeaveCrit();
|
|
/*
|
|
* There is a race condition up above, where the foreground
|
|
* window can change between the call to CanAcquire() and
|
|
* the call to SetWindowsHookEx(). Close the window by
|
|
* checking a second time after the hook is installed.
|
|
*
|
|
* If we leave the window open, it's possible that we will
|
|
* perform a physical acquire while the wrong window has
|
|
* foreground activation. Then, of course, we are never told
|
|
* that *our* window lost activation, and the physical device
|
|
* remains acquired forever.
|
|
*/
|
|
hres = CDIDev_CanAcquire(this);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
} else
|
|
{
|
|
SquirtSqflPtszV(sqflError,
|
|
TEXT("Window no longer foreground; ")
|
|
TEXT("punting acquire"));
|
|
CDIDev_InternalUnacquire(this);
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
hres = DIERR_OTHERAPPHASPRIO;
|
|
}
|
|
} else
|
|
{ /* No window; vacuous success */
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | RealUnacquire |
|
|
*
|
|
* Release access to the device, even if the device was only
|
|
* partially acquired.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* None.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_RealUnacquire(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
|
|
hres = this->pdcb->lpVtbl->Unacquire(this->pdcb);
|
|
if(hres == S_FALSE)
|
|
{
|
|
if(this->fAcquiredInstance)
|
|
{
|
|
this->fAcquiredInstance = 0;
|
|
hres = Hel_UnacquireInstance(this->pvi);
|
|
AssertF(SUCCEEDED(hres));
|
|
} else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | FFAcquire |
|
|
*
|
|
* The device has been successfully acquired. Do any
|
|
* necessary force feedback related acquisition goo.
|
|
*
|
|
* @cwrap PDD | pdd
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_FFAcquire(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
|
|
AssertF(CDIDev_InCrit(this));
|
|
|
|
if(this->pes && (this->discl & DISCL_EXCLUSIVE))
|
|
{
|
|
if(SUCCEEDED(hres = this->pes->lpVtbl->SendForceFeedbackCommand(
|
|
this->pes, &this->sh,
|
|
DISFFC_FORCERESET)))
|
|
{
|
|
|
|
CDIDev_RefreshGain(this);
|
|
|
|
/*
|
|
* If the center spring is to be disabled,
|
|
* then disable the center spring.
|
|
*/
|
|
if(!this->dwAutoCenter)
|
|
{
|
|
this->pes->lpVtbl->SendForceFeedbackCommand(
|
|
this->pes, &this->sh,
|
|
DISFFC_STOPALL);
|
|
}
|
|
|
|
hres = S_OK;
|
|
}
|
|
|
|
} else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | Acquire |
|
|
*
|
|
* Obtains access to the device.
|
|
*
|
|
* Device acquisition does not reference-count. If a device is
|
|
* acquired twice then unacquired once, the device is unacquired.
|
|
*
|
|
* Before the device can be acquired, a data format must
|
|
* first be set via the <mf IDirectInputDevice::SetDataFormat>
|
|
* method.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c S_FALSE>: The device has already been acquired. Note
|
|
* that this value is a success code.
|
|
*
|
|
* <c DIERR_OTHERAPPHASPRIO>: Access to the device was not granted.
|
|
* The most common cause of this is attempting to acquire a
|
|
* device with the <c DISCL_FOREGROUND> cooperative level when
|
|
* the associated window is not foreground.
|
|
*
|
|
* This error code is also returned if an attempt to
|
|
* acquire a device in exclusive mode fails because the device
|
|
* is already acquired in exclusive mode by somebody else.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The device
|
|
* does not have a selected data format.
|
|
*/
|
|
/*
|
|
* The point at which we take the exclusive semaphore is important.
|
|
* We should do it after preliminary validation of foreground
|
|
* permission, so that we don't accidentally lock out somebody
|
|
* else who legitimately has permission.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_Acquire(PV pdd _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::Acquire, (_ "p", pdd));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody from
|
|
* acquiring or changing the data format while we're acquiring.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
/*
|
|
* The app explicitly messed with acquisition. Any problems
|
|
* retrieving data are now the apps' fault.
|
|
*/
|
|
this->hresNotAcquired = DIERR_NOTACQUIRED;
|
|
|
|
//We now need a pvi even in where the device doesn't use VxDs
|
|
if(this->pdix && this->pvi)
|
|
{
|
|
if(this->pvi->fl & VIFL_ACQUIRED)
|
|
{
|
|
hres = S_FALSE;
|
|
} else if(SUCCEEDED(hres = CDIDev_CanAcquire(this)))
|
|
{
|
|
|
|
hres = Excl_Acquire(&this->guid, this->hwnd, this->discl);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
if(SUCCEEDED(hres = CDIDev_FFAcquire(this)))
|
|
{
|
|
#endif
|
|
hres = this->pdcb->lpVtbl->Acquire(this->pdcb);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
if(hres == S_FALSE)
|
|
{
|
|
hres = Hel_AcquireInstance(this->pvi);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
this->fAcquiredInstance = 1;
|
|
|
|
/*
|
|
* If relative mode, need to prime the
|
|
* pvLastBuffer with the current state.
|
|
*/
|
|
|
|
if(this->pvi->fl & VIFL_RELATIVE)
|
|
{
|
|
hres = this->pdcb->lpVtbl->GetDeviceState(
|
|
this->pdcb, this->pvLastBuffer);
|
|
if(FAILED(hres))
|
|
{
|
|
goto unacquire;
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
goto unacquire;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Note that InstallCwp must be the last thing
|
|
* we do, because it will add us to the foreground
|
|
* list, and none of our error exit paths remove us.
|
|
*/
|
|
hres = CDIDev_InstallCwp(this);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
this->fAcquired = 1;
|
|
this->fOnceAcquired = 1;
|
|
|
|
/*
|
|
* From now on, if we lose acquisition,
|
|
* it's not the app's fault.
|
|
*/
|
|
this->hresNotAcquired = DIERR_INPUTLOST;
|
|
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
goto unacquire;
|
|
}
|
|
|
|
} else
|
|
{
|
|
unacquire:;
|
|
CDIDev_RealUnacquire(this);
|
|
}
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
// For some apps (e.g. "Sonic R") we need to always succeed Acquire() -- see Windows bug 15085
|
|
if ((this->diHacks.fSucceedAcquire ) && (FAILED(hres)))
|
|
{
|
|
this->fOnceAcquired = 1;
|
|
hres = S_OK;
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(Acquire, (PV pdd), (pdd THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_AcquireA CDIDev_Acquire
|
|
#define CDIDev_AcquireW CDIDev_Acquire
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | InternalUnacquire |
|
|
*
|
|
* This does the real work of unacquiring. The internal
|
|
* version bypasses the "the app requested this" flag,
|
|
* so when the app goes to request something, it gets
|
|
* <c DIERR_INPUTLOST> instead of <c DIERR_NOTACQUIRED>.
|
|
*
|
|
* If the application error code is <c DIERR_INPUTLOST>, then
|
|
* we will also signal the associated event so that it knows
|
|
* that the state changed.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_InternalUnacquire(PDD this)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::InternalUnacquire, (_ "p", this));
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent confusing other
|
|
* methods which change their behavior depending on whether the device
|
|
* is acquired.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if(this->fAcquired)
|
|
{
|
|
AssertF(this->pdcb != c_pdcbNil);
|
|
this->fAcquired = 0;
|
|
Excl_Unacquire(&this->guid, this->hwnd, this->discl);
|
|
if(this->discl & DISCL_FOREGROUND)
|
|
{
|
|
AssertF(this->hwnd);
|
|
CDIDev_DelForegroundDevice(this);
|
|
#ifdef WINNT
|
|
if( IsIconic(this->hwnd) ) {
|
|
this->fUnacquiredWhenIconic = 1;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
/*
|
|
* ISSUE-2001/03/29-timgill multithreading means we cannot rely on Excl_Unaquire() return values
|
|
* We cannot trust the return value (if we made one)
|
|
* of Excl_Unacquire, because another instance may have
|
|
* snuck in and acquired the device after we Excl_Unacquire'd
|
|
* it and started doing force feedback on it.
|
|
*
|
|
* We need to fix this with the joystick mutex.
|
|
*/
|
|
if(this->pes && (this->discl & DISCL_EXCLUSIVE))
|
|
{
|
|
this->pes->lpVtbl->SendForceFeedbackCommand(
|
|
this->pes, &this->sh, DISFFC_RESET);
|
|
this->sh.dwTag = 0;
|
|
}
|
|
#endif
|
|
|
|
hres = CDIDev_RealUnacquire(this);
|
|
if(this->hresNotAcquired == DIERR_INPUTLOST)
|
|
{
|
|
CDIDev_SetNotifyEvent(this);
|
|
}
|
|
} else
|
|
{
|
|
hres = S_FALSE;
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | Unacquire |
|
|
*
|
|
* Release access to the device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c S_FALSE>: The object is not currently acquired.
|
|
* This may have been caused by a prior loss of input.
|
|
* Note that this is a success code.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_Unacquire(PV pdd _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::Unacquire, (_ "p", pdd));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
|
|
/*
|
|
* The app explicitly messed with acquisition. Any problems
|
|
* retrieving data are now the apps' fault.
|
|
*/
|
|
this->hresNotAcquired = DIERR_NOTACQUIRED;
|
|
|
|
hres = CDIDev_InternalUnacquire(this);
|
|
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(Unacquire, (PV pdd), (pdd THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_UnacquireA CDIDev_Unacquire
|
|
#define CDIDev_UnacquireW CDIDev_Unacquire
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method PDIPROPVALIDINFO | IDirectInputDevice | ppviFind |
|
|
*
|
|
* Locate the DIPROPVALIDINFO structure that describes
|
|
* the predefined property.
|
|
*
|
|
* @parm const GUID * | pguid |
|
|
*
|
|
* Property guid, or predefined property.
|
|
*
|
|
* @returns
|
|
*
|
|
* Pointer to a const <t DIPROPVALIDINFO> that describes
|
|
* what is and is not valid for this property.
|
|
*
|
|
* Returns 0 if the property is not one of the predefined
|
|
* properties.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
typedef struct DIPROPVALIDINFO
|
|
{
|
|
PCGUID pguid; /* Property name */
|
|
DWORD dwSize; /* expected size */
|
|
DWORD fl; /* flags */
|
|
} DIPROPVALIDINFO, *PDIPROPVALIDINFO;
|
|
|
|
/*
|
|
* Note that the flags are negative in sense.
|
|
* This makes validation easier.
|
|
*/
|
|
#define DIPVIFL_NOTDEVICE 0x00000001 /* Cannot be device */
|
|
#define DIPVIFL_NOTOBJECT 0x00000002 /* Cannot be object */
|
|
#define DIPVIFL_READONLY 0x00000004 /* Cannot be set */
|
|
#define DIPVIFL_NOTPRIVATE 0x00000008 /* Cannot handle private pvi */
|
|
#define DIPVIFL_NOTACQUIRED 0x00000010 /* Cannot modify while acquired */
|
|
|
|
DIPROPVALIDINFO c_rgpvi[] = {
|
|
|
|
{
|
|
DIPROP_BUFFERSIZE,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_NOTPRIVATE | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_AXISMODE,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_NOTPRIVATE | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_GRANULARITY,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTDEVICE | DIPVIFL_READONLY | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_RANGE,
|
|
cbX(DIPROPRANGE),
|
|
DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
/*
|
|
* Note that you can set the dead zone on the entire device.
|
|
* This is the same as applying it to each axis individually.
|
|
*/
|
|
{
|
|
DIPROP_DEADZONE,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
/*
|
|
* Note that you can set the saturation on the entire device.
|
|
* This is the same as applying it to each axis individually.
|
|
*/
|
|
{
|
|
DIPROP_SATURATION,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
/*
|
|
* Note that you can change the gain either while acquired
|
|
* or not. Your choice.
|
|
*/
|
|
{
|
|
DIPROP_FFGAIN,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT,
|
|
},
|
|
|
|
/*
|
|
* Note that the FF load is meaningful only when acquired,
|
|
* so we'd better not complain if they access it while acquired!
|
|
*/
|
|
{
|
|
DIPROP_FFLOAD,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_READONLY,
|
|
},
|
|
|
|
{
|
|
DIPROP_AUTOCENTER,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_CALIBRATIONMODE,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_CALIBRATION,
|
|
cbX(DIPROPCAL),
|
|
DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_GUIDANDPATH,
|
|
cbX(DIPROPGUIDANDPATH),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_READONLY,
|
|
},
|
|
|
|
{
|
|
DIPROP_INSTANCENAME,
|
|
cbX(DIPROPSTRING),
|
|
DIPVIFL_NOTOBJECT,
|
|
},
|
|
|
|
{
|
|
DIPROP_PRODUCTNAME,
|
|
cbX(DIPROPSTRING),
|
|
DIPVIFL_NOTOBJECT,
|
|
},
|
|
|
|
{
|
|
DIPROP_MAXBUFFERSIZE,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_NOTPRIVATE | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
|
|
{
|
|
DIPROP_JOYSTICKID,
|
|
cbX(DIPROPDWORD),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
|
|
{
|
|
DIPROP_GETPORTDISPLAYNAME,
|
|
cbX(DIPROPSTRING),
|
|
DIPVIFL_NOTOBJECT | DIPVIFL_READONLY,
|
|
},
|
|
|
|
/*
|
|
* Note that you can change the report ID while acquired
|
|
* or not. Your choice.
|
|
*/
|
|
{
|
|
DIPROP_ENABLEREPORTID,
|
|
cbX(DIPROPDWORD),
|
|
0x0,
|
|
},
|
|
|
|
#if 0
|
|
{
|
|
DIPROP_SPECIFICCALIBRATION,
|
|
cbX(DIPROPCAL),
|
|
DIPVIFL_NOTDEVICE | DIPVIFL_NOTACQUIRED,
|
|
},
|
|
#endif
|
|
|
|
};
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
STDMETHODIMP_(PDIPROPVALIDINFO)
|
|
CDIDev_ppviFind(PCGUID pguid)
|
|
{
|
|
PDIPROPVALIDINFO ppvi;
|
|
UINT ipvi;
|
|
|
|
for(ipvi = 0, ppvi = c_rgpvi; ipvi < cA(c_rgpvi); ipvi++, ppvi++)
|
|
{
|
|
if(ppvi->pguid == pguid)
|
|
{
|
|
goto found;
|
|
}
|
|
}
|
|
ppvi = 0;
|
|
|
|
found:
|
|
return ppvi;
|
|
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | hresValidProp |
|
|
*
|
|
* Check that the property structure makes sense.
|
|
* Returns the object index for further processing.
|
|
*
|
|
* @parm const GUID * | pguid |
|
|
*
|
|
* Property guid, or predefined property.
|
|
*
|
|
* @parm LPCDIPROPHEADER | pdiph |
|
|
*
|
|
* Propery header structure.
|
|
*
|
|
* @parm BOOL | fWrite |
|
|
*
|
|
* Whether property should be validate for writing.
|
|
*
|
|
* @parm OUT LPDIPROPINFO | ppropi |
|
|
*
|
|
* Receives object index.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
typedef BOOL (WINAPI *PFNBAD)(PCV pv, UINT cb);
|
|
|
|
STDMETHODIMP
|
|
CDIDev_hresValidProp(PDD this, const GUID *pguid, LPCDIPROPHEADER pdiph,
|
|
BOOL fWrite, LPDIPROPINFO ppropi)
|
|
{
|
|
HRESULT hres;
|
|
PFNBAD pfnBad;
|
|
EnterProcR(IDirectInputDevice::Get/SetProperty,
|
|
(_ "pxpx", this, pguid, pdiph, fWrite));
|
|
|
|
AssertF(CDIDev_InCrit(this));
|
|
|
|
if(fWrite)
|
|
{
|
|
pfnBad = (PFNBAD)IsBadWritePtr;
|
|
} else
|
|
{
|
|
pfnBad = (PFNBAD)IsBadReadPtr;
|
|
}
|
|
|
|
if(!pfnBad(pdiph, cbX(DIPROPHEADER)) &&
|
|
pdiph->dwHeaderSize == cbX(DIPROPHEADER) &&
|
|
pdiph->dwSize % 4 == 0 &&
|
|
pdiph->dwSize >= pdiph->dwHeaderSize &&
|
|
!pfnBad(pdiph, pdiph->dwSize))
|
|
{
|
|
|
|
if(fWrite)
|
|
{
|
|
ScrambleBuf((PV)(pdiph+1), pdiph->dwSize - cbX(DIPROPHEADER));
|
|
}
|
|
|
|
/*
|
|
* Now convert the item descriptor into an index.
|
|
*/
|
|
hres = CDIDev_hresMapHow(this, pdiph->dwObj, pdiph->dwHow, ppropi);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
|
|
/*
|
|
* Now validate the property id or guid.
|
|
*/
|
|
if(HIWORD((UINT_PTR)pguid) == 0)
|
|
{
|
|
|
|
PDIPROPVALIDINFO ppvi;
|
|
|
|
ppvi = CDIDev_ppviFind(pguid);
|
|
|
|
/*
|
|
* Note that if we don't find the GUID in our list,
|
|
* we fail it straight away. This prevents ISVs
|
|
* from trying to create properties in the Microsoft
|
|
* Reserved range.
|
|
*/
|
|
if(ppvi)
|
|
{
|
|
if( ppvi->pguid == DIPROP_CALIBRATION ) {
|
|
if( pdiph->dwSize == ppvi->dwSize ||
|
|
pdiph->dwSize == cbX(DIPROPCALPOV) )
|
|
{
|
|
hres = S_OK;
|
|
} else {
|
|
RPF("%s: Arg 2: Invalid dwSize for property", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else if( pdiph->dwSize == ppvi->dwSize )
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("%s: Arg 2: Invalid dwSize for property", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else
|
|
{
|
|
RPF("%s: Arg 1: Unknown property", s_szProc);
|
|
hres = E_NOTIMPL;
|
|
}
|
|
|
|
} else
|
|
{
|
|
hres = hresFullValidGuid(pguid, 1);
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
RPF("%s: Arg 2: Invalid pointer", s_szProc);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | hresValidDefProp |
|
|
*
|
|
* Determine whether the property is something we can handle
|
|
* in the default property handler.
|
|
*
|
|
* @parm IN LPCDIPROPINFO | ppropi |
|
|
*
|
|
* Information describing the property being retrieved.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags for forbidden things.
|
|
* <c DIPVIFL_READONLY> if being validated for writing.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: Passes validation.
|
|
*
|
|
* <c E_NOTIMPL>: Not something we handle.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INTERNAL
|
|
CDIDev_hresValidDefProp(PDD this, LPCDIPROPINFO ppropi, DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
PDIPROPVALIDINFO ppvi;
|
|
EnterProc(CDIDev_hresValidDefProp,
|
|
(_ "pGxx", this, ppropi->pguid, ppropi->dwDevType, dwFlags));
|
|
|
|
/*
|
|
* Note that it's okay if the device is acquired. We want to
|
|
* allow GetProperty to succeed on an acquired device.
|
|
*/
|
|
AssertF(CDIDev_InCrit(this));
|
|
|
|
ppvi = CDIDev_ppviFind(ppropi->pguid);
|
|
|
|
if(ppvi)
|
|
{
|
|
if(ppropi->iobj == 0xFFFFFFFF)
|
|
{
|
|
dwFlags |= DIPVIFL_NOTDEVICE; /* Fail if devices forbidden */
|
|
} else
|
|
{
|
|
dwFlags |= DIPVIFL_NOTOBJECT; /* Fail if objects forbidden */
|
|
}
|
|
if(this->pvi == 0)
|
|
{
|
|
dwFlags |= DIPVIFL_NOTPRIVATE; /* Fail if privates forbidden */
|
|
}
|
|
/*
|
|
* If attempting to modify property and we are acquired,
|
|
* then also set the "but not while acquired" filter.
|
|
*/
|
|
if((dwFlags & DIPVIFL_READONLY) && this->fAcquired)
|
|
{
|
|
dwFlags |= DIPVIFL_NOTACQUIRED; /* Fail if r/o while acq'd */
|
|
}
|
|
|
|
if((ppvi->fl & dwFlags) == 0)
|
|
{
|
|
hres = S_OK; /* Seems reasonable */
|
|
} else
|
|
{
|
|
if(ppvi->fl & dwFlags & DIPVIFL_READONLY)
|
|
{
|
|
RPF("SetProperty: Property is read-only");
|
|
hres = DIERR_READONLY;
|
|
} else if(ppvi->fl & dwFlags & DIPVIFL_NOTACQUIRED)
|
|
{
|
|
RPF("SetProperty: Cannot change property while acquired");
|
|
hres = DIERR_ACQUIRED;
|
|
} else
|
|
{
|
|
RPF("Get/SetProperty: Property does not exist for that object");
|
|
hres = E_NOTIMPL; /* Cannot do that */
|
|
}
|
|
}
|
|
|
|
} else
|
|
{
|
|
RPF("Get/SetProperty: Property does not exist");
|
|
hres = E_NOTIMPL; /* Definitely way out */
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | DefGetProperty |
|
|
*
|
|
* Default implementation of <mf IDirectInputDevice::GetProperty>
|
|
* to handle properties which the device decides not to implement.
|
|
*
|
|
* @parm IN LPCDIPROPINFO | ppropi |
|
|
*
|
|
* Information describing the property being retrieved.
|
|
*
|
|
* @parm OUT LPDIPROPHEADER | pdiph |
|
|
*
|
|
* Where to put the property value.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p pdiph> parameter is not a valid pointer.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_DefGetProperty(PDD this, LPCDIPROPINFO ppropi, LPDIPROPHEADER pdiph)
|
|
{
|
|
HRESULT hres;
|
|
EnterProc(CDIDev_DefGetProperty,
|
|
(_ "pGx", this, ppropi->pguid, ppropi->dwDevType));
|
|
|
|
AssertF(CDIDev_InCrit(this));
|
|
|
|
hres = CDIDev_hresValidDefProp(this, ppropi, 0);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
LPDIPROPDWORD pdipdw = (PV)pdiph;
|
|
LPDIPROPRANGE pdiprg = (PV)pdiph;
|
|
|
|
switch((DWORD)(UINT_PTR)ppropi->pguid)
|
|
{
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_BUFFERSIZE:
|
|
AssertF(this->pvi); /* Validation should've caught this */
|
|
pdipdw->dwData = this->celtBuf;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_AXISMODE:
|
|
AssertF(this->pvi); /* Validation should've caught this */
|
|
if(this->pvi->fl & VIFL_RELATIVE)
|
|
{
|
|
pdipdw->dwData = DIPROPAXISMODE_REL;
|
|
} else
|
|
{
|
|
pdipdw->dwData = DIPROPAXISMODE_ABS;
|
|
}
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_GRANULARITY:
|
|
|
|
if(DIDFT_GETTYPE(ppropi->dwDevType) & DIDFT_AXIS)
|
|
{
|
|
/* Default axis granularity is 1 */
|
|
pdipdw->dwData = 1;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
/*
|
|
* Buttons don't have granularity.
|
|
* POVs must be handled by device driver.
|
|
*/
|
|
RPF("GetProperty: Object doesn't have a granularity");
|
|
hres = E_NOTIMPL;
|
|
}
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_RANGE:
|
|
if(DIDFT_GETTYPE(ppropi->dwDevType) & DIDFT_RELAXIS)
|
|
{
|
|
/* Default rel-axis range is infinite */
|
|
pdiprg->lMin = DIPROPRANGE_NOMIN;
|
|
pdiprg->lMax = DIPROPRANGE_NOMAX;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
/*
|
|
* Device driver must handle abs axis range.
|
|
* Buttons and POVs don't have range.
|
|
*/
|
|
RPF("GetProperty: Object doesn't have a range");
|
|
hres = E_NOTIMPL;
|
|
}
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_MAXBUFFERSIZE:
|
|
pdipdw->dwData = this->celtBufMax;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
case (DWORD)(UINT_PTR)DIPROP_FFGAIN:
|
|
pdipdw->dwData = this->dwGain;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_FFLOAD:
|
|
hres = CDIDev_GetLoad(this, &pdipdw->dwData);
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_AUTOCENTER:
|
|
if(this->didcFF & DIDC_FORCEFEEDBACK)
|
|
{
|
|
pdipdw->dwData = this->dwAutoCenter;
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
hres = E_NOTIMPL;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
/*
|
|
* The user is asking for some property that simply
|
|
* makes no sense here. E.g., asking for the dead
|
|
* zone on a keyboard.
|
|
*/
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("GetProperty: Property 0x%08x not supported on device"),
|
|
(DWORD)(UINT_PTR)ppropi->pguid );
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | GetProperty |
|
|
*
|
|
* Obtain information about a device or object in a device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN REFGUID | rguidProp |
|
|
*
|
|
* The identity of the property to be obtained. This can be
|
|
* one of the predefined <c DIPROP_*> values, or it may
|
|
* be a private GUID.
|
|
*
|
|
* @parm IN LPDIPROPHEADER | pdiph |
|
|
*
|
|
* Points to the <t DIPROPHEADER> portion of a structure
|
|
* which dependson the property.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p pdiph> parameter is not a valid pointer, or the
|
|
* <p dwHow> field is invalid, or the <p dwObj> field
|
|
* is not zero when <p dwHow> is set to <c DIPH_DEVICE>.
|
|
*
|
|
* <c DIERR_OBJECTNOTFOUND>: The specified object does not
|
|
* exist.
|
|
*
|
|
* <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The property
|
|
* is not supported by the device or object.
|
|
*
|
|
* @ex
|
|
*
|
|
* The following "C" code fragment illustrates how to obtain
|
|
* the value of the <c DIPROP_BUFFERSIZE> property.
|
|
*
|
|
* |
|
|
*
|
|
* DIPROPDWORD dipdw;
|
|
* HRESULT hres;
|
|
* dipdw.diph.dwSize = sizeof(DIPROPDWORD);
|
|
* dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
* dipdw.diph.dwObj = 0; // device property
|
|
* hres = IDirectInputDevice_GetProperty(pdid, DIPROP_BUFFERSIZE, &dipdw.diph);
|
|
* if (SUCCEEDED(hres)) {
|
|
* // dipdw.dwData contains the value of the property
|
|
* }
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_GetProperty(PV pdd, REFGUID rguid, LPDIPROPHEADER pdiph _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::GetProperty, (_ "pxp", pdd, rguid, pdiph));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
DIPROPINFO propi;
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody
|
|
* acquiring or changing the property we are reading. We need
|
|
* to do this before validating, to prevent an acquisition.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
propi.pguid = rguid;
|
|
if(SUCCEEDED(hres = CDIDev_hresValidProp(this, rguid, pdiph,
|
|
1, &propi)))
|
|
{
|
|
|
|
hres = this->pdcb->lpVtbl->GetProperty(this->pdcb, &propi, pdiph);
|
|
|
|
if(hres == E_NOTIMPL)
|
|
{
|
|
hres = CDIDev_DefGetProperty(this, &propi, pdiph);
|
|
}
|
|
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitBenignOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(GetProperty, (PV pdm, REFGUID rguid, LPDIPROPHEADER pdiph),
|
|
(pdm, rguid, pdiph THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_GetPropertyA CDIDev_GetProperty
|
|
#define CDIDev_GetPropertyW CDIDev_GetProperty
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | SetAxisMode |
|
|
*
|
|
* Default handler for clients trying to set the axis mode.
|
|
* If the device doesn't handle axis modes natively, then
|
|
* we'll fake it ourselves.
|
|
*
|
|
* @parm DWORD | dwMode |
|
|
*
|
|
* Desired new mode.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetAxisMode(PDD this, DWORD dwMode)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::SetProperty(AXISMODE),
|
|
(_ "px", this, dwMode));
|
|
|
|
AssertF(this->pvi); /* Validation should've caught this */
|
|
|
|
hres = hresFullValidFl(dwMode, DIPROPAXISMODE_VALID, 2);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
if(dwMode & DIPROPAXISMODE_REL)
|
|
{
|
|
this->GetDeviceState = CDIDev_GetRelDeviceState;
|
|
this->pvi->fl |= VIFL_RELATIVE;
|
|
} else
|
|
{
|
|
this->GetDeviceState = CDIDev_GetAbsDeviceState;
|
|
this->pvi->fl &= ~VIFL_RELATIVE;
|
|
}
|
|
if( this->diHacks.fNativeAxisOnly )
|
|
{
|
|
this->pvi->fl |= VIFL_MODECOMPAT;
|
|
}
|
|
if(this->cAxes)
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
hres = DI_PROPNOEFFECT;
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | SetAutoCenter |
|
|
*
|
|
* Default handler for clients trying to set the
|
|
* auto-center property.
|
|
*
|
|
* If the device doesn't have control over the
|
|
* auto-center spring, then we fail.
|
|
*
|
|
* @parm DWORD | dwMode |
|
|
*
|
|
* Desired new mode.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetAutoCenter(PDD this, DWORD dwMode)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::SetProperty(AUTOCENTER),
|
|
(_ "px", this, dwMode));
|
|
|
|
hres = hresFullValidFl(dwMode, DIPROPAUTOCENTER_VALID, 2);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
if(this->didcFF & DIDC_FORCEFEEDBACK)
|
|
{
|
|
/*
|
|
* We need to create the effect driver if disabling
|
|
* autocenter so that CDIDev_FFAcquire will set the feedback
|
|
* mode properly.
|
|
*/
|
|
if(fLimpFF(dwMode == DIPROPAUTOCENTER_OFF,
|
|
SUCCEEDED(hres = CDIDev_CreateEffectDriver(this))))
|
|
{
|
|
this->dwAutoCenter = dwMode;
|
|
hres = S_OK;
|
|
}
|
|
} else
|
|
{
|
|
hres = E_NOTIMPL;
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | SetGlobalAxisProp |
|
|
*
|
|
* Default implementation of <mf IDirectInputDevice::SetProperty>
|
|
* to handle properties which can be applied globally to all
|
|
* absolute axes.
|
|
*
|
|
* @parm IN LPDIPROPINFO | ppropi |
|
|
*
|
|
* Information describing the property being set.
|
|
* We edit it to avoid reallocating memory all the time.
|
|
*
|
|
* @parm IN LPCDIPROPHEADER | pdiph |
|
|
*
|
|
* The property itself.
|
|
*
|
|
* @returns
|
|
*
|
|
* We consider the property-set a success if all candidates
|
|
* succeeded. <c E_NOTIMPL> counts as success, on the assumption
|
|
* that the property is not meaningful on the candidate.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetGlobalAxisProp(PDD this, LPDIPROPINFO ppropi, LPCDIPROPHEADER pdiph)
|
|
{
|
|
HRESULT hres;
|
|
|
|
for(ppropi->iobj = 0; ppropi->iobj < this->df.dwNumObjs; ppropi->iobj++)
|
|
{
|
|
DWORD dwType = this->df.rgodf[ppropi->iobj].dwType;
|
|
if(dwType & DIDFT_ABSAXIS)
|
|
{
|
|
ppropi->dwDevType = this->df.rgodf[ppropi->iobj].dwType;
|
|
|
|
hres = this->pdcb->lpVtbl->SetProperty(this->pdcb, ppropi, pdiph);
|
|
if(FAILED(hres) && hres != E_NOTIMPL)
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
hres = S_OK;
|
|
|
|
done:;
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev | DefSetProperty |
|
|
*
|
|
* Default implementation of <mf IDirectInputDevice::SetProperty>
|
|
* to handle properties which the device decides not to implement.
|
|
*
|
|
* @parm IN LPDIPROPINFO | ppropi |
|
|
*
|
|
* Information describing the property being set.
|
|
* We edit it to avoid reallocating memory all the time.
|
|
*
|
|
* @parm OUT LPCDIPROPHEADER | pdiph |
|
|
*
|
|
* Where to put the property value.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DI_POLLEDDEVICE>: The device is polled, so the result
|
|
* might not be meaningful. (This return code is used when
|
|
* you attempt to set the buffer size property.)
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p pdiph> parameter is not a valid pointer.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_DefSetProperty(PDD this, LPDIPROPINFO ppropi, LPCDIPROPHEADER pdiph)
|
|
{
|
|
HRESULT hres;
|
|
EnterProc(CDIDev_DefSetProperty,
|
|
(_ "pGx", this, ppropi->pguid, ppropi->dwDevType));
|
|
|
|
AssertF(CDIDev_InCrit(this));
|
|
|
|
/*
|
|
* Note: The indentation here is historical; I left it this way
|
|
* to keep the diff size down.
|
|
*/
|
|
|
|
hres = CDIDev_hresValidDefProp(this, ppropi, DIPVIFL_READONLY);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
LPDIPROPDWORD pdipdw = (PV)pdiph;
|
|
LPDIPROPRANGE pdiprg = (PV)pdiph;
|
|
VXDDWORDDATA vdd;
|
|
|
|
switch((DWORD)(UINT_PTR)ppropi->pguid)
|
|
{
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_BUFFERSIZE:
|
|
AssertF(this->pvi); /* Validation should've caught this */
|
|
vdd.pvi = this->pvi;
|
|
if( pdipdw->dwData > this->celtBufMax )
|
|
{
|
|
RPF( "DIPROP_BUFFERSIZE: requested size %d is larger than maximum %d, using %d",
|
|
pdipdw->dwData, this->celtBufMax, this->celtBufMax );
|
|
vdd.dw = this->celtBufMax;
|
|
}
|
|
else
|
|
{
|
|
vdd.dw = pdipdw->dwData;
|
|
}
|
|
|
|
hres = Hel_SetBufferSize(&vdd);
|
|
#ifdef DEBUG_STICKY
|
|
{
|
|
TCHAR tszDbg[80];
|
|
wsprintf( tszDbg, TEXT("SetBufferSize(0x%08x) returned 0x%08x\r\n"), vdd.dw, hres );
|
|
OutputDebugString( tszDbg );
|
|
}
|
|
#endif /* DEBUG_STICKY */
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
this->celtBuf = pdipdw->dwData;
|
|
hres = this->hresPolled;
|
|
}
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_AXISMODE:
|
|
hres = CDIDev_SetAxisMode(this, pdipdw->dwData);
|
|
break;
|
|
|
|
/*
|
|
* We will handle these global properties
|
|
* if the callback doesn't want to.
|
|
*/
|
|
case (DWORD)(UINT_PTR)DIPROP_RANGE:
|
|
case (DWORD)(UINT_PTR)DIPROP_DEADZONE:
|
|
case (DWORD)(UINT_PTR)DIPROP_SATURATION:
|
|
case (DWORD)(UINT_PTR)DIPROP_CALIBRATIONMODE:
|
|
case (DWORD)(UINT_PTR)DIPROP_CALIBRATION:
|
|
if(ppropi->dwDevType == 0)
|
|
{ /* For device */
|
|
hres = CDIDev_SetGlobalAxisProp(this, ppropi, pdiph);
|
|
} else
|
|
{
|
|
goto _default;
|
|
}
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_MAXBUFFERSIZE:
|
|
this->celtBufMax = pdipdw->dwData;
|
|
hres = S_OK;
|
|
break;
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
case (DWORD)(UINT_PTR)DIPROP_FFGAIN:
|
|
if(ISVALIDGAIN(pdipdw->dwData))
|
|
{
|
|
this->dwGain = pdipdw->dwData;
|
|
CDIDev_RefreshGain(this);
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("ERROR: SetProperty(DIPROP_FFGAIN): Gain out of range");
|
|
hres = E_INVALIDARG;
|
|
}
|
|
break;
|
|
|
|
case (DWORD)(UINT_PTR)DIPROP_AUTOCENTER:
|
|
hres = CDIDev_SetAutoCenter(this, pdipdw->dwData);
|
|
break;
|
|
#endif
|
|
|
|
_default:;
|
|
default:
|
|
/*
|
|
* The validation filter already failed invalid properties.
|
|
* So what's left is that the property is valid but cannot
|
|
* be set, because it doesn't exist on the device (e.g.,
|
|
* dead zone) or because it is read-only.
|
|
*/
|
|
SquirtSqflPtszV(sqfl | sqflBenign,
|
|
TEXT("SetProperty: Property 0x%08x not supported on device"),
|
|
(DWORD)(UINT_PTR)ppropi->pguid );
|
|
hres = E_NOTIMPL;
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | RealSetProperty |
|
|
*
|
|
* The function that does the real work.
|
|
*
|
|
* <mf IDirectInputDevice::SetDataFormat> will internally
|
|
* set the axis mode property, so it needs this backdoor
|
|
* entry point.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN REFGUID | rguidProp |
|
|
*
|
|
* The identity of the property to be set.
|
|
*
|
|
* @parm IN LPDIPROPHEADER | pdiph |
|
|
*
|
|
* Points to the <t DIPROPHEADER> portion of a structure
|
|
* which depends on the property.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_RealSetProperty(PDD this, REFGUID rguid, LPCDIPROPHEADER pdiph)
|
|
{
|
|
HRESULT hres;
|
|
DIPROPINFO propi;
|
|
EnterProcR(IDirectInputDevice::SetProperty, (_ "pxp", this, rguid, pdiph));
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody
|
|
* acquiring or changing the property we are reading. We need
|
|
* to do this before validating, to prevent an acquisition.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
propi.pguid = rguid;
|
|
if(SUCCEEDED(hres = CDIDev_hresValidProp(this, rguid, pdiph,
|
|
0, &propi)))
|
|
{
|
|
|
|
hres = this->pdcb->lpVtbl->SetProperty(this->pdcb, &propi, pdiph);
|
|
|
|
if(hres == E_NOTIMPL)
|
|
{
|
|
hres = CDIDev_DefSetProperty(this, &propi, pdiph);
|
|
}
|
|
}
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
ExitOleProc();
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetProperty |
|
|
*
|
|
* Set information about a device or object in a device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN REFGUID | rguidProp |
|
|
*
|
|
* The identity of the property to be set. This can be
|
|
* one of the predefined <c DIPROP_*> values, or it may
|
|
* be a pointer to a private GUID.
|
|
*
|
|
* @parm IN LPDIPROPHEADER | pdiph |
|
|
*
|
|
* Points to the <t DIPROPHEADER> portion of a structure
|
|
* which depends on the property.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DI_PROPNOEFFECT> <c S_FALSE>: The operation completed
|
|
* successfully but
|
|
* had no effect. For example, changing the axis mode
|
|
* on a device with no axes will return this value.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p pdiph> parameter is not a valid pointer, or the
|
|
* <p dwHow> field is invalid, or the <p dwObj> field
|
|
* is not zero when <p dwHow> is set to <c DIPH_DEVICE>.
|
|
*
|
|
* <c DIERR_OBJECTNOTFOUND>: The specified object does not
|
|
* exist.
|
|
*
|
|
* <c DIERR_UNSUPPORTED> = <c E_NOTIMPL>: The property
|
|
* is not supported by the device or object.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetProperty(PV pdd, REFGUID rguid, LPCDIPROPHEADER pdiph _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::SetProperty, (_ "pxp", pdd, rguid, pdiph));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
hres = CDIDev_RealSetProperty(this, rguid, pdiph);
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(SetProperty, (PV pdm, REFGUID rguid, LPCDIPROPHEADER pdiph),
|
|
(pdm, rguid, pdiph THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_SetPropertyA CDIDev_SetProperty
|
|
#define CDIDev_SetPropertyW CDIDev_SetProperty
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | SetCooperativeLevel |
|
|
*
|
|
* Establish the cooperativity level for the instance of
|
|
* the device.
|
|
*
|
|
* The cooperativity level determines how the instance of
|
|
* the device interacts with other instances of the device
|
|
* and the rest of the system.
|
|
*
|
|
* Note that if the system mouse is acquired in exclusive
|
|
* mode, then the mouse cursor will be removed from the screen
|
|
* until the device is unacquired.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm HWND | hwnd |
|
|
*
|
|
* The window associated with the device.
|
|
* The window must be a top-level window.
|
|
*
|
|
* It is an error to destroy the window while it is still
|
|
* active in a DirectInput device.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* Flags which describe the cooperativity level associated
|
|
* with the device.
|
|
*
|
|
* It consists of <c DISCL_*> flags, documented separately.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p hwnd> parameter is not a valid pointer.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT INLINE
|
|
CDIDev_SetCooperativeLevel_IsValidFl(DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
RD(static char s_szProc[] = "IDirectInputDevice::SetCooperativeLevel");
|
|
|
|
if(!(dwFlags & ~DISCL_VALID))
|
|
{
|
|
if((dwFlags & DISCL_EXCLMASK) == DISCL_EXCLUSIVE ||
|
|
(dwFlags & DISCL_EXCLMASK) == DISCL_NONEXCLUSIVE)
|
|
{
|
|
if((dwFlags & DISCL_GROUNDMASK) == DISCL_FOREGROUND ||
|
|
(dwFlags & DISCL_GROUNDMASK) == DISCL_BACKGROUND)
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("ERROR %s: arg %d: Must set exactly one of "
|
|
"DISCL_FOREGROUND or DISCL_BACKGROUND", s_szProc, 2);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else
|
|
{
|
|
RPF("ERROR %s: arg %d: Must set exactly one of "
|
|
"DISCL_EXCLUSIVE or DISCL_NONEXCLUSIVE", s_szProc, 2);
|
|
hres = E_INVALIDARG;
|
|
}
|
|
} else
|
|
{
|
|
RPF("ERROR %s: arg %d: invalid flags", s_szProc, 2);
|
|
hres = E_INVALIDARG;
|
|
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT INLINE
|
|
CDIDev_SetCooperativeLevel_IsValidHwnd(HWND hwnd, DWORD dwFlags)
|
|
{
|
|
HRESULT hres;
|
|
RD(static char s_szProc[] = "IDirectInputDevice::SetCooperativeLevel");
|
|
|
|
#if DIRECTINPUT_VERSION == 0x0300
|
|
if(dwFlags & DISCL_FOREGROUND)
|
|
{
|
|
if(SUCCEEDED(hres = hresFullValidHwnd(hwnd, 1)))
|
|
{
|
|
/*
|
|
* The window must be a top-level window to be activated.
|
|
*/
|
|
if(!(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD))
|
|
{
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("ERROR %s: window may not be a child window", s_szProc);
|
|
hres = E_HANDLE;
|
|
}
|
|
}
|
|
} else if(dwFlags & DISCL_EXCLUSIVE)
|
|
{
|
|
RPF("IDirectInputDevice::SetCooperativeLevel: "
|
|
"DISCL_EXCLUSIVE requires DISCL_FOREGROUND");
|
|
hres = E_NOTIMPL;
|
|
} else
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
#else
|
|
|
|
/*
|
|
* If a window handle is passed, it must be valid.
|
|
*
|
|
* The window must be a top-level window to be activated.
|
|
*
|
|
* The window must belong to the calling process so we can
|
|
* hook it.
|
|
*/
|
|
if(hwnd)
|
|
{
|
|
hres = hresFullValidHwnd(hwnd, 1);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
if(!(GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD))
|
|
{
|
|
if(GetWindowPid(hwnd) == GetCurrentProcessId())
|
|
{
|
|
} else
|
|
{
|
|
RPF("ERROR %s: window must belong to current process",
|
|
s_szProc);
|
|
hres = E_HANDLE;
|
|
}
|
|
|
|
} else
|
|
{
|
|
RPF("ERROR %s: window may not be a child window", s_szProc);
|
|
hres = E_HANDLE;
|
|
goto done;
|
|
}
|
|
} else
|
|
{
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Foreground mode or exclusive mode both require a window handle.
|
|
*/
|
|
if(dwFlags & (DISCL_FOREGROUND | DISCL_EXCLUSIVE))
|
|
{
|
|
if(hwnd)
|
|
{
|
|
} else
|
|
{
|
|
RPF("ERROR %s: window handle required "
|
|
"if DISCL_EXCLUSIVE or DISCL_FOREGROUND", s_szProc);
|
|
hres = E_HANDLE;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
hres = S_OK;
|
|
done:;
|
|
#endif
|
|
return hres;
|
|
}
|
|
|
|
|
|
STDMETHODIMP
|
|
CDIDev_SetCooperativeLevel(PV pdd, HWND hwnd, DWORD dwFlags _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::SetCooperativeLevel,
|
|
(_ "pxx", pdd, hwnd, dwFlags));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody
|
|
* acquiring or Reset()ing behind our back.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if(SUCCEEDED(hres = IDirectInputDevice_NotAcquired(this)) &&
|
|
SUCCEEDED(hres = CDIDev_SetCooperativeLevel_IsValidFl(dwFlags)) &&
|
|
SUCCEEDED(hres = CDIDev_SetCooperativeLevel_IsValidHwnd(hwnd, dwFlags)))
|
|
{
|
|
|
|
AssertF(CDIDev_IsConsistent(this));
|
|
|
|
/*
|
|
* For DX7 post Gold fix, check version against co-op level.
|
|
* Note, GetCaps is non-trivial on a HID but this is no time
|
|
* for a rewrite.
|
|
*/
|
|
if( this->dwVersion < 0x0700 )
|
|
{
|
|
if( dwFlags & DISCL_NOWINKEY )
|
|
{
|
|
hres = E_INVALIDARG;
|
|
}
|
|
else if( dwFlags & DISCL_EXCLUSIVE )
|
|
{
|
|
DIDEVCAPS_DX3 dc;
|
|
dc.dwSize = cbX(dc);
|
|
if( SUCCEEDED( this->pdcb->lpVtbl->GetCapabilities( this->pdcb, (PV)&dc) ) )
|
|
{
|
|
/*
|
|
* Don't allow keyboard exclusive mode pre-DX7
|
|
*/
|
|
if( GET_DIDEVICE_TYPE(dc.dwDevType) == DIDEVTYPE_KEYBOARD )
|
|
{
|
|
hres = E_INVALIDARG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* PS/2 Mouse and keyboard GetCaps are trivial so
|
|
* won't fail. HID is unlikely to fail for a mouse
|
|
* or keyboard as they generally stay plugged in so
|
|
* assume a failure to GetCaps means a joystick.
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED( hres ) )
|
|
{
|
|
hres = this->pdcb->lpVtbl->SetCooperativeLevel(
|
|
this->pdcb, hwnd, dwFlags);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
this->discl = dwFlags;
|
|
this->hwnd = hwnd;
|
|
if(this->pvi)
|
|
{
|
|
this->pvi->hwnd = hwnd;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AssertF( hres == E_INVALIDARG );
|
|
RPF("ERROR %s: arg %d: invalid flags", s_szProc, 2);
|
|
}
|
|
|
|
|
|
AssertF(CDIDev_IsConsistent(this));
|
|
|
|
}
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
}
|
|
|
|
ExitOleProcR();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(SetCooperativeLevel, (PV pdm, HWND hwnd, DWORD fl),
|
|
(pdm, hwnd, fl THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_SetCooperativeLevelA CDIDev_SetCooperativeLevel
|
|
#define CDIDev_SetCooperativeLevelW CDIDev_SetCooperativeLevel
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | RunControlPanel |
|
|
*
|
|
* Run the DirectInput control panel for the device.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN HWND | hwndOwner |
|
|
*
|
|
* Identifies the window handle that will be used as the
|
|
* parent window for subsequent UI. NULL is a valid parameter,
|
|
* indicating that there is no parent window.
|
|
*
|
|
* @parm DWORD | dwFlags |
|
|
*
|
|
* No flags are currently defined. This parameter "must" be
|
|
* zero.
|
|
*
|
|
* @returns
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The device is attached.
|
|
*
|
|
* @devnote
|
|
*
|
|
* The <p dwFlags> is eventually going to allow
|
|
* <c DIRCP_MODAL> to request a modal control panel.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_RunControlPanel(PV pdd, HWND hwndOwner, DWORD fl _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::RunControlPanel,
|
|
(_ "pxx", pdd, hwndOwner, fl));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)) &&
|
|
SUCCEEDED(hres = hresFullValidHwnd0(hwndOwner, 1)) &&
|
|
SUCCEEDED(hres = hresFullValidFl(fl, DIRCP_VALID, 2)))
|
|
{
|
|
|
|
PDD this = _thisPv(pdd);
|
|
IDirectInputDeviceCallback *pdcb;
|
|
|
|
/*
|
|
* Must protect with the critical section to prevent somebody
|
|
* Reset()ing behind our back. However, we cannot hold the
|
|
* critical section during the control panel callback, because
|
|
* that will yield.
|
|
*
|
|
* So we copy/addref the pdcb inside the critical section,
|
|
* then run the control panel outside the critical section,
|
|
* then release the pdcb when we're finally done.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
pdcb = this->pdcb;
|
|
OLE_AddRef(pdcb);
|
|
|
|
CDIDev_LeaveCrit(this);
|
|
|
|
hres = pdcb->lpVtbl->RunControlPanel(pdcb, hwndOwner, fl);
|
|
|
|
OLE_Release(pdcb);
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(RunControlPanel, (PV pdd, HWND hwndOwner, DWORD fl),
|
|
(pdd, hwndOwner, fl THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_RunControlPanelA CDIDev_RunControlPanel
|
|
#define CDIDev_RunControlPanelW CDIDev_RunControlPanel
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | Initialize |
|
|
*
|
|
* Initialize a DirectInputDevice object.
|
|
*
|
|
* Note that if this method fails, the underlying object should
|
|
* be considered to be an an indeterminate state and needs to
|
|
* be reinitialized before it can be subsequently used.
|
|
*
|
|
* The <mf IDirectInput::CreateDevice> method automatically
|
|
* initializes the device after creating it. Applications
|
|
* normally do not need to call this function.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm IN HINSTANCE | hinst |
|
|
*
|
|
* Instance handle of the application or DLL that is creating
|
|
* the DirectInput object.
|
|
*
|
|
* See the section titled "Initialization and Versions"
|
|
* for more information.
|
|
*
|
|
* @parm DWORD | dwVersion |
|
|
*
|
|
* Version number of the dinput.h header file that was used.
|
|
*
|
|
* See the section titled "Initialization and Versions"
|
|
* for more information.
|
|
*
|
|
* @parm IN REFGUID | rguid |
|
|
*
|
|
* Identifies the instance of the device for which the interface
|
|
* should be associated.
|
|
* The <mf IDirectInput::EnumDevices> method
|
|
* can be used to determine which instance GUIDs are supported by
|
|
* the system.
|
|
*
|
|
* @returns
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c S_FALSE>: The device had already been initialized with
|
|
* the instance GUID passed in <p lpGUID>.
|
|
*
|
|
* <c DIERR_ACQUIRED>: The device cannot be initialized while
|
|
* it is acquired.
|
|
*
|
|
* <c DIERR_DEVICENOTREG>: The instance GUID does not exist
|
|
* on the current machine.
|
|
*
|
|
* <c DIERR_HASEFFECTS>:
|
|
* The device cannot be reinitialized because there are
|
|
* still effects attached to it.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_Initialize(PV pdd, HINSTANCE hinst, DWORD dwVersion, REFGUID rguid _THAT)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::Initialize,
|
|
(_ "pxxG", pdd, hinst, dwVersion, rguid));
|
|
|
|
if(SUCCEEDED(hres = hresPvT(pdd)) &&
|
|
SUCCEEDED(hres = hresFullValidGuid(rguid, 1)))
|
|
{
|
|
PDD this = _thisPv(pdd);
|
|
CREATEDCB CreateDcb;
|
|
IDirectInputDeviceCallback *pdcb;
|
|
|
|
/*
|
|
* Must take the critical section to avoid Reset()ing
|
|
* the device (or generally speaking, screwing with the
|
|
* internal state variables) while somebody else is
|
|
* messing with it.
|
|
*/
|
|
CDIDev_EnterCrit(this);
|
|
|
|
if(SUCCEEDED(hres = hresValidInstanceVer(hinst, dwVersion)) &&
|
|
SUCCEEDED(hres = hresFindInstanceGUID(rguid, &CreateDcb, 1)) &&
|
|
SUCCEEDED(hres = CDIDev_Reset(this)))
|
|
{
|
|
hres = CreateDcb(0, rguid, &IID_IDirectInputDeviceCallback,
|
|
(PPV)&pdcb);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
this->pdcb = pdcb;
|
|
AssertF(this->pvi == 0);
|
|
if(SUCCEEDED(hres = CDIDev_GetDataFormat(this)) &&
|
|
SUCCEEDED(hres = CDIDev_GetPolled(this)) &&
|
|
SUCCEEDED(hres = this->pdcb->lpVtbl->GetInstance(
|
|
this->pdcb, &this->pvi)))
|
|
{
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
this->dwVersion = dwVersion;
|
|
#if (DIRECTINPUT_VERSION > 0x061A)
|
|
AhGetAppHacks( &this->diHacks );
|
|
#endif
|
|
this->pdcb->lpVtbl->SetDIData(this->pdcb, dwVersion, &this->diHacks);
|
|
|
|
#ifdef BUGGY_DX7_WINNT
|
|
if( (this->dwVersion < 0x700) && (this->dwVersion != 0x5B2) ) {
|
|
if( GET_DIDEVICE_TYPE(this->diHacks.dwDevType) == DIDEVTYPE_JOYSTICK ) {
|
|
hres = CDIDev_ParseDataFormatInternal(this, &c_dfDIJoystick);
|
|
}
|
|
}
|
|
#endif //BUGGY_DX7_WINNT
|
|
#endif //IDirectInputDevice2Vtbl
|
|
|
|
if(dwVersion >= DIRECTINPUT_VERSION)
|
|
{
|
|
this->didftInstance = DIDFT_ANYINSTANCE;
|
|
}
|
|
|
|
this->guid = *rguid;
|
|
if(this->pvi && (this->pvi->fl & VIFL_EMULATED))
|
|
{
|
|
this->pvi->pdd = this;
|
|
}
|
|
|
|
hres = this->pdcb->lpVtbl->CookDeviceData(this->pdcb, 0, 0);
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
this->fCook = 1;
|
|
}
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
CDIDev_InitFF(this);
|
|
#endif
|
|
hres = S_OK;
|
|
} else
|
|
{
|
|
RPF("Device driver didn't provide a data format");
|
|
}
|
|
} else
|
|
{
|
|
#ifdef NOISY
|
|
RPF("Cannot create device");
|
|
#endif
|
|
}
|
|
}
|
|
CDIDev_LeaveCrit(this);
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
#ifdef XDEBUG
|
|
|
|
CSET_STUBS(Initialize,
|
|
(PV pdd, HINSTANCE hinst, DWORD dwVersion, REFGUID rguid),
|
|
(pdd, hinst, dwVersion, rguid THAT_))
|
|
|
|
#else
|
|
|
|
#define CDIDev_InitializeA CDIDev_Initialize
|
|
#define CDIDev_InitializeW CDIDev_Initialize
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method void | IDirectInputDevice | Init |
|
|
*
|
|
* Initialize the internal parts of the DirectInputDevice object.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
void INLINE
|
|
CDIDev_Init(PDD this)
|
|
{
|
|
/*
|
|
* 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 CDIDev_New.)
|
|
*/
|
|
this->fCritInited = fInitializeCriticalSection(&this->crst);
|
|
|
|
if( this->fCritInited )
|
|
{
|
|
this->celtBufMax = 1023; /* Default maximum buffer size */
|
|
|
|
this->pdcb = c_pdcbNil;
|
|
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
GPA_InitFromZero(&this->gpaEff);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | New |
|
|
*
|
|
* Create a new DirectInputDevice object, uninitialized.
|
|
*
|
|
* @parm IN PUNK | punkOuter |
|
|
*
|
|
* Controlling unknown for aggregation.
|
|
*
|
|
* @parm IN RIID | riid |
|
|
*
|
|
* Desired interface to new object.
|
|
*
|
|
* @parm OUT PPV | ppvObj |
|
|
*
|
|
* Output pointer for new object.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
STDMETHODIMP
|
|
CDIDev_New(PUNK punkOuter, RIID riid, PPV ppvObj)
|
|
{
|
|
HRESULT hres;
|
|
EnterProcR(IDirectInputDevice::<constructor>, (_ "Gp", riid, punkOuter));
|
|
|
|
if (SUCCEEDED(hres = hresFullValidPcbOut(ppvObj, cbX(*ppvObj), 3)))
|
|
{
|
|
hres = Excl_Init();
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
LPVOID pvTry = NULL;
|
|
hres = Common_NewRiid(CDIDev, punkOuter, riid, &pvTry);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
PDD this = _thisPv(pvTry);
|
|
CDIDev_Init(this);
|
|
if( this->fCritInited )
|
|
{
|
|
*ppvObj = pvTry;
|
|
}
|
|
else
|
|
{
|
|
Common_Unhold(this);
|
|
*ppvObj = NULL;
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProcPpvR(ppvObj);
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method HRESULT | CDIDev_ModifyEffectParams |
|
|
*
|
|
* Modifies parameters of DIEFFECT structure to fit the current FF device
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
*
|
|
* @parm IN OUT LPDIEFFECT | peff |
|
|
*
|
|
* Pointer to the effect structure
|
|
*
|
|
* @parm IN GUID | effGUID |
|
|
*
|
|
* GUID for the effect
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_UNSUPPORTED>: The effect can't be supported by the current device
|
|
* (e.g. the number of FF axes on the device is 0)
|
|
*
|
|
* <c DIERR_INVALIDPARAM>: Can't create the effect even with the modified parameters
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT CDIDev_ModifyEffectParams
|
|
(
|
|
PV pdd,
|
|
LPDIEFFECT peff,
|
|
GUID effGUID
|
|
)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
HRESULT hresCreate = S_OK;
|
|
LPDIRECTINPUTEFFECT pdeff;
|
|
PDD this = _thisPv(pdd);
|
|
|
|
EnterProcR(CDIDev_ModifyEffectParams, (_ "p", pdd));
|
|
|
|
// Check to make sure that effects we enumerate will
|
|
// actually get created on the device.
|
|
// try creating the effect.
|
|
#ifdef XDEBUG
|
|
hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL, ((LPUNKNOWN)this)->lpVtbl);
|
|
#else
|
|
hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL);
|
|
#endif
|
|
|
|
if(SUCCEEDED(hresCreate))
|
|
{
|
|
Invoke_Release(&pdeff);
|
|
}
|
|
else
|
|
{
|
|
if (hresCreate == DIERR_INVALIDPARAM)
|
|
{
|
|
//two things can give DIERR_INVALIDPARAM:
|
|
//invalid axes and invalid trigger button
|
|
//check the axes first, then the trigger buttons
|
|
//try to eliminate all DIERR_INVALIDPARAMS
|
|
LPDIOBJECTDATAFORMAT lpObjDat = this->df.rgodf;
|
|
DWORD dwNum = this->df.dwNumObjs;
|
|
DWORD nCount;
|
|
LPDWORD lpAxes;
|
|
LPDWORD lpThisAxis;
|
|
LPDWORD lpEffAxis;
|
|
DWORD nAxes = 0;
|
|
DWORD dwTrigger = DIJOFS_BUTTON(0);
|
|
BOOL bTriggerCorrect = FALSE;
|
|
AllocCbPpv(sizeof(DWORD)*dwNum, &lpAxes);
|
|
lpThisAxis = lpAxes;
|
|
for (nCount = 0; nCount < dwNum; nCount ++)
|
|
{
|
|
AssertF(lpObjDat != NULL);
|
|
|
|
//check the axes
|
|
if ((lpObjDat->dwType & (DIDFT_AXIS | DIDFT_FFACTUATOR) & DIDFT_TYPEMASK) &&
|
|
(fHasAllBitsFlFl(lpObjDat->dwType, (DIDFT_AXIS | DIDFT_FFACTUATOR) & DIDFT_ATTRMASK)))
|
|
{
|
|
*lpAxes = lpObjDat->dwOfs;
|
|
nAxes++;
|
|
lpAxes++;
|
|
}
|
|
else
|
|
{
|
|
//check the trigger button, if there's one
|
|
if ((peff->dwTriggerButton != DIEB_NOTRIGGER) &&
|
|
(lpObjDat->dwType & DIDFT_FFEFFECTTRIGGER & DIDFT_TYPEMASK) &&
|
|
(fHasAllBitsFlFl(lpObjDat->dwType, DIDFT_FFEFFECTTRIGGER & DIDFT_ATTRMASK)))
|
|
|
|
{
|
|
if (lpObjDat->dwOfs == peff->dwTriggerButton)
|
|
{
|
|
//the trigger is valid
|
|
bTriggerCorrect = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//remember the trigger offset for the future
|
|
dwTrigger = lpObjDat->dwOfs;
|
|
}
|
|
}
|
|
}
|
|
|
|
lpObjDat++;
|
|
}
|
|
|
|
//first, chack if there are any FF axes
|
|
if (nAxes == 0)
|
|
{
|
|
//return an error if no FF axes on device
|
|
hres = DIERR_UNSUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
//trigger buttons are checked for validity before axes,
|
|
//so set the trigger button, if needed,
|
|
//because if it is invalid, this is what caused the error
|
|
if ((peff->dwTriggerButton != DIEB_NOTRIGGER) && (bTriggerCorrect == FALSE))
|
|
{
|
|
peff->dwTriggerButton = dwTrigger;
|
|
|
|
// and try creating again
|
|
#ifdef XDEBUG
|
|
hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL, ((LPUNKNOWN)this)->lpVtbl);
|
|
#else
|
|
hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL);
|
|
#endif
|
|
if(SUCCEEDED(hresCreate))
|
|
{
|
|
Invoke_Release(&pdeff);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if (hresCreate == DIERR_INVALIDPARAM)
|
|
{
|
|
|
|
HRESULT hresInfo = S_OK;
|
|
EFFECTMAPINFO emi;
|
|
|
|
//this time, set the axes
|
|
if (peff->cAxes > nAxes)
|
|
{
|
|
//change the number of axes
|
|
peff->cAxes = nAxes;
|
|
|
|
//change the flags
|
|
if ((nAxes < 3) && (peff->dwFlags & DIEFF_SPHERICAL))
|
|
{
|
|
peff->dwFlags &= ~DIEFF_SPHERICAL;
|
|
peff->dwFlags |= DIEFF_POLAR;
|
|
}
|
|
else
|
|
{
|
|
if ((nAxes < 2) && (peff->dwFlags & DIEFF_POLAR))
|
|
{
|
|
peff->dwFlags &= ~DIEFF_POLAR;
|
|
peff->dwFlags |= DIEFF_CARTESIAN;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//check if size of type-specific param structures is not bigger then number of axes,
|
|
//since this can also give us invalid params in type-specific .
|
|
|
|
//need to do this only for conditions
|
|
if (SUCCEEDED(hresInfo = CDIDev_FindEffectGUID(this, &effGUID, &emi, 2)))
|
|
{
|
|
//do the conditions
|
|
if (emi.attr.dwEffType & DIEFT_CONDITION)
|
|
{
|
|
if (peff->cbTypeSpecificParams/(sizeof(DICONDITION)) > peff->cAxes)
|
|
{
|
|
peff->cbTypeSpecificParams = peff->cAxes*(sizeof(DICONDITION));
|
|
}
|
|
}
|
|
|
|
//don't need to do anything for custom forces,
|
|
//since DInput doesn't check number of channels against number of axes anyway
|
|
}
|
|
|
|
|
|
//write over the axes
|
|
lpEffAxis = peff->rgdwAxes;
|
|
for (nCount = 0; nCount < nAxes, nCount < peff->cAxes; nCount ++)
|
|
{
|
|
*(lpEffAxis) = *(lpThisAxis);
|
|
lpThisAxis ++;
|
|
lpEffAxis++;
|
|
}
|
|
|
|
|
|
// and try creating again
|
|
#ifdef XDEBUG
|
|
hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL, ((LPUNKNOWN)this)->lpVtbl);
|
|
#else
|
|
hresCreate = CDIDev_CreateEffect(this, &effGUID, peff, &pdeff, NULL);
|
|
#endif
|
|
if(SUCCEEDED(hresCreate))
|
|
{
|
|
Invoke_Release(&pdeff);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//free the axes array
|
|
FreePpv(&lpAxes);
|
|
}
|
|
}
|
|
|
|
if ((SUCCEEDED(hres)) && (hresCreate == DIERR_INVALIDPARAM))
|
|
{
|
|
hres = hresCreate;
|
|
}
|
|
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc INTERNAL
|
|
*
|
|
* @method BOOL | CDIDev_IsStandardEffect |
|
|
*
|
|
* Checks if the effect GUID belongs to a standard DI effect
|
|
*
|
|
* @parm IN GUID | effGUID |
|
|
*
|
|
* GUID for the effect
|
|
*
|
|
* @returns BOOL
|
|
*
|
|
* TRUE if it is a standard DI effect;
|
|
* FALSE otherwise.
|
|
*
|
|
*
|
|
*****************************************************************************/
|
|
|
|
BOOL CDIDev_IsStandardEffect
|
|
(GUID effGUID)
|
|
{
|
|
BOOL bStandard = TRUE;
|
|
|
|
|
|
//check all the standard DX7 GUIDs
|
|
if ((IsEqualGUID(&effGUID, &GUID_Sine)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_Triangle)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_ConstantForce)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_RampForce)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_Square)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_SawtoothUp)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_SawtoothDown)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_Spring)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_Damper)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_Inertia)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_Friction)) ||
|
|
(IsEqualGUID(&effGUID, &GUID_CustomForce)))
|
|
{
|
|
bStandard = TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
bStandard = FALSE;
|
|
}
|
|
|
|
return bStandard;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | EnumEffectsInFile |
|
|
*
|
|
* Enumerates DIEFFECT struct(s) and effect GUID from file.
|
|
* An application can use this in order to create pre-authored
|
|
* force effects.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPCSTR | lpszFileName |
|
|
*
|
|
* Name of the RIFF file that contains collection of effects.
|
|
*
|
|
* @parm IN OUT LPENUMEFFECTSCALLBACK | pec |
|
|
*
|
|
* The callback function.
|
|
*
|
|
* @parm IN OUT LPVOID | pvRef |
|
|
* Specifies the application-defined value given in the
|
|
* <mf IDirectInputDevice::EnumObjects> function.
|
|
*
|
|
* @parm IN DWORD | dwFlags |
|
|
*
|
|
* Flags which control the enumeration.
|
|
*
|
|
* It consists of <c DIFEF_*> flags, documented separately.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpDirectInputDevice> or
|
|
* <p lpdc> parameter is invalid.
|
|
*
|
|
* @cb BOOL CALLBACK | DIEnumEffectsCallback |
|
|
*
|
|
* An application-defined callback function that receives
|
|
* effect GUID, DIEFFECT and repeat count as a result of a call to the
|
|
* <om IDirectInputDevice::EnumEffectsInFile> method.
|
|
*
|
|
* @parm OUT LPCDIFILEEFFECT | lpDiFileEf |
|
|
*
|
|
* Pointer to a DIFILEEFFECT structure.
|
|
*
|
|
*
|
|
* @parm IN OUT LPVOID | pvRef |
|
|
* Specifies the application-defined value given in the
|
|
* <mf IDirectInputDevice::EnumObjects> function.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns <c DIENUM_CONTINUE> to continue the enumeration
|
|
* or <c DIENUM_STOP> to stop the enumeration.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
HRESULT CDIDev_EnumEffectsInFileA
|
|
(
|
|
PV pddA,
|
|
LPCSTR lpszFileName,
|
|
LPDIENUMEFFECTSINFILECALLBACK pec,
|
|
LPVOID pvRef,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
|
|
|
|
EnterProcR(IDirectInputDevice::EnumEffectsInFile, (_ "s", lpszFileName));
|
|
|
|
/* Validate incoming parameters */
|
|
if(SUCCEEDED(hres = hresPvA(pddA)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrA(lpszFileName, MAX_JOYSTRING,1)) &&
|
|
SUCCEEDED(hres = hresFullValidPfn(pec, 2)) &&
|
|
SUCCEEDED(hres = hresFullValidFl(dwFlags, DIFEF_ENUMVALID, 3)) )
|
|
{
|
|
PDD this = _thisPvNm(pddA, ddA);
|
|
HMMIO hmmio;
|
|
MMCKINFO mmck;
|
|
DWORD dwEffectSz;
|
|
|
|
hres = RIFF_Open(lpszFileName, MMIO_READ | MMIO_ALLOCBUF , &hmmio, &mmck, &dwEffectSz);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
HRESULT hresRead;
|
|
DIEFFECT effect;
|
|
DIFILEEFFECT DiFileEf;
|
|
DIENVELOPE diEnvelope;
|
|
DWORD rgdwAxes[DIEFFECT_MAXAXES];
|
|
LONG rglDirection[DIEFFECT_MAXAXES];
|
|
|
|
effect.rgdwAxes = rgdwAxes;
|
|
effect.rglDirection = rglDirection;
|
|
effect.lpEnvelope = &diEnvelope;
|
|
|
|
DiFileEf.dwSize = cbX(DiFileEf);
|
|
DiFileEf.lpDiEffect = &effect;
|
|
|
|
while ((SUCCEEDED(hres)) && (SUCCEEDED(hresRead = RIFF_ReadEffect(hmmio, &DiFileEf))))
|
|
{
|
|
BOOL fRc = DIENUM_CONTINUE;
|
|
BOOL bInclude = TRUE;
|
|
HRESULT hresModify = DI_OK;
|
|
|
|
//modify if needed
|
|
if (dwFlags & DIFEF_MODIFYIFNEEDED)
|
|
{
|
|
hresModify = CDIDev_ModifyEffectParams(this, &effect, DiFileEf.GuidEffect);
|
|
}
|
|
|
|
//if necessary, check whether effect is standard
|
|
if (!(dwFlags & DIFEF_INCLUDENONSTANDARD))
|
|
{
|
|
bInclude = CDIDev_IsStandardEffect(DiFileEf.GuidEffect);
|
|
}
|
|
|
|
//call back only if all the conditions posed by the flags are satisfied
|
|
if ((SUCCEEDED(hresModify)) && (bInclude == TRUE))
|
|
{
|
|
fRc = Callback(pec, &DiFileEf, pvRef);
|
|
}
|
|
|
|
//free type-specific only if allocated
|
|
if(effect.cbTypeSpecificParams > 0)
|
|
{
|
|
FreePv(effect.lpvTypeSpecificParams);
|
|
effect.cbTypeSpecificParams = 0x0;
|
|
}
|
|
|
|
if(fRc == DIENUM_STOP)
|
|
{
|
|
break;
|
|
} else if(fRc == DIENUM_CONTINUE)
|
|
{
|
|
continue;
|
|
} else
|
|
{
|
|
RPF("IDirectInputDevice::EnumEffectsInFile: Invalid return value from enumeration callback");
|
|
ValidationException();
|
|
break;
|
|
}
|
|
}
|
|
RIFF_Close(hmmio, 0);
|
|
//if hresRead failed because couldn't descend into the chunk, it means the end of file,
|
|
//so everything is OK;
|
|
//else return this error
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (hresRead == hresLe(ERROR_SECTOR_NOT_FOUND))
|
|
{
|
|
hres = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hres = hresRead;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDIDev_EnumEffectsInFileW
|
|
(
|
|
PV pddW,
|
|
LPCWSTR lpszFileName,
|
|
LPDIENUMEFFECTSINFILECALLBACK pec,
|
|
LPVOID pvRef,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
|
|
HRESULT hres = E_FAIL;
|
|
|
|
EnterProcR(IDirectInputDevice::EnumEffectsInFileW, (_ "s", lpszFileName));
|
|
|
|
/* Validate incoming parameters */
|
|
if(SUCCEEDED(hres = hresPvW(pddW)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrW(lpszFileName, MAX_JOYSTRING,1)) &&
|
|
SUCCEEDED(hres = hresFullValidPfn(pec, 2)) &&
|
|
SUCCEEDED(hres = hresFullValidFl(dwFlags, DIFEF_ENUMVALID, 3)) )
|
|
{
|
|
CHAR szFileName[MAX_PATH];
|
|
|
|
PDD this = _thisPvNm(pddW, ddW);
|
|
|
|
UToA(szFileName, MAX_PATH, lpszFileName);
|
|
|
|
hres = CDIDev_EnumEffectsInFileA(&this->ddA, szFileName, pec, pvRef, dwFlags);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* @doc EXTERNAL
|
|
*
|
|
* @method HRESULT | IDirectInputDevice | WriteEffectToFile |
|
|
*
|
|
* Writes DIEFFECT struct(s) and effect GUID to a file.
|
|
* An application can use this in order to create pre-authored
|
|
* force effects.
|
|
*
|
|
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
|
|
*
|
|
* @parm LPCSTR | lpszFileName |
|
|
*
|
|
* Name of the RIFF file that contains collection of effects.
|
|
*
|
|
* @parm IN DWORD | dwEntries |
|
|
*
|
|
* Number of <t DIFILEEFFECT> structures in the array.
|
|
*
|
|
* @parm IN LPCDIFILEEFFECT | rgDiFileEft |
|
|
*
|
|
* Array of <t DIFILEEFFECT> structure.
|
|
*
|
|
*
|
|
* @parm IN DWORD | dwFlags |
|
|
*
|
|
* Flags which control how the effect should be written.
|
|
*
|
|
* It consists of <c DIFEF_*> flags, documented separately.
|
|
*
|
|
* @returns
|
|
*
|
|
* Returns a COM error code. The following error codes are
|
|
* intended to be illustrative and not necessarily comprehensive.
|
|
*
|
|
* <c DI_OK> = <c S_OK>: The operation completed successfully.
|
|
*
|
|
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
|
|
* <p lpDirectInputDevice> or
|
|
* <p lpdc> parameter is invalid.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
|
|
HRESULT CDIDev_WriteEffectToFileA
|
|
(
|
|
PV pddA,
|
|
LPCSTR lpszFileName,
|
|
DWORD dwEntries,
|
|
LPDIFILEEFFECT rgDiFileEffect,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
HRESULT hres = E_NOTIMPL;
|
|
|
|
EnterProcR(IDirectInputDevice:cd inc
|
|
:WriteEffectToFileA, (_ "s", lpszFileName));
|
|
|
|
|
|
/* Validate incoming parameters */
|
|
if(SUCCEEDED(hres = hresPvA(pddA)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrA(lpszFileName, MAX_JOYSTRING,1))&&
|
|
SUCCEEDED(hres = hresFullValidFl(dwFlags, DIFEF_ENUMVALID, 3)) &&
|
|
SUCCEEDED(hres = (IsBadReadPtr(rgDiFileEffect, cbX(*rgDiFileEffect))) ? E_POINTER : S_OK))
|
|
|
|
{
|
|
PDD this = _thisPvNm(pddA, ddA);
|
|
HMMIO hmmio;
|
|
MMCKINFO mmck;
|
|
DWORD dwEffectSz;
|
|
|
|
hres = RIFF_Open(lpszFileName, MMIO_CREATE | MMIO_WRITE | MMIO_ALLOCBUF , &hmmio, &mmck, &dwEffectSz);
|
|
|
|
if(SUCCEEDED(hres))
|
|
{
|
|
UINT nCount;
|
|
LPDIFILEEFFECT lpDiFileEf = rgDiFileEffect;
|
|
|
|
//write out the effects
|
|
for(nCount = 0; nCount < dwEntries; nCount++)
|
|
{
|
|
BOOL bInclude = TRUE;
|
|
|
|
hres = (IsBadReadPtr(lpDiFileEf, cbX(*lpDiFileEf))) ? E_POINTER : S_OK;
|
|
|
|
if (FAILED(hres))
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
//if necessary, check whether the effect is standard
|
|
if (!(dwFlags & DIFEF_INCLUDENONSTANDARD))
|
|
{
|
|
bInclude = CDIDev_IsStandardEffect(lpDiFileEf->GuidEffect);
|
|
}
|
|
|
|
if ((SUCCEEDED(hres)) && (bInclude == TRUE))
|
|
{
|
|
hres = RIFF_WriteEffect(hmmio, lpDiFileEf);
|
|
}
|
|
|
|
if(FAILED(hres))
|
|
{
|
|
break;
|
|
}
|
|
|
|
lpDiFileEf++;
|
|
|
|
}
|
|
RIFF_Close(hmmio, 0);
|
|
}
|
|
}
|
|
|
|
ExitOleProc();
|
|
return hres;
|
|
}
|
|
|
|
|
|
HRESULT CDIDev_WriteEffectToFileW
|
|
(
|
|
PV pddW,
|
|
LPCWSTR lpszFileName,
|
|
DWORD dwEntries,
|
|
LPDIFILEEFFECT lpDiFileEffect,
|
|
DWORD dwFlags
|
|
)
|
|
{
|
|
|
|
HRESULT hres = E_FAIL;
|
|
|
|
EnterProcR(IDirectInputDevice::WriteEffectToFile, (_ "s", lpszFileName));
|
|
|
|
/* Validate incoming parameters */
|
|
if(SUCCEEDED(hres = hresPvW(pddW)) &&
|
|
SUCCEEDED(hres = hresFullValidReadStrW(lpszFileName, MAX_JOYSTRING,1)))
|
|
{
|
|
CHAR szFileName[MAX_PATH];
|
|
|
|
PDD this = _thisPvNm(pddW, ddW);
|
|
|
|
UToA(szFileName, MAX_PATH, lpszFileName);
|
|
|
|
hres = CDIDev_WriteEffectToFileA(&this->ddA, szFileName, dwEntries, lpDiFileEffect, dwFlags);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
/*****************************************************************************
|
|
*
|
|
* The long-awaited vtbls and templates
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
#define CDIDev_Signature 0x20564544 /* "DEV " */
|
|
|
|
Primary_Interface_Begin(CDIDev, TFORM(ThisInterfaceT))
|
|
TFORM(CDIDev_GetCapabilities),
|
|
TFORM(CDIDev_EnumObjects),
|
|
TFORM(CDIDev_GetProperty),
|
|
TFORM(CDIDev_SetProperty),
|
|
TFORM(CDIDev_Acquire),
|
|
TFORM(CDIDev_Unacquire),
|
|
TFORM(CDIDev_GetDeviceState),
|
|
TFORM(CDIDev_GetDeviceData),
|
|
TFORM(CDIDev_SetDataFormat),
|
|
TFORM(CDIDev_SetEventNotification),
|
|
TFORM(CDIDev_SetCooperativeLevel),
|
|
TFORM(CDIDev_GetObjectInfo),
|
|
TFORM(CDIDev_GetDeviceInfo),
|
|
TFORM(CDIDev_RunControlPanel),
|
|
TFORM(CDIDev_Initialize),
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
TFORM(CDIDev_CreateEffect),
|
|
TFORM(CDIDev_EnumEffects),
|
|
TFORM(CDIDev_GetEffectInfo),
|
|
TFORM(CDIDev_GetForceFeedbackState),
|
|
TFORM(CDIDev_SendForceFeedbackCommand),
|
|
TFORM(CDIDev_EnumCreatedEffectObjects),
|
|
TFORM(CDIDev_Escape),
|
|
TFORM(CDIDev_Poll),
|
|
TFORM(CDIDev_SendDeviceData),
|
|
#ifdef IDirectInputDevice7Vtbl
|
|
TFORM(CDIDev_EnumEffectsInFile),
|
|
TFORM(CDIDev_WriteEffectToFile),
|
|
#endif
|
|
#endif
|
|
|
|
Primary_Interface_End(CDIDev, TFORM(ThisInterfaceT))
|
|
|
|
Secondary_Interface_Begin(CDIDev, SFORM(ThisInterfaceT), SFORM(dd))
|
|
SFORM(CDIDev_GetCapabilities),
|
|
SFORM(CDIDev_EnumObjects),
|
|
SFORM(CDIDev_GetProperty),
|
|
SFORM(CDIDev_SetProperty),
|
|
SFORM(CDIDev_Acquire),
|
|
SFORM(CDIDev_Unacquire),
|
|
SFORM(CDIDev_GetDeviceState),
|
|
SFORM(CDIDev_GetDeviceData),
|
|
SFORM(CDIDev_SetDataFormat),
|
|
SFORM(CDIDev_SetEventNotification),
|
|
SFORM(CDIDev_SetCooperativeLevel),
|
|
SFORM(CDIDev_GetObjectInfo),
|
|
SFORM(CDIDev_GetDeviceInfo),
|
|
SFORM(CDIDev_RunControlPanel),
|
|
SFORM(CDIDev_Initialize),
|
|
#ifdef IDirectInputDevice2Vtbl
|
|
SFORM(CDIDev_CreateEffect),
|
|
SFORM(CDIDev_EnumEffects),
|
|
SFORM(CDIDev_GetEffectInfo),
|
|
SFORM(CDIDev_GetForceFeedbackState),
|
|
SFORM(CDIDev_SendForceFeedbackCommand),
|
|
SFORM(CDIDev_EnumCreatedEffectObjects),
|
|
TFORM(CDIDev_Escape),
|
|
SFORM(CDIDev_Poll),
|
|
SFORM(CDIDev_SendDeviceData),
|
|
#ifdef IDirectInputDevice7Vtbl
|
|
SFORM(CDIDev_EnumEffectsInFile),
|
|
SFORM(CDIDev_WriteEffectToFile),
|
|
#endif
|
|
#endif
|
|
Secondary_Interface_End(CDIDev, SFORM(ThisInterfaceT), SFORM(dd))
|