windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/dx7/dll/didevdf.c

2485 lines
79 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************
*
* DIDevDf.c
*
* Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* The part of IDirectInputDevice that worries about
* data formats and reading device data.
*
*****************************************************************************/
#include "dinputpr.h"
#include "didev.h"
int INTERNAL
CDIDev_OffsetToIobj(PDD this, DWORD dwOfs);
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDev_GetAbsDeviceState |
*
* Get the absolute device state.
*
* @parm OUT LPVOID | pvData |
*
* Application-provided output buffer.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetAbsDeviceState(PDD this, LPVOID pvData)
{
return this->pdcb->lpVtbl->GetDeviceState(this->pdcb, pvData);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDev_GetRelDeviceState |
*
* Get the relative device state.
*
* @parm OUT LPVOID | pvData |
*
* Application-provided output buffer.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetRelDeviceState(PDD this, LPVOID pvData)
{
HRESULT hres;
hres = this->pdcb->lpVtbl->GetDeviceState(this->pdcb, pvData);
if ( SUCCEEDED(hres) ) {
UINT iaxis;
AssertF(fLimpFF(this->cAxes, this->pvLastBuffer && this->rgdwAxesOfs));
/*
* For each axis, replace the app's buffer with the delta,
* and save the old value.
*/
for ( iaxis = 0; iaxis < this->cAxes; iaxis++ ) {
LONG UNALIGNED *plApp = pvAddPvCb(pvData, this->rgdwAxesOfs[iaxis]);
LONG UNALIGNED *plLast = pvAddPvCb(this->pvLastBuffer,
this->rgdwAxesOfs[iaxis]);
LONG lNew = *plApp;
*plApp -= *plLast;
*plLast = lNew;
}
hres = S_OK;
}
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDev_GetDeviceStateSlow |
*
* Obtains data from the DirectInput device the slow way.
*
* Read the data into the private buffer, then copy it
* bit by bit into the application's buffer.
*
* @parm OUT LPVOID | lpvData |
*
* Application-provided output buffer.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetDeviceStateSlow(PDD this, LPVOID pvData)
{
HRESULT hres;
EnterProcR(IDirectInputDevice::GetDeviceStateSlow, (_ "pp", this, pvData));
AssertF(this->diopt == dioptNone);
AssertF(this->pvBuffer);
AssertF(this->pdcb);
hres = this->GetDeviceState(this, this->pvBuffer);
if ( SUCCEEDED(hres) ) {
int iobj;
ZeroMemory(pvData, this->dwDataSize);
for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
if ( this->pdix[iobj].dwOfs != 0xFFFFFFFF ) { /* Data was requested */
DWORD UNALIGNED *pdwOut = pvAddPvCb(pvData, this->pdix[iobj].dwOfs);
DWORD UNALIGNED *pdwIn = pvAddPvCb(this->pvBuffer, this->df.rgodf[iobj].dwOfs);
if ( this->df.rgodf[iobj].dwType & DIDFT_DWORDOBJS ) {
*pdwOut = *pdwIn;
} else {
*(LPBYTE)pdwOut = *(LPBYTE)pdwIn;
}
}
}
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDev_GetDeviceStateMatched |
*
* Obtains data from the DirectInput device in the case
* where the data formats are matched.
*
* Read the data into the private buffer, then block copy it
* into the application's buffer.
*
* @parm OUT LPVOID | lpvData |
*
* Application-provided output buffer.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetDeviceStateMatched(PDD this, LPVOID pvData)
{
HRESULT hres;
EnterProcR(IDirectInputDevice::GetDeviceStateMatched, (_ "pp", this, pvData));
AssertF(this->diopt == dioptMatch);
AssertF(this->pvBuffer);
AssertF(this->pdcb);
hres = this->GetDeviceState(this, this->pvBuffer);
if ( SUCCEEDED(hres) ) {
/*
* To keep keyboard clients happy: Zero out the fore and aft.
* No need to optimize the perfect match case, because that
* gets a different optimization level.
*/
ZeroMemory(pvData, this->dwDataSize);
memcpy(pvAddPvCb(pvData, this->ibDelta + this->ibMin),
pvAddPvCb(this->pvBuffer, this->ibMin), this->cbMatch);
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDev_GetDeviceStateDirect |
*
* Obtains data from the DirectInput device in the case
* where we can read the data directly into the client buffer.
*
* @parm OUT LPVOID | lpvData |
*
* Application-provided output buffer.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetDeviceStateDirect(PDD this, LPVOID pvData)
{
HRESULT hres;
EnterProcR(IDirectInputDevice::GetDeviceStateDirect, (_ "pp", this, pvData));
AssertF(this->diopt == dioptDirect);
AssertF(!this->pvBuffer);
AssertF(this->pdcb);
/*
* To keep keyboard clients happy: Zero out the fore and aft.
*/
ZeroBuf(pvData, this->dwDataSize);
hres = this->GetDeviceState(this, pvAddPvCb(pvData, this->ibDelta));
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @func HRESULT | CDIDev_GetDeviceStateEqual |
*
* Obtains data from the DirectInput device in the case
* where the two data formats are completely identical.
*
* @parm OUT LPVOID | lpvData |
*
* Application-provided output buffer.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetDeviceStateEqual(PDD this, LPVOID pvData)
{
HRESULT hres;
EnterProcR(IEqualInputDevice::GetDeviceStateEqual, (_ "pp", this, pvData));
AssertF(this->diopt == dioptEqual);
AssertF(this->ibDelta == 0);
AssertF(this->dwDataSize == this->df.dwDataSize);
AssertF(!this->pvBuffer);
AssertF(this->pdcb);
/*
* Note that this->ibMin is not necessarily zero if the device
* data format doesn't begin at zero (which keyboards don't).
*/
hres = this->GetDeviceState(this, pvData);
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method BOOL | CDIDev | IsMatchingGUID |
*
* Helper function that checks if a <t GUID> counts as
* a match when parsing the data format.
*
* @parm PCGUID | pguidSrc |
*
* The <t GUID> to check.
*
* @parm PCGUID | pguidDst |
*
* The <t GUID> it should match.
*
* @returns
*
* Nonzero if this counts as a success.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
GUID GUID_Null; /* A zero-filled guid */
#pragma END_CONST_DATA
BOOL INLINE
CDIDev_IsMatchingGUID(PDD this, PCGUID pguidSrc, PCGUID pguidDst)
{
return IsEqualGUID(pguidSrc, &GUID_Null) ||
IsEqualGUID(pguidSrc, pguidDst);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method BOOL | CDIDev | IsMatchingUsage |
*
* Helper function that checks if a <f DIMAKEUSAGEDWORD>
* counts as a match when parsing the data format.
*
* @parm DWORD | dwUsage |
*
* The <f DIMAKEUSAGEDWORD> to check.
*
* @parm int | iobj |
*
* The index of hte object to check for a match.
*
* @returns
*
* Nonzero if this counts as a success.
*
*****************************************************************************/
BOOL INLINE
CDIDev_IsMatchingUsage(PDD this, DWORD dwUsage, int iobj)
{
AssertF(this->pdcb);
return dwUsage == this->pdcb->lpVtbl->GetUsage(this->pdcb, iobj);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method int | CDIDev | FindDeviceObjectFormat |
*
* Search the device object format table for the one that
* matches the guid in question.
*
* @parm PCODF | podf |
*
* The object to locate. If the <e DIOBJECTDATAFORMAT.rguid>
* is null, then the field is a wildcard.
*
* If the <e DIOBJECTDATAFORMAT.dwType> specifies
* <c DIDFT_ANYINSTANCE>, then any instance will be accepted.
*
* @parm PDIXLAT | pdix |
*
* The partial translation table so far. This is used to find
* an empty slot in case of wildcards.
*
* @returns
*
* Returns the index of the object that matches, or -1 if
* the object is not supported by the device.
*
* Someday: Should fall back to best match if types don't match.
*
*****************************************************************************/
int INTERNAL
CDIDev_FindDeviceObjectFormat(PDD this, PCODF podf, PDIXLAT pdix)
{
PCODF podfD; /* The format in the device */
UINT iobj;
/*
* We must count upwards, so that first-fit chooses the smallest one.
*/
for ( iobj = 0; iobj < this->df.dwNumObjs; iobj++ ) {
podfD = &this->df.rgodf[iobj];
if (
/*
* Type needs to match.
*
* Note that works for output-only actuators:
* Since you cannot read from an output-only
* actuator, you can't put it in a data format.
*
*/
(podf->dwType & DIDFT_TYPEVALID & podfD->dwType)
/*
* Attributes need to match.
*/
&& fHasAllBitsFlFl(podfD->dwType, podf->dwType & DIDFT_ATTRVALID)
/*
* Slot needs to be empty.
*/
&& pdix[iobj].dwOfs == 0xFFFFFFFF
/*
* "If there is a guid/usage, it must match."
*
* If pguid is NULL, then the match is vacuous.
*
* If DIDOI_GUIDISUSAGE is clear, then pguid points to
* a real GUID. GUID_NULL means "Don't care" and matches
* anything. Otherwise, it must match the actual GUID.
*
* If DIDOI_GUIDISUSAGE is set, then pguid is really
* a DIMAKEUSAGEDWORD of the usage and usage page,
* which we compare against the same in the object.
*/
&& (podf->pguid == 0 ||
((podf->dwFlags & DIDOI_GUIDISUSAGE) ?
CDIDev_IsMatchingUsage(this, (DWORD)(UINT_PTR)podf->pguid, iobj) :
CDIDev_IsMatchingGUID(this, podf->pguid, podfD->pguid)))
/*
* If there is an instance number, it must match.
*
* Note that we need to be careful how we check, because
* DX 3.0 and DX 5.0 uses different masks. (DX 5.0 needs
* 16 bits of instance data to accomodate HID devices.)
*/
&& fLimpFF((podf->dwType & this->didftInstance) !=
this->didftInstance,
fEqualMaskFlFl(this->didftInstance,
podf->dwType, podfD->dwType))
/*
* If there is an aspect, it must match.
*
* If the device data format doesn't specify an aspect,
* then that counts as a free match too.
*/
&& fLimpFF((podf->dwFlags & DIDOI_ASPECTMASK) &&
(podfD->dwFlags & DIDOI_ASPECTMASK),
fEqualMaskFlFl(DIDOI_ASPECTMASK,
podf->dwFlags, podfD->dwFlags))
) { /* Criterion matches, woo-hoo */
return iobj;
}
}
return -1;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIDev | ParseDataFormat |
*
* Parse the data format passed by the application and
* convert it into a format that we can use to translate
* the device data into application data.
*
* @parm IN LPDIDATAFORMAT | lpdf |
*
* Points to a structure that describes the format of the data
* the DirectInputDevice should return.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p lpvData> parameter is not a valid pointer.
*
* <c DIERR_ACQUIRED>: Cannot change the data format while the
* device is acquired.
*
*
*****************************************************************************/
STDMETHODIMP
CDIDev_ParseDataFormat(PDD this, const DIDATAFORMAT *lpdf)
{
PDIXLAT pdix;
// Prefix Whistler: 45081
PINT rgiobj = NULL;
HRESULT hres;
DIPROPDWORD dipdw;
VXDDATAFORMAT vdf;
DWORD dwDataSize;
#ifdef DEBUG
EnterProc(CDIDev_ParseDataFormat, (_ "pp", this, lpdf));
#else
EnterProcR(IDirectInputDevice::SetDataFormat, (_ "pp", this, lpdf));
#endif
/*
* Caller should've nuked the old translation table.
*/
AssertF(this->pdix == 0);
AssertF(this->rgiobj == 0);
AssertF(this->cdwPOV == 0);
vdf.cbData = this->df.dwDataSize;
vdf.pDfOfs = 0;
/*
* If the device is cooked, then we stash the client offset
* into the high word of the VxD data, so it had better fit into
* a word...
*/
dwDataSize = min(lpdf->dwDataSize, 0x00010000);
if ( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix)) &&
SUCCEEDED(hres = AllocCbPpv(cbCdw(this->df.dwDataSize), &vdf.pDfOfs)) &&
SUCCEEDED(hres = AllocCbPpv(cbCdw(lpdf->dwDataSize), &rgiobj)) &&
SUCCEEDED(hres =
ReallocCbPpv(cbCdw(lpdf->dwNumObjs), &this->rgdwPOV)) ) {
UINT iobj;
/*
* Pre-init all the translation tags to -1,
* which means "not in use"
*/
memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT));
memset(vdf.pDfOfs, 0xFF, cbCdw(this->df.dwDataSize));
memset(rgiobj, 0xFF, cbCdw(lpdf->dwDataSize));
SquirtSqflPtszV(sqflDf, TEXT("Begin parse data format"));
for ( iobj = 0; iobj < lpdf->dwNumObjs; iobj++ ) {
PCODF podf = &lpdf->rgodf[iobj];
SquirtSqflPtszV(sqflDf, TEXT("Object %2d: offset %08x"),
iobj, podf->dwOfs);
/*
* Note that the podf->dwOfs < dwDataSize test is safe
* even for DWORD objects, since we also check that both
* values are DWORD multiples.
*/
if ( ((podf->dwFlags & DIDOI_GUIDISUSAGE) ||
fLimpFF(podf->pguid,
SUCCEEDED(hres = hresFullValidGuid(podf->pguid, 1)))) &&
podf->dwOfs < dwDataSize ) {
int iobjDev = CDIDev_FindDeviceObjectFormat(this, podf, pdix);
/* Hack for pre DX6 apps that only look for a Z axis,
* newer USB devices use GUID_Slider for the same functionality.
*
*/
if ( podf->pguid != 0x0 // Looking for matching GUID
&& this->dwVersion < 0x600 // Only for Dx version < 0x600
&& iobjDev == -1 // Did not find default mapping
&& IsEqualGUID(podf->pguid, &GUID_ZAxis) ) // Looking for GUID_ZAxis
{
ODF odf = lpdf->rgodf[iobj]; // Make a copy of the object data format
odf.pguid = &GUID_Slider; // Substitute Slider for Z axis
iobjDev = CDIDev_FindDeviceObjectFormat(this, &odf, pdix);
}
if ( iobjDev != -1 ) {
PCODF podfFound = &this->df.rgodf[iobjDev];
if ( podfFound->dwType & DIDFT_DWORDOBJS ) {
if ( (podf->dwOfs & 3) == 0 ) {
} else {
RPF("%s: Dword objects must be aligned", s_szProc);
goto fail;
}
}
if ( this->fCook ) {
vdf.pDfOfs[podfFound->dwOfs] =
(DWORD)DICOOK_DFOFSFROMOFSID(podf->dwOfs,
podfFound->dwType);
} else {
vdf.pDfOfs[podfFound->dwOfs] = podf->dwOfs;
}
pdix[iobjDev].dwOfs = podf->dwOfs;
rgiobj[podf->dwOfs] = iobjDev;
if ( podfFound->dwFlags & DIDOI_POLLED ) {
this->fPolledDataFormat = TRUE;
}
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = podfFound->dwType;
dipdw.diph.dwHow = DIPH_BYID;
dipdw.dwData = 0x1; // Enable this report ID
hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
if ( hres == E_NOTIMPL )
{
SquirtSqflPtszV(sqflDf,
TEXT("Could not set DIPROP_ENABLEREPORTID for offset %d"),
iobj);
hres = S_OK;
}
} else if ( podf->dwType & DIDFT_OPTIONAL ) {
SquirtSqflPtszV(sqflDf,
TEXT("Object %2d: Skipped (optional)"),
iobj);
/*
* We need to remember where the failed POVs live
* so we can neutralize them in GetDeviceState().
*/
if ( podf->dwType & DIDFT_POV ) {
AssertF(this->cdwPOV < lpdf->dwNumObjs);
this->rgdwPOV[this->cdwPOV++] = podf->dwOfs;
}
} else {
RPF("%s: Format not compatible with device", s_szProc);
goto fail;
}
} else {
if ( podf->dwOfs >= lpdf->dwDataSize ) {
RPF("%s: Offset out of range in data format", s_szProc);
} else if ( podf->dwOfs >= dwDataSize ) {
RPF("%s: Data format cannot exceed 64K", s_szProc);
}
fail:;
hres = E_INVALIDARG;
goto done;
}
}
#ifdef DEBUG
/*
* Double-check the lookup tables just to preserve our sanity.
*/
{
UINT dwOfs;
for ( dwOfs = 0; dwOfs < lpdf->dwDataSize; dwOfs++ ) {
if ( rgiobj[dwOfs] >= 0 ) {
AssertF(pdix[rgiobj[dwOfs]].dwOfs == dwOfs);
} else {
AssertF(rgiobj[dwOfs] == -1);
}
}
}
#endif
/*
* Shrink the "failed POV" array to its actual size.
* The shrink "should" always succeed. Note also that
* even if it fails, we're okay; we just waste a little
* memory.
*/
hres = ReallocCbPpv(cbCdw(this->cdwPOV), &this->rgdwPOV);
AssertF(SUCCEEDED(hres));
/*
* If we are using cooked data, then we actually hand the
* device driver a different translation table which
* combines the offset and dwDevType so data cooking can
* happen safely.
*/
vdf.pvi = this->pvi;
if ( fLimpFF(this->pvi,
SUCCEEDED(hres = Hel_SetDataFormat(&vdf))) ) {
this->pdix = pdix;
pdix = 0;
this->rgiobj = rgiobj;
rgiobj = 0;
this->dwDataSize = lpdf->dwDataSize;
hres = S_OK;
} else {
AssertF(FAILED(hres));
}
} else {
/* Out of memory */
}
done:;
FreePpv(&pdix);
FreePpv(&rgiobj);
FreePpv(&vdf.pDfOfs);
ExitOleProc();
return hres;
}
#ifdef BUGGY_DX7_WINNT
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIDev | ParseDataFormatInternal |
*
* Parse the data format passed by CDIDev_Intialize and
* convert it into a format that we can use to translate
* the device data into application data. Used on WINNT only.
*
* @parm IN LPDIDATAFORMAT | lpdf |
*
* Points to a structure that describes the format of the data
* the DirectInputDevice should return.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p lpvData> parameter is not a valid pointer.
*
* <c DIERR_ACQUIRED>: Cannot change the data format while the
* device is acquired.
*
* @devnotes:
* This function is originaly wrote to fix manbug: 41464.
* Boarder Zone gets object's data offset before it calls SetDataFormat,
* so Dinput returns it internal data offsets. But it uses them as user data
* offset, which causes the bug.
* To fix it, we call this function in CDIDev_Initialize with c_dfDIJoystick,
* which is used by many games. When an application ask for object info,
* we check if an user data format has been set, if not, we will assume user
* uses c_dfDIJoystick, hence return the data offset based on it.
* This function will only be called if application uses dinput (version < 0x700)
* && (version != 0x5B2).
*
*****************************************************************************/
HRESULT CDIDev_ParseDataFormatInternal(PDD this, const DIDATAFORMAT *lpdf)
{
PDIXLAT pdix;
PINT rgiobj = NULL;
HRESULT hres;
#ifdef DEBUG
EnterProc(CDIDev_ParseDataFormat2, (_ "pp", this, lpdf));
#endif
AssertF(this->pdix2 == 0);
if( SUCCEEDED(hres = AllocCbPpv(cbCxX(this->df.dwNumObjs, DIXLAT), &pdix)) &&
SUCCEEDED(hres = AllocCbPpv(cbCdw(lpdf->dwDataSize), &rgiobj)))
{
UINT iobj;
/*
* Pre-init all the translation tags to -1,
* which means "not in use"
*/
memset(pdix, 0xFF, cbCxX(this->df.dwNumObjs, DIXLAT));
memset(rgiobj, 0xFF, cbCdw(lpdf->dwDataSize));
for ( iobj = 0; iobj < lpdf->dwNumObjs; iobj++ ) {
PCODF podf = &lpdf->rgodf[iobj];
SquirtSqflPtszV(sqflDf | sqflVerbose, TEXT("Object %2d: offset %08x"),
iobj, podf->dwOfs);
/*
* Note that the podf->dwOfs < lpdf->dwDataSize test is safe
* even for DWORD objects, since we also check that both
* values are DWORD multiples.
*/
if ( ((podf->dwFlags & DIDOI_GUIDISUSAGE) ||
fLimpFF(podf->pguid,
SUCCEEDED(hres = hresFullValidGuid(podf->pguid, 1)))) &&
podf->dwOfs < lpdf->dwDataSize ) {
int iobjDev = CDIDev_FindDeviceObjectFormat(this, podf, pdix);
if ( iobjDev != -1 ) {
PCODF podfFound = &this->df.rgodf[iobjDev];
if ( podfFound->dwType & DIDFT_DWORDOBJS ) {
if ( (podf->dwOfs & 3) == 0 ) {
} else {
RPF("%s: Dword objects must be aligned", s_szProc);
goto fail;
}
}
pdix[iobjDev].dwOfs = podf->dwOfs;
rgiobj[podf->dwOfs] = iobjDev;
} else if ( podf->dwType & DIDFT_OPTIONAL ) {
//do nothing
} else {
RPF("%s: Format not compatible with device", s_szProc);
goto fail;
}
} else {
if ( podf->dwOfs >= lpdf->dwDataSize ) {
RPF("%s: rgodf[%d].dwOfs of 0x%08x out of range in data format",
s_szProc, iobj, podf->dwOfs );
}
fail:;
hres = E_INVALIDARG;
goto done;
}
}
#ifdef DEBUG
/*
* Double-check the lookup tables just to preserve our sanity.
*/
{
UINT dwOfs;
for ( dwOfs = 0; dwOfs < lpdf->dwDataSize; dwOfs++ ) {
if ( rgiobj[dwOfs] >= 0 ) {
AssertF(pdix[rgiobj[dwOfs]].dwOfs == dwOfs);
} else {
AssertF(rgiobj[dwOfs] == -1);
}
}
}
#endif
this->pdix2 = pdix;
pdix = 0;
this->rgiobj2 = rgiobj;
rgiobj = 0;
this->dwDataSize2 = lpdf->dwDataSize;
hres = S_OK;
} else {
/* Out of memory */
hres = ERROR_NOT_ENOUGH_MEMORY;
}
done:;
FreePpv(&pdix);
FreePpv(&rgiobj);
ExitOleProc();
return hres;
}
#endif //BUGGY_DX7_WINNT
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIDev | OptimizeDataFormat |
*
* Study the parsed data format to determine whether we can
* used an optimized <mf CDIDev::GetDeviceState> to obtain
* the data more quickly.
*
* The data format is considered optimized if it matches the
* device data format, modulo possible shifting due to insertion
* of bonus fields at the beginning or end, and modulo missing
* fields.
*
* The data format is considered fully-optimized if it
* optimized, and no shifting is necessary, and the structure size
* is exactly the same. This means the buffer can be passed
* straight through to the driver.
*
*
*****************************************************************************/
HRESULT INTERNAL
CDIDev_OptimizeDataFormat(PDD this)
{
int ib;
DWORD ibMax; /* One past highest match point */
DWORD ibMin; /* Lowest match point */
int iobj;
DWORD dwDataSize;
HRESULT hres;
EnterProc(CDIDev_OptimizeDataFormat, (_ "p", this));
ib = -1; /* Not yet known */
ibMin = 0xFFFFFFFF;
ibMax = 0;
/*
* ISSUE-2001/03/29-timgill Need to change data sentinel value
* -1 is not a valid sentinel; we might validly
* get data at an offset of -1.
*/
for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
DWORD ibMaxThis;
if ( this->pdix[iobj].dwOfs != 0xFFFFFFFF ) { /* Data was requested */
int ibExpected = (int)(this->pdix[iobj].dwOfs -
this->df.rgodf[iobj].dwOfs);
if ( fLimpFF(ib != -1, ib == ibExpected) ) {
ib = ibExpected;
} else {
SquirtSqflPtszV(sqfl | sqflMajor,
TEXT("IDirectInputDevice: Optimization level 0, translation needed") );
this->diopt = dioptNone;
this->GetState = CDIDev_GetDeviceStateSlow;
goto done;
}
if ( ibMin > this->df.rgodf[iobj].dwOfs ) {
ibMin = this->df.rgodf[iobj].dwOfs;
}
if ( this->df.rgodf[iobj].dwType & DIDFT_DWORDOBJS ) {
ibMaxThis = this->df.rgodf[iobj].dwOfs + sizeof(DWORD);
} else {
ibMaxThis = this->df.rgodf[iobj].dwOfs + sizeof(BYTE);
}
if ( ibMax < ibMaxThis ) {
ibMax = ibMaxThis;
}
}
}
/*
* Make sure we actually found something.
*/
if ( ib != -1 ) { /* Data format is matched */
AssertF(ibMin < ibMax);
AssertF( ib + (int)ibMin >= 0);
AssertF(ib + ibMax <= this->dwDataSize);
this->ibDelta = ib;
this->ibMin = ibMin;
this->cbMatch = ibMax - ibMin;
if ( ib >= 0 && ib + this->df.dwDataSize <= this->dwDataSize ) {
/* We can go direct */
if ( ib == 0 && this->dwDataSize == this->df.dwDataSize ) {
/* Data formats are equal! */
this->diopt = dioptEqual;
this->GetState = CDIDev_GetDeviceStateEqual;
SquirtSqflPtszV(sqfl | sqflMajor,
TEXT("IDirectInputDevice: Optimization level 3, full speed ahead!") );
} else {
this->diopt = dioptDirect;
this->GetState = CDIDev_GetDeviceStateDirect;
SquirtSqflPtszV(sqfl | sqflMajor,
TEXT("IDirectInputDevice: Optimization level 2, direct access") );
}
} else {
SquirtSqflPtszV(sqfl | sqflMajor,
TEXT("IDirectInputDevice: Optimization level 1, okay") );
this->diopt = dioptMatch;
this->GetState = CDIDev_GetDeviceStateMatched;
}
} else { /* No data in data format! */
RPF("IDirectInputDevice: Null data format; if that's what you want...");
this->diopt = dioptNone;
this->GetState = CDIDev_GetDeviceStateSlow;
}
done:;
if ( this->diopt >= dioptDirect ) { /* Can go direct; don't need buf */
dwDataSize = 0;
} else {
dwDataSize = this->df.dwDataSize;
}
hres = ReallocCbPpv(dwDataSize, &this->pvBuffer);
if ( SUCCEEDED(hres) ) {
AssertF(this->GetState);
} else {
FreePpv(&this->pdix);
D(this->GetState = 0);
}
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputDevice | SetDataFormat |
*
* Set the data format for the DirectInput device.
*
* The data format must be set before the device can be
* acquired.
*
* It is necessary to set the data format only once.
*
* The data format may not be changed while the device
* is acquired.
*
* If the attempt to set the data format fails, all data
* format information is lost, and a valid data format
* must be set before the device may be acquired.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm IN LPDIDATAFORMAT | lpdf |
*
* Points to a structure that describes the format of the data
* the DirectInputDevice should return.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p lpvData> parameter is not a valid pointer.
*
* <c DIERR_ACQUIRED>: Cannot change the data format while the
* device is acquired.
*
*
*****************************************************************************/
HRESULT INLINE
CDIDev_SetDataFormat_IsValidDataSize(LPCDIDATAFORMAT lpdf)
{
HRESULT hres;
if ( lpdf->dwDataSize % 4 == 0 ) {
hres = S_OK;
} else {
RPF("IDirectInputDevice::SetDataFormat: "
"dwDataSize must be a multiple of 4");
hres = E_INVALIDARG;
}
return hres;
}
HRESULT INLINE
CDIDev_SetDataFormat_IsValidObjectSize(LPCDIDATAFORMAT lpdf)
{
HRESULT hres;
if ( lpdf->dwObjSize == cbX(ODF) ) {
hres = S_OK;
} else {
RPF("IDirectInputDevice::SetDataFormat: Invalid dwObjSize");
hres = E_INVALIDARG;
}
return hres;
}
STDMETHODIMP
CDIDev_SetDataFormat(PV pdd, LPCDIDATAFORMAT lpdf _THAT)
{
HRESULT hres;
EnterProcR(IDirectInputDevice::SetDataFormat, (_ "pp", pdd, lpdf));
if ( SUCCEEDED(hres = hresPvT(pdd)) &&
SUCCEEDED(hres = hresFullValidReadPxCb(lpdf, DIDATAFORMAT, 1)) &&
SUCCEEDED(hres = hresFullValidFl(lpdf->dwFlags, DIDF_VALID, 1)) &&
SUCCEEDED(hres = CDIDev_SetDataFormat_IsValidDataSize(lpdf)) &&
SUCCEEDED(hres = CDIDev_SetDataFormat_IsValidObjectSize(lpdf)) &&
SUCCEEDED(hres = hresFullValidReadPvCb(lpdf->rgodf,
cbCxX(lpdf->dwNumObjs, ODF), 1)) ) {
PDD this = _thisPv(pdd);
/*
* Must protect with the critical section to prevent two people
* from changing the format simultaneously, or one person from
* changing the data format while somebody else is reading data.
*/
CDIDev_EnterCrit(this);
#if DIRECTINPUT_VERSION >= 0x04F0
if ( this->dwVersion == 0 ) {
RPF("Warning: IDirectInputDevice::Initialize not called; "
"assuming version 3.0");
}
#endif
if ( !this->fAcquired ) {
DIPROPDWORD dipdw;
/*
* Nuke the old data format stuff before proceeding.
*/
FreePpv(&this->pdix);
FreePpv(&this->rgiobj);
this->cdwPOV = 0;
D(this->GetState = 0);
this->fPolledDataFormat = FALSE;
/*
* Wipe out the report IDs
*/
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0x0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = 0; // Nuke all knowledge of reportId's
hres = CDIDev_RealSetProperty(this, DIPROP_ENABLEREPORTID, &dipdw.diph);
if ( SUCCEEDED(hres) || hres == E_NOTIMPL )
{
hres = CDIDev_ParseDataFormat(this, lpdf);
if ( SUCCEEDED(hres) ) {
hres = CDIDev_OptimizeDataFormat(this);
/*
* Now set the axis mode, as a convenience.
*/
CAssertF(DIDF_VALID == (DIDF_RELAXIS | DIDF_ABSAXIS));
switch ( lpdf->dwFlags ) {
case 0:
hres = S_OK;
goto axisdone;
case DIDF_RELAXIS:
dipdw.dwData = DIPROPAXISMODE_REL;
break;
case DIDF_ABSAXIS:
dipdw.dwData = DIPROPAXISMODE_ABS;
break;
default:
RPF("%s: Cannot combine DIDF_RELAXIS with DIDF_ABSAXIS",
s_szProc);
hres = E_INVALIDARG;
goto axisdone;
}
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
hres = CDIDev_RealSetProperty(this, DIPROP_AXISMODE, &dipdw.diph);
if ( SUCCEEDED(hres) ) {
hres = S_OK;
}
}
} else
{
SquirtSqflPtszV(sqflDf,
TEXT("Could not set DIPROP_ENABLEREPORTID to 0x0"));
}
axisdone:;
} else { /* Already acquired */
hres = DIERR_ACQUIRED;
}
CDIDev_LeaveCrit(this);
}
ExitOleProcR();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputDevice | GetDeviceState |
*
* Obtains instantaneous data from the DirectInput device.
*
* Before device data can be obtained, the data format must
* be set via <mf IDirectInputDevice::SetDataFormat>, and
* the device must be acquired via
* <mf IDirectInputDevice::Acquire>.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm DWORD | cbData |
*
* The size of the buffer pointed to by <p lpvData>, in bytes.
*
* @parm OUT LPVOID | lpvData |
*
* Points to a structure that receives the current state
* of the device.
* The format of the data is established by a prior call
* to <mf IDirectInputDevice::SetDataFormat>.
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c E_PENDING>: The device does not have data yet.
* Some devices (such as USB joysticks) require a delay
* between the time the device is turned on and the time
* the device begins sending data. During this "warm-up" time,
* <mf IDirectInputDevice::GetDeviceState> will return
* <c E_PENDING>. When data becomes available, the event
* notification handle will be signalled.
*
* <c DIERR_NOTACQUIRED>: The device is not acquired.
*
* <c DIERR_INPUTLOST>: Access to the device has been
* interrupted. The application should re-acquire the
* device.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: The
* <p lpvData> parameter is not a valid pointer or
* the <p cbData> parameter does not match the data size
* set by a previous call to <mf IDirectInputDevice::SetDataFormat>.
*
*****************************************************************************/
extern STDMETHODIMP CDIDev_Acquire(PV pdd _THAT);
STDMETHODIMP
CDIDev_GetDeviceState(PV pdd, DWORD cbDataSize, LPVOID pvData _THAT)
{
HRESULT hres;
PDD this;
EnterProcR(IDirectInputDevice::GetDeviceState, (_ "pp", pdd, pvData));
/*
* Note that we do not validate the parameters.
* The reason is that GetDeviceState is an inner loop function,
* so it should be as fast as possible.
*/
#ifdef XDEBUG
hresPvT(pdd);
hresFullValidWritePvCb(pvData, cbDataSize, 1);
#endif
this = _thisPv(pdd);
/*
* Must protect with the critical section to prevent somebody from
* unacquiring while we're reading.
*/
CDIDev_EnterCrit(this);
/*
* Reacquire is not allowed until after Win98 SE, see OSR Bug # 89958
*/
#if (DIRECTINPUT_VERSION > 0x061A)
if ( this->diHacks.fReacquire &&
!this->fAcquired && (this->fOnceAcquired || this->fOnceForcedUnacquired) )
{
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
// 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
RPF(" DirectInput: Auto acquired (0x%p)", pdd);
}
}
#ifdef WINNT
if( this->fUnacquiredWhenIconic && !IsIconic(this->hwnd) ) {
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
this->fUnacquiredWhenIconic = 0;
RPF(" DirectInput: Auto acquired device (0x%p) after being iconic. ", pdd);
}
}
#endif
#endif
if ( this->fAcquired ) {
AssertF(this->pdix); /* Acquire shouldn't let you get this far */
AssertF(this->GetState);
AssertF(this->GetDeviceState);
AssertF(this->pdcb);
if ( this->dwDataSize == cbDataSize ) {
#ifndef DEBUG_STICKY
hres = this->GetState(this, pvData);
#else
PBYTE pbDbg;
TCHAR tszDbg[80];
hres = this->GetState(this, pvData);
for( pbDbg=(PBYTE)pvData; pbDbg<((PBYTE)pvData+cbDataSize); pbDbg++ )
{
if( *pbDbg )
{
wsprintf( tszDbg, TEXT("GotState @ 0x%02x, 0x%02x\r\n"), pbDbg-(PBYTE)pvData, *pbDbg );
OutputDebugString( tszDbg );
}
}
#endif /* DEBUG_STICKY */
if ( SUCCEEDED(hres) ) {
UINT idw;
AssertF(hres == S_OK);
/*
* Icky POV hack for apps that don't check if they have
* a POV before reading from it.
*/
for ( idw = 0; idw < this->cdwPOV; idw++ ) {
DWORD UNALIGNED *pdw = pvAddPvCb(pvData, this->rgdwPOV[idw]);
*pdw = JOY_POVCENTERED;
}
hres = S_OK;
} else if ( hres == DIERR_INPUTLOST ) {
RPF("%s: Input lost", s_szProc);
CDIDev_InternalUnacquire(this);
hres = DIERR_INPUTLOST;
}
} else {
RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
hres = E_INVALIDARG;
}
} else {
hres = this->hresNotAcquired;
}
if ( FAILED(hres) ) {
ScrambleBuf(pvData, cbDataSize);
}
CDIDev_LeaveCrit(this);
ExitOleProc();
return hres;
}
#if DIRECTINPUT_VERSION > 0x0300
/*****************************************************************************
*
* @doc INTERNAL
*
* @method void | IDirectInputDevice | CookDeviceData |
*
* Cook device data that was recently obtained from the
* device buffer.
*
* Right now, only the joystick device requires cooking,
* and nobody in their right mind uses buffered joystick
* data, and the joystick has only a few objects, so we
* can afford to be slow on this.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm UINT | cdod |
*
* Number of objects to cook.
*
* @parm LPDIDEVICEOBJECTDATA | rgdod |
*
* Array of object data to cook. The dwOfs are really
* device object indexes (relative to the device format).
* After calling the callback, we convert them into
* application data format offsets.
*
* @returns
*
* None.
*
*****************************************************************************/
void INTERNAL
CDIDev_CookDeviceData(PDD this, UINT cdod, PDOD rgdod)
{
UINT idod;
EnterProc(IDirectInputDevice::CookDeviceData,
(_ "pxp", this, cdod, rgdod));
AssertF(this->fCook);
/*
* Relative data does not need to be cooked by the callback.
*/
if( ( this->pvi->fl & VIFL_RELATIVE ) == 0 )
{
this->pdcb->lpVtbl->CookDeviceData(this->pdcb, cdod, rgdod);
}
for ( idod = 0; idod < cdod; idod++ ) {
rgdod[idod].dwOfs = DICOOK_OFSFROMDFOFS(rgdod[idod].dwOfs);
}
ExitProc();
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @struct SOMEDEVICEDATA |
*
* Instance data used by <mf IDirectInputDevice::GetSomeDeviceData>.
*
* @field DWORD | celtIn |
*
* Number of elements remaining in output buffer.
*
* @field PDOD | rgdod |
*
* Output buffer for data elements, or <c NULL> if
* elements should be discarded.
*
* @field DWORD | celtOut |
*
* Number of elements actually copied (so far).
*
*****************************************************************************/
typedef struct SOMEDEVICEDATA {
DWORD celtIn;
PDOD rgdod;
DWORD celtOut;
} SOMEDEVICEDATA, *PSOMEDEVICEDATA;
/*****************************************************************************
*
* @doc INTERNAL
*
* @method PDOD | IDirectInputDevice | GetSomeDeviceData |
*
* Obtains a small amount of
* buffered data from the DirectInput device.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm PDOD | pdod |
*
* First element to copy.
*
* @parm DWORD | celt |
*
* Maximum number of elements to copy.
*
* @parm PSOMEDEVICEDATA | psdd |
*
* Structure describing the state of the ongoing
* <mf IDirectInputDevice::GetDeviceData>.
*
* @returns
*
* Returns a pointer to the first uncopied item.
*
*****************************************************************************/
PDOD INTERNAL
CDIDev_GetSomeDeviceData(PDD this, PDOD pdod, DWORD celt, PSOMEDEVICEDATA psdd)
{
EnterProc(IDirectInputDevice::GetSomeDeviceData,
(_ "ppxx", this, pdod, celt, psdd->celtIn));
/*
* Copy as many elements as fit, but not more than exist
* in the output buffer.
*/
if ( celt > psdd->celtIn ) {
celt = psdd->celtIn;
}
/*
* Copy the elements (if requested) and update the state.
* Note that celt might be zero; fortunately, memcpy does
* the right thing.
*/
if ( psdd->rgdod ) {
memcpy(psdd->rgdod, pdod, cbCxX(celt, DOD));
psdd->rgdod += celt;
}
psdd->celtOut += celt;
psdd->celtIn -= celt;
pdod += celt;
if ( pdod == this->pvi->pEnd ) {
pdod = this->pvi->pBuffer;
}
ExitProcX(celt);
return pdod;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputDevice | GetDeviceData |
*
* Obtains buffered data from the DirectInput device.
*
* DirectInput devices are, by default, unbuffered. To
* turn on buffering, you must set the buffer size
* via <mf IDirectInputDevice::SetProperty>, setting the
* <c DIPROP_BUFFERSIZE> property to the desired size
* of the input buffer.
*
* Before device data can be obtained, the data format must
* be set via <mf IDirectInputDevice::SetDataFormat>, and
* the device must be acquired via
* <mf IDirectInputDevice::Acquire>.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm DWORD | cbObjectData |
*
* The size of a single <t DIDEVICEOBJECTDATA> structure in bytes.
*
* @parm OUT LPDIDEVICEOBJECTDATA | rgdod |
*
* Array of <t DIDEVICEOBJECTDATA> structures to receive
* the buffered data. It must consist of
* *<p pdwInOut> elements.
*
* If this parameter is <c NULL>, then the buffered data is
* not stored anywhere, but all other side-effects take place.
*
* @parm INOUT LPDWORD | pdwInOut |
*
* On entry, contains the number of elements in the array
* pointed to by <p rgdod>. On exit, contains the number
* of elements actually obtained.
*
* @parm DWORD | fl |
*
* Flags which control the manner in which data is obtained.
* It may be zero or more of the following flags:
*
* <c DIGDD_PEEK>: Do not remove the items from the buffer.
* A subsequent <mf IDirectInputDevice::GetDeviceData> will
* read the same data. Normally, data is removed from the
* buffer after it is read.
*
;begin_internal dx4
* <c DIGDD_RESIDUAL>: Read data from the device buffer
* even if the device is not acquired. Normally, attempting
* to read device data from an unacquired device will return
* <c DIERR_NOTACQUIRED> or <c DIERR_INPUTLOST>.
;end_internal dx4
*
* @returns
*
* <c DI_OK> = <c S_OK>: All data were retrieved
* successfully. Note that the application needs to check
* the output value of *<p pdwInOut> to determine whether
* and how much data was retrieved: The value may be zero,
* indicating that the buffer was empty.
*
* <c DI_BUFFEROVERFLOW> = <c S_FALSE>: Some data
* were retrieved successfully, but some data were lost
* because the device's buffer size was not large enough.
* The application should retrieve buffered data more frequently
* or increase the device buffer size. This status code is
* returned only on the first <mf IDirectInput::GetDeviceData>
* call after the buffer has overflowed. Note that this is
* a success status code.
*
* <c DIERR_NOTACQUIRED>: The device is not acquired.
*
* <c DIERR_INPUTLOST>: Access to the device has been
* interrupted. The application should re-acquire the
* device.
*
* <c DIERR_INVALIDPARAM> = <c E_INVALIDARG>: One or more
* parameters was invalid. A common cause for this is
* neglecting to set a buffer size.
*
* <c DIERR_NOTBUFFERED>: The device is not buffered.
* Set the <c DIPROP_BUFFERSIZE> property to enable buffering.
*
* @ex
*
* The following sample reads up to ten buffered data elements,
* removing them from the device buffer as they are read.
*
* |
*
* DIDEVICEOBJECTDATA rgdod[10];
* DWORD dwItems = 10;
* hres = IDirectInputDevice_GetDeviceData(
* pdid,
* sizeof(DIDEVICEOBJECTDATA),
* rgdod,
* &dwItems,
* 0);
* if (SUCCEEDED(hres)) {
* // Buffer successfully flushed.
* // dwItems = number of elements flushed
* if (hres == DI_BUFFEROVERFLOW) {
* // Buffer had overflowed.
* }
* }
*
*
*
*
* @ex
*
* If you pass <c NULL> for the <p rgdod> and request an
* infinite number of items, this has the effect of flushing
* the buffer and returning the number of items that were
* flushed.
*
* |
*
* dwItems = INFINITE;
* hres = IDirectInputDevice_GetDeviceData(
* pdid,
* sizeof(DIDEVICEOBJECTDATA),
* NULL,
* &dwItems,
* 0);
* if (SUCCEEDED(hres)) {
* // Buffer successfully flushed.
* // dwItems = number of elements flushed
* if (hres == DI_BUFFEROVERFLOW) {
* // Buffer had overflowed.
* }
* }
*
* @ex
*
* If you pass <c NULL> for the <p rgdod>, request an
* infinite number of items, and ask that the data not be
* removed from the device buffer, this has the effect of
* querying for the number of elements in the device buffer.
*
* |
*
* dwItems = INFINITE;
* hres = IDirectInputDevice_GetDeviceData(
* pdid,
* sizeof(DIDEVICEOBJECTDATA),
* NULL,
* &dwItems,
* DIGDD_PEEK);
* if (SUCCEEDED(hres)) {
* // dwItems = number of elements in buffer
* if (hres == DI_BUFFEROVERFLOW) {
* // Buffer overflow occurred; not all data
* // were successfully captured.
* }
* }
*
* @ex
*
* If you pass <c NULL> for the <p rgdod> and request zero
* items, this has the effect of querying whether buffer
* overflow has occurred.
*
* |
*
* dwItems = 0;
* hres = IDirectInputDevice_GetDeviceData(
* pdid,
* sizeof(DIDEVICEOBJECTDATA),
* NULL,
* &dwItems,
* 0);
* if (hres == DI_BUFFEROVERFLOW) {
* // Buffer overflow occurred
* }
*
*
*//**************************************************************************
*
* When reading this code, the following pictures will come in handy.
*
*
* Buffer not wrapped.
*
* pBuffer pEnd
* | |
* v v
* +----+----+----+----+----+----+----+----+----+----+----+
* | | | | | | | | | | | |
* | | | |data|data|data|data|data| | | |
* | | | | | | | | | | | |
* +----+----+----+----+----+----+----+----+----+----+----+
* ^ ^
* | |
* pTail pHead
*
*
* Buffer wrapped.
*
* pBuffer pEnd
* | |
* v v
* +----+----+----+----+----+----+----+----+----+----+----+
* | | | | | | | | | | | |
* |data|data| | | | | | |data|data|data|
* | | | | | | | | | | | |
* +----+----+----+----+----+----+----+----+----+----+----+
* ^ ^
* | |
* pHead pTail
*
*
* Boundary wrap case.
*
*
* pBuffer pEnd
* | |
* v v
* +----+----+----+----+----+----+----+----+----+----+----+
* | | | | | | | | | | | |
* | | | | | | |data|data|data|data|data|
* | | | | | | | | | | | |
* +----+----+----+----+----+----+----+----+----+----+----+
* ^ ^
* | |
* pHead pTail
*
*
* Note! At no point is pTail == pEnd or pHead == pEnd.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_GetDeviceData(PV pdd, DWORD cbdod, PDOD rgdod,
LPDWORD pdwInOut, DWORD fl _THAT)
{
HRESULT hres;
PDD this;
SOMEDEVICEDATA sdd;
EnterProcR(IDirectInputDevice::GetDeviceData,
(_ "pxpxx", pdd, cbdod, rgdod,
IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl));
/*
* Note that we do not validate the parameters.
* The reason is that GetDeviceData is an inner loop function,
* so it should be as fast as possible.
*
* Note also that it is legal to get device data after the device
* has been unacquired. This lets you "turn on the faucet" for
* a short period of time, and then parse the data out later.
*/
#ifdef XDEBUG
hresPvT(pdd);
if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) {
RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3);
}
if ( rgdod ) {
hresFullValidWritePvCb(rgdod, cbCxX(*pdwInOut, DOD), 2);
}
#endif
this = _thisPv(pdd);
/*
* Must protect with the critical section to prevent somebody from
* acquiring/unacquiring or changing the data format or calling
* another GetDeviceData while we're reading. (We must be serialized.)
*/
CDIDev_EnterCrit(this);
AssertF(CDIDev_IsConsistent(this));
if ( SUCCEEDED(hres = hresFullValidFl(fl, DIGDD_VALID, 4)) ) {
if ( cbdod == cbX(DOD) ) {
if ( this->celtBuf ) {
/*
* Don't try to read more than there possibly could be.
* This avoids overflow conditions in case celtIn is
* some absurdly huge number.
*/
sdd.celtIn = *pdwInOut;
sdd.celtOut = 0;
if ( sdd.celtIn > this->celtBuf ) {
sdd.celtIn = this->celtBuf;
}
sdd.rgdod = rgdod;
/*
* For this version of DirectInput, we do not allow
* callbacks to implement their own GetDeviceData.
*/
if ( this->pvi ) {
/*
* Reacquire is not allowed until after Win98 SE, see OSR Bug # 89958.
*/
#if (DIRECTINPUT_VERSION > 0x061A)
if ( this->diHacks.fReacquire &&
!this->fAcquired && (this->fOnceAcquired || this->fOnceForcedUnacquired) )
{
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
// 7/18/2000(a-JiTay): IA64: Use %p format specifier for 32/64-bit pointers.
RPF(" DirectInput: Auto acquired device (0x%p)", pdd);
}
}
#ifdef WINNT
if( this->fUnacquiredWhenIconic && !IsIconic(this->hwnd) ) {
if ( SUCCEEDED( CDIDev_Acquire(pdd THAT_) ) ) {
this->fUnacquiredWhenIconic = 0;
RPF(" DirectInput: Auto acquired device (0x%p) after being iconic. ", pdd);
}
}
#endif
#endif
if ( (this->fAcquired && (this->pvi->fl & VIFL_ACQUIRED)) ||
(fl & DIGDD_RESIDUAL) ) {
LPDIDEVICEOBJECTDATA pdod, pdodHead;
DWORD celt;
/*
* Snapshot the value of pdodHead, because it can
* change asynchronously. The other fields won't
* change unless we ask for them to be changed.
*/
pdodHead = this->pvi->pHead;
/*
* Throughout, pdod points to the first unprocessed
* element.
*/
pdod = this->pvi->pTail;
/*
* If we are wrapped, handle the initial run.
*/
if ( pdodHead < this->pvi->pTail ) {
celt = (DWORD)(this->pvi->pEnd - this->pvi->pTail);
AssertF(celt);
pdod = CDIDev_GetSomeDeviceData(this, pdod, celt, &sdd);
}
/*
* Now handle the glob from pdod to pdodHead.
* Remember, pvi->pdodHead may have changed
* behind our back; use the cached value to
* ensure consistency. (If we miss data,
* it'll show up later.)
*/
AssertF(fLimpFF(sdd.celtIn, pdodHead >= pdod));
celt = (DWORD)(pdodHead - pdod);
if ( celt ) {
pdod = CDIDev_GetSomeDeviceData(this, pdod, celt, &sdd);
}
*pdwInOut = sdd.celtOut;
if ( !(fl & DIGDD_PEEK) ) {
this->pvi->pTail = pdod;
}
#if DIRECTINPUT_VERSION > 0x0300
if ( rgdod && sdd.celtOut && this->fCook ) {
CDIDev_CookDeviceData(this, sdd.celtOut, rgdod);
}
#endif
CAssertF(S_OK == 0);
CAssertF(DI_BUFFEROVERFLOW == 1);
hres = (HRESULT)(UINT_PTR)pvExchangePpvPv(&this->pvi->fOverflow, 0);
#ifdef DEBUG_STICKY
if( hres == 1 )
{
OutputDebugString( TEXT( "Device buffer overflowed\r\n" ) );
}
if( sdd.celtOut )
{
PDOD pdoddbg;
TCHAR tszDbg[80];
wsprintf( tszDbg, TEXT("GotData %d elements: "), sdd.celtOut );
OutputDebugString( tszDbg );
for( pdoddbg=rgdod; pdoddbg<&rgdod[sdd.celtOut]; pdoddbg++ )
{
wsprintf( tszDbg, TEXT("0x%02x:x0x%08x "), pdoddbg->dwOfs, pdoddbg->dwData );
OutputDebugString( tszDbg );
}
OutputDebugString( TEXT("\r\n") );
}
#endif /* DEBUG_STICKY */
} else if (this->fAcquired && !(this->pvi->fl & VIFL_ACQUIRED)) {
RPF("ERROR %s - %s", s_szProc, "input lost");
hres = DIERR_INPUTLOST;
CDIDev_InternalUnacquire(this);
} else {
RPF("ERROR %s: %s", s_szProc,
this->hresNotAcquired == DIERR_NOTACQUIRED
? "Not acquired" : "Input lost");
hres = this->hresNotAcquired;
}
} else { /* Don't support device-side GetData yet */
hres = E_NOTIMPL;
}
} else { /* Device is not buffered */
#ifdef XDEBUG
if ( !this->fNotifiedNotBuffered ) {
this->fNotifiedNotBuffered = 1;
RPF("ERROR %s: arg %d: device is not buffered", s_szProc, 0);
}
#endif
#if DIRECTINPUT_VERSION > 0x0300
hres = DIERR_NOTBUFFERED;
#else
hres = E_INVALIDARG;
#endif
}
} else {
RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
}
}
CDIDev_LeaveCrit(this);
return hres;
}
#ifdef IDirectInputDevice2Vtbl
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputDevice2 | Poll |
*
* Retrieves data from polled objects on a DirectInput device.
* If the device does not require polling, then calling this
* method has no effect. If a device that requires polling
* is not polled periodically, no new data will be received
* from the device.
*
* Before a device data can be polled, the data format must
* be set via <mf IDirectInputDevice::SetDataFormat>, and
* the device must be acquired via
* <mf IDirectInputDevice::Acquire>.
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DI_NOEFFECT> = <c S_FALSE>: The device does not require
* polling.
*
* <c DIERR_INPUTLOST>: Access to the device has been
* interrupted. The application should re-acquire the
* device.
*
* <c DIERR_NOTACQUIRED>: The device is not acquired.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_Poll(PV pdd _THAT)
{
HRESULT hres;
PDD this;
EnterProcR(IDirectInputDevice::Poll, (_ "p", pdd));
/*
* Note that we do not validate the parameters.
* The reason is that Poll is an inner loop function,
* so it should be as fast as possible.
*/
#ifdef XDEBUG
hresPvT(pdd);
#endif
this = _thisPv(pdd);
/*
* Fast out: If the device doesn't require polling,
* then don't bother with the critical section or other validation.
*/
if ( this->fPolledDataFormat ) {
/*
* Must protect with the critical section to prevent somebody from
* unacquiring while we're polling.
*/
CDIDev_EnterCrit(this);
if ( this->fAcquired ) {
hres = this->pdcb->lpVtbl->Poll(this->pdcb);
} else {
hres = this->hresNotAcquired;
}
CDIDev_LeaveCrit(this);
} else {
if ( this->fAcquired ) {
if( this->dwVersion < 0x05B2 ) {
hres = S_OK;
} else {
hres = S_FALSE;
}
} else {
hres = this->hresNotAcquired;
}
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* @doc EXTERNAL
*
* @method HRESULT | IDirectInputDevice2 | SendDeviceData |
*
* Sends data to the device.
*
* Before device data can be sent to a device,
* the device must be acquired via
* <mf IDirectInputDevice::Acquire>.
*
* Note that no guarantees
* are made on the order in which the individual data
* elements are sent. However, data sent by
* successive calls to
* <mf IDirectInputDevice2::SendDeviceData>
* will not be interleaved.
* Furthermore, if multiple pieces of
* data are sent to the same object, it is unspecified
* which actual piece of data is sent.
*
* Consider, for example, a device which can be sent
* data in packets, each packet describing two pieces
* of information, call them A and B. Suppose the
* application attempts to send three data elements,
* "B = 2", "A = 1", and "B = 0".
*
* The actual device will be sent a single packet.
* The "A" field of the packet will contain the value 1,
* and the "B" field of the packet will be either 2 or 0.
*
* If the application wishes the data to be sent to the
* device exactly as specified, then three calls to
* <mf IDirectInputDevice2::SendDeviceData> should be
* performed, each call sending one data element.
*
* In response to the first call,
* the device will be sent a packet where the "A" field
* is blank and the "B" field contains the value 2.
*
* In response to the second call,
* the device will be sent a packet where the "A" field
* contains the value 1, and the "B" field is blank.
*
* Finally, in response to the third call,
* the device will be sent a packet where the "A" field
* is blank and the "B" field contains the value 0.
*
*
*
* @cwrap LPDIRECTINPUTDEVICE | lpDirectInputDevice
*
* @parm DWORD | cbObjectData |
*
* The size of a single <t DIDEVICEOBJECTDATA> structure in bytes.
*
* @parm IN LPCDIDEVICEOBJECTDATA | rgdod |
*
* Array of <t DIDEVICEOBJECTDATA> structures containing
* the data to send to the device. It must consist of
* *<p pdwInOut> elements.
*
* <y Note>: The <e DIDEVICEOBJECTDATA.dwOfs> field of
* the <t DIDEVICEOBJECTDATA> structure must contain the
* device object identifier (as obtained from the
* <e DIDEVICEOBJECTINSTANCE.dwType> field of the
* <t DIDEVICEOBJECTINSTANCE> sturcture) for the device
* object at which the data is directed.
*
* Furthermore, the <e DIDEVICEOBJECTDATA.dwTimeStamp>
* and <e DIDEVICEOBJECTDATA.dwSequence> fields are
* reserved for future use and must be zero.
*
* @parm INOUT LPDWORD | pdwInOut |
*
* On entry, contains the number of elements in the array
* pointed to by <p rgdod>. On exit, contains the number
* of elements actually sent to the device.
*
* @parm DWORD | fl |
*
* Flags which control the manner in which data is sent.
* It may consist of zero or more of the following flags:
*
* <c DISDD_CONTINUE>: If this flag is set, then
* the device data sent will be overlaid upon the previously
* sent device data. Otherwise, the device data sent
* will start from scratch.
*
* For example, suppose a device supports two button outputs,
* call them A and B.
* If an application first calls
* <mf IDirectInputDevice2::SendDeviceData> passing
* "button A pressed", then
* a packet of the form "A pressed, B not pressed" will be
* sent to the device.
* If an application then calls
* <mf IDirectInputDevice2::SendDeviceData> passing
* "button B pressed" and the <c DISDD_CONTINUE> flag, then
* a packet of the form "A pressed, B pressed" will be
* sent to the device.
* However, if the application had not passed the
* <c DISDD_CONTINUE> flag, then the packet sent to the device
* would have been "A not pressed, B pressed".
*
* @returns
*
* Returns a COM error code. The following error codes are
* intended to be illustrative and not necessarily comprehensive.
*
* <c DI_OK> = <c S_OK>: The operation completed successfully.
*
* <c DIERR_INPUTLOST>: Access to the device has been
* interrupted. The application should re-acquire the
* device.
*
* <c DIERR_NOTACQUIRED>: The device is not acquired.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_SendDeviceData(PV pdd, DWORD cbdod, PCDOD rgdod,
LPDWORD pdwInOut, DWORD fl _THAT)
{
HRESULT hres;
PDD this;
EnterProcR(IDirectInputDevice::SendDeviceData,
(_ "pxpxx", pdd, cbdod, rgdod,
IsBadReadPtr(pdwInOut, cbX(DWORD)) ? 0 : *pdwInOut, fl));
/*
* Note that we do not validate the parameters.
* The reason is that SendDeviceData is an inner loop function,
* so it should be as fast as possible.
*/
#ifdef XDEBUG
hresPvT(pdd);
if ( IsBadWritePtr(pdwInOut, cbX(*pdwInOut)) ) {
RPF("ERROR %s: arg %d: invalid value; crash soon", s_szProc, 3);
}
hresFullValidReadPvCb(rgdod, cbCxX(*pdwInOut, DOD), 2);
#endif
this = _thisPv(pdd);
/*
* Must protect with the critical section to prevent somebody from
* unacquiring while we're sending data.
*/
CDIDev_EnterCrit(this);
if ( SUCCEEDED(hres = hresFullValidFl(fl, DISDD_VALID, 4)) ) {
if ( cbdod == cbX(DOD) ) {
#ifdef XDEBUG
UINT iod;
for ( iod = 0; iod < *pdwInOut; iod++ ) {
if ( rgdod[iod].dwTimeStamp ) {
RPF("%s: ERROR: dwTimeStamp must be zero", s_szProc);
}
if ( rgdod[iod].dwSequence ) {
RPF("%s: ERROR: dwSequence must be zero", s_szProc);
}
}
#endif
if ( this->fAcquired ) {
UINT iod;
for ( iod=0; iod < *pdwInOut; iod++ ) {
int iobj = CDIDev_OffsetToIobj(this, rgdod[iod].dwOfs);
LPDWORD pdw = (LPDWORD)&rgdod[iod].dwOfs;
*pdw = this->df.rgodf[iobj].dwType;
}
hres = this->pdcb->lpVtbl->SendDeviceData(this->pdcb,
rgdod, pdwInOut, fl);
} else {
hres = this->hresNotAcquired;
}
} else {
RPF("ERROR %s: arg %d: invalid value", s_szProc, 1);
}
}
CDIDev_LeaveCrit(this);
ExitOleProc();
return hres;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @func BOOL | CDIDev_CheckId |
*
* Verify that the item has the appropriate type.
*
* @parm DWORD | dwId |
*
* ID to locate.
*
* @parm UINT | fl |
*
* Bitmask of flags for things to validate.
*
* The <c DEVCO_TYPEMASK> fields describe what type
* of object we should be locating.
*
* The <c DEVCO_ATTRMASK> fields describe the attribute
* bits that are required.
*
*****************************************************************************/
BOOL INLINE
CDIDev_CheckId(DWORD dwId, DWORD fl)
{
CAssertF(DIDFT_ATTRMASK == DEVCO_ATTRMASK);
return(dwId & fl & DEVCO_TYPEMASK) &&
fHasAllBitsFlFl(dwId, fl & DIDFT_ATTRMASK);
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method int | CDIDev | IdToIobj |
*
* Locate an item which matches the specified ID.
*
* @cwrap PDD | this
*
* @parm DWORD | dwId |
*
* ID to locate.
*
* @returns
*
* Returns the index of the object found, or -1 on error.
*
*****************************************************************************/
int INTERNAL
CDIDev_IdToIobj(PDD this, DWORD dwId)
{
int iobj;
/* Someday: Perf: Should have xlat table */
for ( iobj = this->df.dwNumObjs; --iobj >= 0; ) {
PODF podf = &this->df.rgodf[iobj];
if ( DIDFT_FINDMATCH(podf->dwType, dwId) ) {
goto done;
}
}
iobj = -1;
done:;
return iobj;
}
#if 0
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIDev | IdToId |
*
* Convert a single <t DWORD> from an ID to an ID.
*
* This is clearly a very simple operation.
*
* It's all validation.
*
* @cwrap PDD | this
*
* @parm LPDWORD | pdw |
*
* Single item to convert.
*
* @parm UINT | fl |
*
* Bitmask of flags that govern the conversion.
* The function should look only at
* <c DEVCO_AXIS> or <c DEVCO_BUTTON>.
*
*****************************************************************************/
HRESULT INTERNAL
CDIDev_IdToId(PDD this, LPDWORD pdw, UINT fl)
{
HRESULT hres;
int iobj;
iobj = CDIDev_FindId(this, *pdw, fl);
if ( iobj >= 0 ) {
*pdw = this->df.rgodf[iobj].dwType;
hres = S_OK;
} else {
hres = E_INVALIDARG;
}
return hres;
}
#endif
/*****************************************************************************
*
* @doc INTERNAL
*
* @method int | CDIDev | OffsetToIobj |
*
* Convert a single <t DWORD> from an offset to an object index.
*
* @cwrap PDD | this
*
* @parm DWORD | dwOfs |
*
* Offset to convert.
*
*****************************************************************************/
int INTERNAL
CDIDev_OffsetToIobj(PDD this, DWORD dwOfs)
{
int iobj;
AssertF(this->pdix);
AssertF(this->rgiobj);
if ( dwOfs < this->dwDataSize ) {
iobj = this->rgiobj[dwOfs];
if ( iobj >= 0 ) {
AssertF(this->pdix[iobj].dwOfs == dwOfs);
} else {
AssertF(iobj == -1);
}
} else {
iobj = -1;
}
return iobj;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method int | CDIDev | IobjToId |
*
* Convert an object index to an ID.
*
* @cwrap PDD | this
*
* @parm int | iobj |
*
* Single item to convert.
*
*****************************************************************************/
DWORD INLINE
CDIDev_IobjToId(PDD this, int iobj)
{
AssertF((DWORD)iobj < this->df.dwNumObjs);
return this->df.rgodf[iobj].dwType;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method int | CDIDev | IobjToOffset |
*
* Convert an object index to an offset.
*
* @cwrap PDD | this
*
* @parm int | iobj |
*
* Single item to convert.
*
*****************************************************************************/
DWORD INLINE
CDIDev_IobjToOffset(PDD this, int iobj)
{
AssertF((DWORD)iobj < this->df.dwNumObjs);
AssertF(this->pdix);
return this->pdix[iobj].dwOfs;
}
/*****************************************************************************
*
* @doc INTERNAL
*
* @method HRESULT | CDIDev | ConvertObjects |
*
* Convert between the ways of talking about device gizmos.
*
* Since this is used only by the force feedback subsystem,
* we also barf if the device found does not support
* force feedback.
*
* @cwrap PDD | this
*
* @parm UINT | cdw |
*
* Number of elements to convert (in-place).
*
* @parm LPDWORD | rgdw |
*
* Array of elements to convert.
*
* @parm UINT | fl |
*
* Flags that describe how to do the conversion.
*
* <c DEVCO_AXIS> or <c DEVCO_BUTTON> indicate whether
* the item being converted is an axis or button.
*
* <c DEVCO_FROM*> specifies what the existing value is.
*
* <c DEVCO_TO*> specifies what the new values should be.
*
*****************************************************************************/
STDMETHODIMP
CDIDev_ConvertObjects(PDD this, UINT cdw, LPDWORD rgdw, UINT fl)
{
HRESULT hres;
/*
* Don't let somebody change the data format while we're
* looking at it.
*/
CDIDev_EnterCrit(this);
AssertF((fl & ~DEVCO_VALID) == 0);
if ( fLimpFF(fl & (DEVCO_FROMOFFSET | DEVCO_TOOFFSET),
this->pdix && this->rgiobj) ) {
UINT idw;
for ( idw = 0; idw < cdw; idw++ ) {
/*
* Convert from its source to an object index,
* validate the object index, then convert to
* the target.
*/
int iobj;
switch ( fl & DEVCO_FROMMASK ) {
default:
AssertF(0); /* Huh? */
case DEVCO_FROMID:
iobj = CDIDev_IdToIobj(this, rgdw[idw]);
break;
case DEVCO_FROMOFFSET:
iobj = CDIDev_OffsetToIobj(this, rgdw[idw]);
break;
}
if ( iobj < 0 ) {
hres = E_INVALIDARG; /* Invalid object */
goto done;
}
AssertF((DWORD)iobj < this->df.dwNumObjs);
if ( !CDIDev_CheckId(this->df.rgodf[iobj].dwType, fl) ) {
hres = E_INVALIDARG; /* Bad attributes */
goto done;
}
switch ( fl & DEVCO_TOMASK ) {
default:
AssertF(0); /* Huh? */
case DEVCO_TOID:
rgdw[idw] = CDIDev_IobjToId(this, iobj);
break;
case DEVCO_TOOFFSET:
rgdw[idw] = CDIDev_IobjToOffset(this, iobj);
break;
}
}
hres = S_OK;
done:;
} else {
RPF("ERROR: Must have a data format to use offsets");
hres = E_INVALIDARG;
}
CDIDev_LeaveCrit(this);
return hres;
}