/*****************************************************************************
*
* 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
. When foreground
* activation is lost, all objects in the array are unacquired.
*
*
* @global UINT | g_cpddForegroundMax |
*
* The size of the
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
* if all is well, or 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
*
* if the device is exclusively acquired.
*
* if acquisition has been lost.
*
* 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 .
*
*****************************************************************************
*
* @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 .
*
*****************************************************************************
*
* @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 .
*
*****************************************************************************/
#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 structure that is filled in
* by the function. The
* field "must" be filled in
* by the application before calling this method.
* See the documentation of the structure
* for additional information.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* = : The
* or
*
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
* macro.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
* Note that if the callback stops the enumeration prematurely,
* the enumeration is considered to have succeeded.
*
* = : The
* 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
* method.
*
* @parm IN LPCDIDEVICEOBJECTINSTANCE | lpddoi |
*
* A structure which describes
* the object being enumerated.
*
* @parm IN OUT LPVOID | pvRef |
* Specifies the application-defined value given in the
* function.
*
* @returns
*
* Returns to continue the enumeration
* or 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
* which
* translates the UNICODE string to ANSI.
*
* @parm IN LPCDIENUMDEVICEOBJECTINSTANCE | pdoiW |
*
* Structure to be translated to ANSI.
*
* @parm IN LPVOID | pvRef |
*
* Pointer to 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 for more information.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm IN LPDIENUMDEVICEOBJECTSCALLBACK | lpCallback |
*
* Same as , except in ANSI.
*
* @parm IN LPVOID | pvRef |
*
* Same as .
*
* @parm IN DWORD | fl |
*
* Same as .
*
*****************************************************************************/
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 on the event
* while it has been selected into an
* object. You must call
* with the
* 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 the handle when
* the state of the device changes.
*
* The application should create the handle via the
* 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 to reset it. DirectInput will not
* call for event notification handles.
*
* If the 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.
*
* = : The operation completed successfully.
*
* : The object
* already has an event notification handle
* associated with it. DirectInput supports only one event
* notification handle per object.
*
* : The object
* has been acquired. You must
* the device before you can change the notification state.
*
* : 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 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 and
fields into an object index.
*
* @parm DWORD | dwObj |
*
* Object identifier.
*
* @parm DWORD | dwHow |
*
* How
should be interpreted.
*
* @parm OUT LPDIPROPINFO | ppropi |
*
* Receives object index and
.
*
*****************************************************************************/
#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
* 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 parameter.
* See the documentation of the
* structure for additional information.
*
* @parm DWORD | dwHow |
*
* Identifies how is to be interpreted.
* It must be one of the values.
* See the documentation of the
* structure for additional information.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* = : One or more
* parameters was invalid.
*
* : 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 is to be interpreted.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* = : 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
* field before calling this method.
*
* If is equal to the size of
* the 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.
*
* = : The operation completed successfully.
*
* = : 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.
*
* = : The operation completed successfully.
*
* = : 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 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
* 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 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 hook doesn't
* find out that the input has been lost until the app next
* calls .
*
* So the way this is done is via a collection of global
* variables (which must be accessed atomically).
*
* 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 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
* method.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* : The device has already been acquired. Note
* that this value is a success code.
*
* : Access to the device was not granted.
* The most common cause of this is attempting to acquire a
* device with the 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.
*
* = : 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
* instead of .
*
* If the application error code is , 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.
*
* = : The operation completed successfully.
*
* : 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 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.
* if being validated for writing.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : Passes validation.
*
* : 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
* 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.
*
* = : The operation completed successfully.
*
* = : The
* 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 values, or it may
* be a private GUID.
*
* @parm IN LPDIPROPHEADER | pdiph |
*
* Points to the 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.
*
* = : The operation completed successfully.
*
* = : The
* parameter is not a valid pointer, or the
*
field is invalid, or the
field
* is not zero when
is set to .
*
* : The specified object does not
* exist.
*
* = : The property
* is not supported by the device or object.
*
* @ex
*
* The following "C" code fragment illustrates how to obtain
* the value of the 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.
*
* = : 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.
*
* = : 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
* 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. 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
* 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.
*
* = : The operation completed successfully.
*
* : 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.)
*
* = : The
* 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.
*
* 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 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 values, or it may
* be a pointer to a private GUID.
*
* @parm IN LPDIPROPHEADER | pdiph |
*
* Points to the 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.
*
* = : The operation completed successfully.
*
* : The operation completed
* successfully but
* had no effect. For example, changing the axis mode
* on a device with no axes will return this value.
*
* = : The
* parameter is not a valid pointer, or the
*
field is invalid, or the
field
* is not zero when
is set to .
*
* : The specified object does not
* exist.
*
* = : 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 flags, documented separately.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* = : The
* 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.
*
* = : The device is attached.
*
* @devnote
*
* The is eventually going to allow
* 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 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 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.
*
* = : The operation completed successfully.
*
* : The device had already been initialized with
* the instance GUID passed in .
*
* : The device cannot be initialized while
* it is acquired.
*
* : The instance GUID does not exist
* on the current machine.
*
* :
* 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::, (_ "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.
*
* = : The operation completed successfully.
*
* : The effect can't be supported by the current device
* (e.g. the number of FF axes on the device is 0)
*
* : 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
* function.
*
* @parm IN DWORD | dwFlags |
*
* Flags which control the enumeration.
*
* It consists of flags, documented separately.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* = : The
* or
*
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
* method.
*
* @parm OUT LPCDIFILEEFFECT | lpDiFileEf |
*
* Pointer to a DIFILEEFFECT structure.
*
*
* @parm IN OUT LPVOID | pvRef |
* Specifies the application-defined value given in the
* function.
*
* @returns
*
* Returns to continue the enumeration
* or 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 structures in the array.
*
* @parm IN LPCDIFILEEFFECT | rgDiFileEft |
*
* Array of structure.
*
*
* @parm IN DWORD | dwFlags |
*
* Flags which control how the effect should be written.
*
* It consists of flags, documented separately.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* = : The operation completed successfully.
*
* = : The
* or
*
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))