|
|
/*****************************************************************************
* PidHid.c * * Copyright (c) 1999 Microsoft Corporation. All Rights Reserved. * * Abstract: * * HID utility routines for PID . * *****************************************************************************/ #include "PidPr.h"
#define sqfl ( sqflHid )
/*****************************************************************************
* * PID_GetReportId * * Obtain the HID report ID given the usage, usagePage and LinkCollection * * IDirectInputEffectDriver | ped | * * The effect driver interface * * PPIDREPORT | pPidReport | * * Address of PIDREPORT structure * * USHORT | uLinkCollection | * * LinkCollection ID * * OUT UCHAR * | pReportId | * * Report ID. Undefined if unsuccessful * * Returns: * * HRESULT * Error code * *****************************************************************************/ STDMETHODIMP PID_GetReportId ( IDirectInputEffectDriver *ped, PPIDREPORT pPidReport, USHORT uLinkCollection, UCHAR* pReportId ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres = S_OK;
EnterProcI(PID_GetReportId, (_"xxxx", ped, pPidReport, pReportId));
if( SUCCEEDED(hres) ) { HIDP_VALUE_CAPS ValCaps; USHORT cAValCaps = 0x1; USAGE Usage = DIGETUSAGE(pPidReport->rgPidUsage->dwUsage); USAGE UsagePage = DIGETUSAGEPAGE(pPidReport->rgPidUsage->dwUsage);
hres = HidP_GetSpecificValueCaps ( pPidReport->HidP_Type, UsagePage, uLinkCollection, Usage, &ValCaps, &cAValCaps, this->ppd );
// If the report has no values, only buttons
if(hres == HIDP_STATUS_USAGE_NOT_FOUND ) { // Guarded Laziness
CAssertF(cbX(HIDP_VALUE_CAPS) == cbX(HIDP_BUTTON_CAPS) ); CAssertF(FIELD_OFFSET(HIDP_VALUE_CAPS, ReportID) == FIELD_OFFSET(HIDP_BUTTON_CAPS, ReportID) );
hres = HidP_GetSpecificButtonCaps ( pPidReport->HidP_Type, UsagePage, uLinkCollection, 0x0, (PHIDP_BUTTON_CAPS)&ValCaps, &cAValCaps, this->ppd ); }
if( SUCCEEDED(hres ) || ( hres == HIDP_STATUS_BUFFER_TOO_SMALL) ) { (*pReportId) = ValCaps.ReportID; hres = S_OK; } else { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL HidP_GetValCaps for CollectionId%d (%x %x:%s) "), s_tszProc, uLinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); } }
ExitOleProc();
return hres; } /*****************************************************************************
* * PID_GetCollectionIndex * * Obtain the collection index for collection usage page & usage. * * * The external joystick number being addressed. * * dwEffect * * The effect to be queried. * * pdwStatus * * Receives the effect status in the form of zero * or more DIEGES_* flags. * * Returns: * Collection Index ( 0 .. NumberLinkCollectionNodes -1 ) on success * 0x0 on failure * *****************************************************************************/ STDMETHODIMP PID_GetLinkCollectionIndex ( IDirectInputEffectDriver *ped, USAGE UsagePage, USAGE Collection, USHORT Parent, PUSHORT puLinkCollection ) { CPidDrv *this = (CPidDrv *)ped; USHORT indx; HRESULT hres; PHIDP_LINK_COLLECTION_NODE pLinkCollection;
EnterProcI(PID_GetLinkCollectionIndex, (_"xxxxx", this, UsagePage, Collection, Parent, puLinkCollection)); hres = DIERR_PID_USAGENOTFOUND;
*puLinkCollection = 0x0; for(indx = 0x0, pLinkCollection = this->pLinkCollection; indx < this->caps.NumberLinkCollectionNodes; indx++, pLinkCollection++ ) {
if( pLinkCollection->LinkUsagePage == UsagePage && pLinkCollection->LinkUsage == Collection ) { if( Parent && Parent != pLinkCollection->Parent ) { continue; } *puLinkCollection = indx; hres = S_OK; break; } }
if( FAILED(hres) ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: FAIL No LinkCollection for (%x %x:%s) "), s_tszProc, UsagePage, Collection, PIDUSAGETXT(UsagePage,Collection) ); }else { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: LinkCollection for (%x %x:%s)=%x "), s_tszProc, UsagePage, Collection, PIDUSAGETXT(UsagePage,Collection), *puLinkCollection ); }
ExitBenignOleProc(); return (hres) ; }
//Helper fn to tell us when we're dealing w/ an "absolute" usage, since they require special handling
BOOL PID_IsUsageAbsoluteLike ( IDirectInputEffectDriver *ped, USHORT Usage ) { //"Absolute-like" usages need special handling,
//since we can't simply translate data into logical units by scaling
//but need to calculate exponent, etc.
//and then use special procedure to set the values
//"Absolute" usages are all time usages as well as trigger button usages
if ((Usage == HID_USAGE_PID_DURATION) || (Usage ==HID_USAGE_PID_SAMPLE_PERIOD ) || (Usage == HID_USAGE_PID_TRIGGER_REPEAT_INTERVAL) || (Usage == HID_USAGE_PID_START_DELAY) || (Usage == HID_USAGE_PID_ATTACK_TIME ) ||(Usage == HID_USAGE_PID_FADE_TIME) || (Usage == HID_USAGE_PID_PERIOD) || (Usage == HID_USAGE_PID_TRIGGER_BUTTON)) { return TRUE; }
return FALSE; }
//Helper fn to tell us when we're dealing w/ a magnitude that can be both positive and negative,
//since we have to scale those differently
BOOL PID_IsUsagePositiveNegative ( IDirectInputEffectDriver *ped, USHORT Usage, USHORT LinkCollection ) { BOOL isPosNeg = FALSE; //All the usages corresponding to the structures given by LONGs can be positive or negative.
//Exception is the direction / angle, which should already be scaled into the range 0 - 360 * DI_DEGREES,
//so should be treated as only positive.
//Another exception is DICONDITION.lDeadband, which is defined as a LONG, but our headers
//say it can only be in the range 0 to DI_FFNOMINALMAX.
if ((Usage == HID_USAGE_PID_CP_OFFSET) || (Usage == HID_USAGE_PID_POSITIVE_COEFFICIENT) || (Usage == HID_USAGE_PID_NEGATIVE_COEFFICIENT) || (Usage == HID_USAGE_PID_RAMP_START) ||(Usage == HID_USAGE_PID_RAMP_END) || (Usage == HID_USAGE_PID_OFFSET)) { isPosNeg = TRUE; }
//Magnitude of the constant force and the magnitude of the periodic force are defined to be the same thing,
//but only the constant force magnitude can be both positive and negative.
//To distinguish them, need to look at the collection.
//Get constant force's collection and compare.
if (Usage == HID_USAGE_PID_MAGNITUDE) { USHORT ConstCollection = 0x0; PID_GetLinkCollectionIndex(ped, g_Constant.UsagePage, g_Constant.Collection, 0x0, &ConstCollection); if (LinkCollection == ConstCollection) { isPosNeg = TRUE; } }
return isPosNeg; }
STDMETHODIMP PID_PackValue ( IDirectInputEffectDriver *ped, PPIDREPORT pPidReport, USHORT LinkCollection, PVOID pvData, UINT cbData, PCHAR pReport, ULONG cbReport ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres; PPIDUSAGE pPidUsage; UINT indx;
EnterProcI( PID_PackValue, (_"xxxxxxx", ped, pPidReport, LinkCollection, pvData, cbData, pReport, cbReport));
hres = S_OK; // Loop over all data values in the PID Report
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage; indx < pPidReport->cAPidUsage; indx++, pPidUsage++ ) { // Make sure the offsets are valid
if( pPidUsage->DataOffset < cbData ) { LONG lValue; NTSTATUS ntStat; USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage); USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
lValue = *((LONG*)((UCHAR*)pvData+pPidUsage->DataOffset));
ntStat = HidP_SetScaledUsageValue ( pPidReport->HidP_Type, UsagePage, LinkCollection, Usage, lValue, this->ppd, pReport, cbReport );
if( FAILED(ntStat) ) { // HidP_SetScaledUsageValue FAILED
SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%s: FAIL HidP_SetScaledUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), lValue );
// Try to set the unscaled value to get something that might make sense
if( ntStat != HIDP_STATUS_USAGE_NOT_FOUND ) { lValue = -1; // The range could be messed up.
ntStat = HidP_SetUsageValue ( pPidReport->HidP_Type, UsagePage, LinkCollection, Usage, lValue, this->ppd, pReport, cbReport ); if(FAILED(ntStat) ) { SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%s: FAIL HidP_SetUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), lValue ); } } } else { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: HidP_SetScaledUsageValue:0x%x for(%x,%x,%x:%s)=0x%x "), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), lValue ); } } else { //SquirtSqflPtszV(sqfl | sqflBenign,
// TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
// s_tszProc, pPidUsage->DataOffset, cbData );
} } ExitOleProc(); return hres; }
//blocking version -- used for creating a new effect or destroying an effect, and for custom forces
STDMETHODIMP PID_SendReportBl ( IDirectInputEffectDriver *ped, PUCHAR pReport, UINT cbReport, HIDP_REPORT_TYPE HidP_Type ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres;
EnterProcI( PID_SendReportBl, (_"xxxx", ped, pReport, cbReport, HidP_Type));
hres = S_OK; if( HidP_Type == HidP_Output ) { BOOL frc; UINT cbWritten;
frc = WriteFile (this->hdev, pReport, cbReport, &cbWritten, NULL);
if( frc != TRUE || cbWritten != cbReport ) { LONG lrc = GetLastError(); hres = hresLe(lrc); SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL WriteFile():%d (cbWritten(0x%x) cbReport(0x%x) Le(0x%x)"), s_tszProc, frc, cbWritten, cbReport, lrc );
} } else if( HidP_Type == HidP_Feature ) { hres = HidD_SetFeature (this->hdev, pReport, cbReport ); if(FAILED(hres) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL SendD_Feature() hres:0x%x"), s_tszProc, hres );
}
} else { hres = DIERR_PID_USAGENOTFOUND; } ExitOleProc(); return hres; }
STDMETHODIMP PID_SendReport ( IDirectInputEffectDriver *ped, PUCHAR pReport, UINT cbReport, HIDP_REPORT_TYPE HidP_Type, BOOL bBlocking, UINT blockNr, UINT totalBlocks ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres = S_OK;
EnterProcI( PID_SendReport, (_"xxxx", ped, pReport, cbReport, HidP_Type));
if (bBlocking == TRUE) { hres = PID_SendReportBl(ped, pReport, cbReport, HidP_Type); } else { AssertF(this->hThread != 0x0); AssertF(this->hWrite != 0x0); AssertF(this->hWriteComplete != 0x0);
//blockNr is 0-based.
AssertF(totalBlocks > 0); AssertF(blockNr < totalBlocks);
if( HidP_Type == HidP_Output ) { //WaitForMultipleObjects() till the completion event becomes set.
//we save each report into the appropriate place in the array.
//when we get all the reports, we set the event to signal to the other thread to write.
// Windows bug 627797 -- do not use INFINITE wait, so that we don't hang the app
//if smth goes wrong w/ the previous write, but instead use the blocking version.
DWORD dwWait = WaitForMultipleObjects(1, &this->hWriteComplete, FALSE, 1000); if (dwWait == WAIT_OBJECT_0) { AssertF(this->dwWriteAttempt == 0); //save the report data
ZeroMemory(this->pWriteReport[blockNr], this->cbWriteReport[blockNr]); memcpy(this->pWriteReport[blockNr], pReport, cbReport); this->cbWriteReport[blockNr] = (USHORT)cbReport; if (blockNr == totalBlocks-1) { this->totalBlocks = totalBlocks; this->blockNr = 0; ResetEvent(this->hWriteComplete); SetEvent(this->hWrite); } } else { //The wait interval has expired, or an error has occured
RPF( TEXT("Waiting for the write completion event ended without the event being signaled, dwWait = %u"), dwWait); //call the blocking version
hres = PID_SendReportBl(ped, pReport, cbReport, HidP_Type); }
} else if( HidP_Type == HidP_Feature ) { hres = HidD_SetFeature (this->hdev, pReport, cbReport ); if(FAILED(hres) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL SendD_Feature() hres:0x%x"), s_tszProc, hres );
}
} else { hres = DIERR_PID_USAGENOTFOUND; } }
ExitOleProc(); return hres; }
STDMETHODIMP PID_ParseReport ( IDirectInputEffectDriver *ped, PPIDREPORT pPidReport, USHORT LinkCollection, PVOID pvData, UINT cbData, PCHAR pReport, ULONG cbReport ) { CPidDrv *this = (CPidDrv *)ped; HRESULT hres; PPIDUSAGE pPidUsage; UINT indx; EnterProcI( PID_ParseReport, (_"xxxxxxx", ped, pPidReport, pvData, cbData, pReport, cbReport));
hres = S_OK; // Loop over all data values in the PID Report
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage; indx < pPidReport->cAPidUsage; indx++, pPidUsage++ ) { // Make sure the offsets are valid
if( pPidUsage->DataOffset < cbData ) { LONG lValue; NTSTATUS ntStat; USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage); USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage);
ntStat = HidP_GetScaledUsageValue ( pPidReport->HidP_Type, UsagePage, LinkCollection, Usage, &lValue, this->ppd, pReport, cbReport );
if(SUCCEEDED(ntStat)) { *((LONG*)((UCHAR*)pvData+pPidUsage->DataOffset)) = lValue; } else { hres |= E_NOTIMPL; SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%s: FAIL HidP_GetScaledUsageValue:0x%x for(%x,%x,%x:%s)"), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage) ); } } else {
SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%s: FAIL Invalid Offset(%d), max(%d) "), s_tszProc, pPidUsage->DataOffset, cbData ); } } ExitBenignOleProc(); return hres; }
STDMETHODIMP PID_GetReport ( IDirectInputEffectDriver *ped, PPIDREPORT pPidReport, USHORT LinkCollection, PVOID pReport, UINT cbReport ) { HRESULT hres = S_OK; CPidDrv *this = (CPidDrv *)ped; UCHAR ReportId; EnterProcI( PID_GetReport, (_"xxxxx", ped, pPidReport, LinkCollection, pReport, cbReport));
if( SUCCEEDED(hres) ) { hres = PID_GetReportId(ped, pPidReport, LinkCollection, &ReportId);
if(SUCCEEDED(hres) ) { AssertF(pPidReport->HidP_Type == HidP_Feature);
if(SUCCEEDED(hres) ) { ZeroBuf(pReport, cbReport);
/*
* The Win9x headers do not yet have use HidP_InitializeReportForID * use MAXULONG_PTR to tell the header sets apart so that we can still build */
#ifdef WINNT
/*hres*=*/HidP_InitializeReportForID ( pPidReport->HidP_Type, //ReportType,
ReportId, //ReportID,
this->ppd, //PreparsedData
pReport, //Report
cbReport //ReportLength
); #else
(*(PUCHAR)pReport) = ReportId; hres = S_OK; #endif
if( FAILED(hres) ) { SquirtSqflPtszV(sqfl | sqflError, TEXT("%s: FAIL HidP_InitializeReportForId:0x%x for Type(%d) CollectionId%d ReportID%d "), s_tszProc, hres, pPidReport->HidP_Type, LinkCollection, ReportId ); }
if( SUCCEEDED(hres) && pPidReport->HidP_Type == HidP_Feature ) { BOOL frc; frc = HidD_GetFeature ( this->hdev, // HidDeviceObject,
pReport, // ReportBuffer,
cbReport //ReportBufferLength
);
if( frc != TRUE ) { hres = DIERR_PID_USAGENOTFOUND; } } } } } ExitOleProc(); return(hres); }
/*****************************************************************************
* * PID_ComputeScalingFactors * * Dinput units for various parameters are well defined. The device may choose * to implement the units that it is most comfortable with. This routine * computes scaling factors that are to be used when scaling DINPUT parameters * before they are send to the device. * * IDirectInputEffectDriver | ped | * * The effect driver interface * * PPIDREPORT | pPidReport | * * Address of PIDREPORT structure * * USHORT | uLinkCollection | * * LinkCollection ID * * IN OUT PVOID | pvData | * * Parameter data. On entry value is the nominal scale used by Dinput. * For example: Angles: DI_DEGREES, DI_FFNOMINALMAX, DI_SECONDS * * IN UINT | cbData | * * Number of valid DWORDS in pvData * * Returns: * * HRESULT * Error code * E_NOTIMPL: Did not find any usage / usage Page * DIERR_PID_INVALIDSCALING: Unsupported device scaling parameters. * S_OK: Scaling value for at least one parameter was found * *****************************************************************************/
STDMETHODIMP PID_ComputeScalingFactors ( IDirectInputEffectDriver *ped, PPIDREPORT pPidReport, USHORT LinkCollection, PVOID pvData, UINT cbData, PVOID pvOffset, UINT cbOffset ) { HRESULT hres = E_NOTIMPL; CPidDrv *this = (CPidDrv *)ped; UINT indx; PPIDUSAGE pPidUsage;
EnterProcI( PID_ComputeScalingFactors, (_"xxxxxxx", ped, pPidReport, LinkCollection, pvData, cbData, pvOffset, cbOffset));
// Loop over all data values in the PID Report
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage; indx < pPidReport->cAPidUsage; indx++, pPidUsage++ ) { // Make sure the offsets are valid
if (( pPidUsage->DataOffset < cbData ) && (pPidUsage->DataOffset < cbOffset)) { NTSTATUS ntStat; HIDP_VALUE_CAPS ValCaps; USHORT cAValCaps = 0x1;
USHORT Usage = DIGETUSAGE(pPidUsage->dwUsage); USHORT UsagePage = DIGETUSAGEPAGE(pPidUsage->dwUsage); PDWORD pdwValue; PDWORD pdwOffset; DWORD dwScale = 0x1; DWORD dwOffset = 0x0;
pdwValue = ((DWORD*)((UCHAR*)pvData+pPidUsage->DataOffset)); pdwOffset = ((DWORD*)((UCHAR*)pvOffset+pPidUsage->DataOffset));
ntStat = HidP_GetSpecificValueCaps ( pPidReport->HidP_Type, UsagePage, LinkCollection, Usage, &ValCaps, &cAValCaps, this->ppd );
if(SUCCEEDED(ntStat)) { //some units are "absolute" and thus don't need to be scaled to the limits.
//for them, we just find out the correct units
if (PID_IsUsageAbsoluteLike(ped, Usage)) { if( ! ValCaps.Units ) { SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s:No Units(%x,%x %x:%s) Max:%d Scale:%d "), s_tszProc, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.PhysicalMax, dwScale ); // No units, scaling exponent is default = 1
hres = S_FALSE; } else { LONG UnitExp; UnitExp = (LONG)ValCaps.UnitsExp ;
if( UnitExp > 0x0 ) { RPF(TEXT("Driver does not support Units (%x,%x %x:%s) Exp:%d Max:%d"), LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax ) ; hres = DIERR_PID_INVALIDSCALING; }else { hres = S_OK; }
if(SUCCEEDED(hres) ) { dwScale = (*pdwValue); for(; UnitExp; UnitExp++ ) { dwScale /= 10; }
if( dwScale == 0 ) { RPF(TEXT("Driver does not support Units (%x,%x %x:%s) Exp:%d Max:%d"), LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax ) ; dwScale = 0x1; hres = DIERR_PID_INVALIDSCALING; }else { hres = S_OK; } } SquirtSqflPtszV(sqfl | sqflVerbose, TEXT("%s: (%x,%x %x:%s) Exp%d Max:%d Scale:%d "), s_tszProc, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), ValCaps.UnitsExp, ValCaps.PhysicalMax, (*pdwValue) ); } } else { //for everything else, get Physical and /or Logical Min/ Max
//From PID spec, doesn't have to have a Physical / Logical Min, but does have to have either Physical or Logical Max
if ((!ValCaps.PhysicalMax) && (!ValCaps.LogicalMax)) { RPF(TEXT("Driver does not have either Physical Max or Logical Max for (%x,%x %x:%s)"), LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage)) ; hres = DIERR_PID_INVALIDSCALING; } else { //Compute the scaling value from either Physical or Logical Min/ Max and store it
int Scale = 0; int Min = 0; int Max = 0; if (ValCaps.PhysicalMax) { Max = ValCaps.PhysicalMax; if (ValCaps.PhysicalMin) { Min = ValCaps.PhysicalMin; } } else { Max = ValCaps.LogicalMax; if (ValCaps.LogicalMin) { Min = ValCaps.LogicalMin; } } #ifdef DEBUG
//if Min/max are not in correct order, print a message so that we know if there are any problems w/ the forces
if (Min >= Max) { RPF(TEXT("Maximum of the device's range is %d, not bigger than minimum %d"), Max, Min); } #endif
//certain magnitudes can be both positive and negative -- for those, we need to know the device's offset
if (PID_IsUsagePositiveNegative(ped, Usage, LinkCollection)) { Scale = (Max - Min)/2; dwOffset = (Max + Min)/2;
} //other magnitudes can only be positive
else { Scale = Max - Min; dwOffset = Min; } //for angular usages, multiply by DI_FFNOMINALMAX and divide by 360 * DI_DEGREES
//we are doing this since later we will have no way of knowing that the values represent angles,
//and will thus divide all the values by DI_FFNOMINALMAX
if (*pdwValue == 360 * DI_DEGREES) { dwScale = MulDiv(Scale, DI_FFNOMINALMAX, (360 * DI_DEGREES)); } else { dwScale = Scale; } hres = S_OK; } }
} else { // HidP_SetScaledUsageValue FAILED
SquirtSqflPtszV(sqfl | sqflBenign, TEXT("%s: FAIL HidP_GetSpecificValueCaps:0x%x for(%x,%x,%x:%s)=0x%x "), s_tszProc, ntStat, LinkCollection, UsagePage, Usage, PIDUSAGETXT(UsagePage,Usage), dwScale ); }
(*pdwValue) = dwScale; (*pdwOffset) = dwOffset; } else { //SquirtSqflPtszV(sqfl | sqflVerbose,
// TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
// s_tszProc, pPidUsage->DataOffset, cbData );
} }
ExitOleProc(); return hres; }
/*****************************************************************************
* * PID_ApplyScalingFactors * * Dinput units for various parameters are well defined. The device may choose * to implement the units that it is most comfortable with. This routine * apply scaling factors that are to be used when scaling DINPUT parameters * before they are send to the device. * * IDirectInputEffectDriver | ped | * * The effect driver interface * * PPIDREPORT | pPidReport | * * Address of PIDREPORT structure * * IN PVOID | pvScale | * * Scaling values * * IN UINT | cbScale | * * Number of scaling values. * * IN OUT PVOID | pvData | * * Array of data values. * * IN UINT | cbData | * * Number of data values. * * Returns: * * HRESULT * Error code * E_NOTIMPL: Did not find any usage / usage Page * DIERR_PID_INVALIDSCALING: Unsupported device scaling parameters. * S_OK: Scaling value for at least one parameter was found * *****************************************************************************/
STDMETHODIMP PID_ApplyScalingFactors ( IDirectInputEffectDriver *ped, PPIDREPORT pPidReport, PVOID pvScale, UINT cbScale, PVOID pvOffset, UINT cbOffset, PVOID pvData, UINT cbData ) { HRESULT hres = S_OK; CPidDrv *this = (CPidDrv *)ped; UINT indx; PPIDUSAGE pPidUsage; EnterProcI( PID_ApplyScalingFactors, (_"xxxxxxxx", ped, pPidReport, pvScale, cbScale, pvOffset, cbOffset, pvData, cbData)); // Loop over all data values in the PID Report
for(indx = 0x0, pPidUsage = pPidReport->rgPidUsage; indx < pPidReport->cAPidUsage; indx++, pPidUsage++ ) { // Make sure we the offsets are valid
if( (pPidUsage->DataOffset < cbData) && (pPidUsage->DataOffset < cbScale) && ((pPidUsage->DataOffset < cbOffset) )) { PUINT pValue; PUINT pScale; PUINT pOffset;
pValue = ((PUINT)((UCHAR*)pvData +pPidUsage->DataOffset)); pScale = ((PUINT)((UCHAR*)pvScale +pPidUsage->DataOffset)); pOffset = ((PUINT)((UCHAR*)pvOffset +pPidUsage->DataOffset));
//"absolute"-like usages need special handling, because they don't need to be scaled to the max device values
if (PID_IsUsageAbsoluteLike(ped, DIGETUSAGE(pPidUsage->dwUsage))) { if( (*pScale) > 0x1 ) { (*pValue) /= (*pScale) ; } } //for everything else, do a calculation based on Logical or Physical Min/ Max
else { (int)(*pValue) = MulDiv((*pScale), (*pValue), DI_FFNOMINALMAX) + (*pOffset); } } else { //SquirtSqflPtszV(sqfl | sqflBenign,
// TEXT("%s: FAIL Invalid Offset(%d), max(%d) "),
// s_tszProc, pPidUsage->DataOffset, cbData );
} }
ExitOleProc(); return hres; }
|