windows-nt/Source/XPSP1/NT/multimedia/directx/dinput/pid/pideff.c

1295 lines
41 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*****************************************************************************
*
* PidEff.c
* Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
*
* Abstract:
*
* Download PID Effect Block.
*
*****************************************************************************/
#include "pidpr.h"
#define sqfl ( sqflEff )
#pragma BEGIN_CONST_DATA
/*
* The structure c_rgUsgEffects, aids in translating elements in the DIEFFECT
* structure to PID usages
*/
static PIDUSAGE c_rgUsgEffect[] =
{
MAKE_PIDUSAGE(DURATION, FIELD_OFFSET(DIEFFECT,dwDuration) ),
MAKE_PIDUSAGE(SAMPLE_PERIOD, FIELD_OFFSET(DIEFFECT,dwSamplePeriod) ),
MAKE_PIDUSAGE(GAIN, FIELD_OFFSET(DIEFFECT,dwGain) ),
MAKE_PIDUSAGE(TRIGGER_BUTTON, FIELD_OFFSET(DIEFFECT,dwTriggerButton) ),
MAKE_PIDUSAGE(TRIGGER_REPEAT_INTERVAL,FIELD_OFFSET(DIEFFECT,dwTriggerRepeatInterval) ),
#if DIRECTINPUT_VERSION >= 0x600
MAKE_PIDUSAGE(START_DELAY ,FIELD_OFFSET(DIEFFECT,dwStartDelay)),
#endif
};
/*
* g_Effect provides context to the c_rgUsgEffect struct
*/
PIDREPORT g_Effect =
{
HidP_Output, // Effect Blocks can only be output reports
HID_USAGE_PAGE_PID, // Usage Page
HID_USAGE_PID_SET_EFFECT_REPORT, // Collection
cbX(DIEFFECT), // Size of incoming data
cA(c_rgUsgEffect), // number of elements in c_rgUsgEffect
c_rgUsgEffect // how elements of DIEFFECT are translated to PID
};
/*
* Effect block index to PID usage
*/
static PIDUSAGE c_rgUsgBlockIndex[] =
{
MAKE_PIDUSAGE(EFFECT_BLOCK_INDEX, 0x0 ),
};
/*
* For some PID transactions block index is output report
*/
PIDREPORT g_BlockIndex =
{
HidP_Output, // Report Type
HID_USAGE_PAGE_PID, // Usage Page
0x0, // Any collection
cbX(DWORD), // size of incoming data
cA(c_rgUsgBlockIndex), // translation table for effect block index to PID usages
c_rgUsgBlockIndex
};
/*
* In the PID state report, block index is an input report
*/
PIDREPORT g_BlockIndexIN =
{
HidP_Input,
HID_USAGE_PAGE_PID,
HID_USAGE_PID_STATE_REPORT,
cbX(DWORD),
cA(c_rgUsgBlockIndex),
c_rgUsgBlockIndex
};
//CAssertF(MAX_ORDINALS == cA(c_rgUsgOrdinals));
PIDREPORT g_TypeSpBlockOffset =
{
HidP_Output, // For PID ordinals output reports
HID_USAGE_PAGE_PID, // Usage Page
HID_USAGE_PID_TYPE_SPECIFIC_BLOCK_OFFSET,
cA(c_rgUsgOrdinals)*cbX(DWORD), // sizeof incoming data
cA(c_rgUsgOrdinals), // number of elements
c_rgUsgOrdinals // translation table
};
#pragma END_CONST_DATA
PIDREPORT g_Direction =
{
HidP_Output, // For PID ordinals output reports
HID_USAGE_PAGE_PID, // Usage Page
HID_USAGE_PID_DIRECTION,
0x0,
0x0,
NULL
};
/*****************************************************************************
*
* hresFinddwUsageFromdwFlags
*
* Given the flags for a DEVICEOBJECTINSTANCE, find the usage and usage page
* On init we enum the device and cache the
* DeviceObjects marked as actuators and Effect Triggers.
*
*****************************************************************************/
HRESULT
hresFinddwUsageFromdwFlags
(
IDirectInputEffectDriver *ped,
DWORD dwFlags,
DWORD *pdwUsage
)
{
HRESULT hres = S_OK;
CPidDrv *this = (CPidDrv *)ped;
EnterProcI( PID_hresFinddwUsageFromdwFlags, (_"xxx", ped, dwFlags, pdwUsage ));
// Init FF attributes
hres = PID_InitFFAttributes(ped);
if( SUCCEEDED(hres) )
{
/* Better be a FF object ( actuator / Trigger ) */
if( dwFlags & DIDFT_FFACTUATOR
|| dwFlags & DIDFT_FFEFFECTTRIGGER )
{
hres = S_OK;
} else
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s:FAIL dwFlags(0x%x) not FFACTUATOR | FFEFFECTTRIGGER "),
s_tszProc, dwFlags );
hres = E_UNEXPECTED;
}
if( SUCCEEDED(hres) )
{
UINT cFFObj;
hres = E_NOTIMPL;
/* Loop through the all the objects we found during enum */
for(cFFObj = 0x0;
cFFObj < this->cFFObj;
cFFObj++ )
{
PDIUSAGEANDINST pdiUI = this->rgFFUsageInst + cFFObj;
if( pdiUI->dwType == dwFlags )
{
*pdwUsage = pdiUI->dwUsage;
hres = S_OK;
break;
}
}
}
}
if( FAILED(hres) )
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s:FAIL No mapping for dwFlags(0x%x) "),
s_tszProc, dwFlags );
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* PID_NewEffectIndex
*
* Gets a new effect index.
*
* For host managed devices, we assign an unused effect ID.
* For device managed, we get the effectID from the device
*
*****************************************************************************/
STDMETHODIMP
PID_NewEffectIndex
(
IDirectInputEffectDriver *ped,
LPDIEFFECT peff,
DWORD dwEffectId,
PDWORD pdwEffect
)
{
CPidDrv *this = (CPidDrv *)ped;
HRESULT hres;
USHORT dwEffect;
EnterProcI(PID_NewEffectIndex, (_"xx", this, pdwEffect));
AssertF(*pdwEffect == 0);
// Default assumption is that the device is full.
hres = DIERR_DEVICEFULL;
if( this->uDeviceManaged & PID_DEVICEMANAGED )
{
PVOID pReport;
UINT cbReport;
USHORT LinkCollection = 0x0;
USHORT TLinkCollection = 0x0;
UINT nUsages = 0x1;
USAGE Usage;
USAGE Collection = HID_USAGE_PID_CREATE_NEW_EFFECT;
USAGE UsagePage;
HIDP_REPORT_TYPE HidP_Type = HidP_Feature;
cbReport = this->cbReport[HidP_Type];
pReport = this->pReport[HidP_Type];
ZeroBuf(pReport, cbReport);
// Usage and Usage page determine type of new effect
Usage = DIGETUSAGE(dwEffectId);
UsagePage = DIGETUSAGEPAGE(dwEffectId);
hres = PID_GetLinkCollectionIndex(ped, UsagePage, Collection, 0x0, &LinkCollection );
if( SUCCEEDED(hres) )
{
Collection = HID_USAGE_PID_EFFECT_TYPE;
hres = PID_GetLinkCollectionIndex(ped, UsagePage, Collection, LinkCollection, &TLinkCollection );
}
if( SUCCEEDED(hres) )
{
hres = HidP_SetUsages
(
HidP_Type,
UsagePage,
TLinkCollection,
&Usage,
&nUsages,
this->ppd,
pReport,
cbReport);
}
if( SUCCEEDED(hres) && PIDMAKEUSAGEDWORD(ET_CUSTOM) == dwEffectId )
{
DICUSTOMFORCE DiParam;
LONG lValue;
int nBytes;
AssertF(peff->cbTypeSpecificParams <= cbX(DiParam) );
memcpy(&DiParam, peff->lpvTypeSpecificParams, cbX(DiParam));
//how many bytes do we need per sample?
nBytes = ( this->customCaps[ 0].BitSize + this->customCaps[ 1].BitSize + this->customCaps[ 2].BitSize)/8;
lValue = DiParam.cSamples * nBytes;
hres = HidP_SetScaledUsageValue
(
HidP_Type,
HID_USAGE_PAGE_GENERIC,
LinkCollection,
HID_USAGE_GENERIC_BYTE_COUNT,
lValue,
this->ppd,
pReport,
cbReport
);
}
// Send the report
if( SUCCEEDED(hres) )
{
hres = PID_SendReport(ped, pReport, cbReport, HidP_Type, TRUE, 0, 1);
}
// Get back the effect ID
if( SUCCEEDED(hres) )
{
PIDREPORT BlockIndex = g_BlockIndex;
USHORT LinkCollection;
BlockIndex.Collection = HID_USAGE_PID_BLOCK_LOAD_REPORT;
BlockIndex.HidP_Type = HidP_Feature;
hres = PID_GetLinkCollectionIndex
(ped,
BlockIndex.UsagePage,
BlockIndex.Collection,
0x0,
&LinkCollection);
if( SUCCEEDED(hres) )
{
PUCHAR pReport = this->pReport[BlockIndex.HidP_Type];
UINT cbReport = this->cbReport[BlockIndex.HidP_Type];
PID_GetReport(ped, &BlockIndex, LinkCollection, pReport, cbReport );
// Get the EffectIndex
hres = PID_ParseReport
(
ped,
&BlockIndex,
LinkCollection,
pdwEffect,
cbX(*pdwEffect),
pReport,
cbReport
);
if( SUCCEEDED(hres ) )
{
NTSTATUS ntStat;
USAGE rgUsageList[MAX_BUTTONS];
UINT cUsageList = MAX_BUTTONS;
PID_GetLinkCollectionIndex(ped, HID_USAGE_PAGE_PID, HID_USAGE_PID_BLOCK_LOAD_STATUS, LinkCollection, &LinkCollection );
ntStat = HidP_GetUsages
(
BlockIndex.HidP_Type,
HID_USAGE_PAGE_PID,
LinkCollection,
rgUsageList,
&cUsageList,
this->ppd,
pReport,
cbReport);
if(SUCCEEDED(ntStat) )
{
if (cUsageList != 0)
{
if( rgUsageList[0] == HID_USAGE_PID_BLOCK_LOAD_FULL )
{
hres = DIERR_DEVICEFULL;
} else if(rgUsageList[0] == HID_USAGE_PID_BLOCK_LOAD_ERROR )
{
hres = DIERR_PID_BLOCKLOADERROR;
} else
{
AssertF(rgUsageList[0] == HID_USAGE_PID_BLOCK_LOAD_SUCCESS);
}
}
else
{
//because of issues w/ some chipsets (see Whistler bugs 231235, 304863),
//cUsageList can be 0.
//so warn the user.
RPF(TEXT("Unable to get the effect load status -- may be a USB chipset issue!"));
RPF(TEXT("The effect may not play correctly!"));
}
}
}
if(SUCCEEDED(hres))
{
NTSTATUS ntStat;
UsagePage = HID_USAGE_PAGE_PID;
Usage = HID_USAGE_PID_RAMPOOL_AVAILABLE;
ntStat = HidP_GetScaledUsageValue
(
HidP_Feature,
UsagePage,
LinkCollection,
Usage,
&this->dwUsedMem,
this->ppd,
pReport,
cbReport
);
if(FAILED(ntStat) )
{
// Reset the amount of used memory
this->dwUsedMem = 0x0 ;
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s: FAIL HidP_GetScaledUsageValue:0x%x for(%x, %x,%x:%s)"),
s_tszProc, ntStat,
LinkCollection, UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
}
}
}
}
if( SUCCEEDED(hres) )
{
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,*pdwEffect);
// Serialize access to for new effect
WaitForSingleObject(g_hmtxShared, INFINITE);
AssertF(! (pEffectState->lEfState & PID_EFFECT_BUSY ));
pEffectState->lEfState |= PID_EFFECT_BUSY;
hres = S_OK;
ReleaseMutex(g_hmtxShared);
}
} else
{
// Serialize access to common memory block
WaitForSingleObject(g_hmtxShared, INFINITE);
for(dwEffect = 1;
dwEffect <= this->cMaxEffects;
dwEffect++)
{
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,dwEffect);
if( ! ( pEffectState->lEfState & PID_EFFECT_BUSY ) )
{
pEffectState->lEfState |= PID_EFFECT_BUSY;
*pdwEffect = dwEffect;
ZeroBuf(pEffectState->PidMem, cbX(pEffectState->PidMem[0]) * this->cMaxParameters );
hres = S_OK;
break;
}
}
ReleaseMutex(g_hmtxShared);
}
if( SUCCEEDED(hres) )
{
((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded++;
} else
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s:FAIL Could not create new effects, already have %d "),
s_tszProc, ((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded );
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* PID_ValidateEffectIndex
*
* Validates an effect index.
*
*****************************************************************************/
STDMETHODIMP PID_ValidateEffectIndex
(
IDirectInputEffectDriver *ped,
DWORD dwEffect
)
{
CPidDrv *this = (CPidDrv *)ped;
HRESULT hres;
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this, dwEffect);
EnterProc(PID_ValidateEffectIndex, (_"xx", this, dwEffect));
if( pEffectState->lEfState & PID_EFFECT_BUSY )
{
hres = S_OK;
} else
{
hres = E_HANDLE;
}
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* PID_DestroyEffect
*
* Remove an effect from the device.
*
* If the effect is playing, the driver should stop it
* before unloading it.
*
* dwId
*
* The external joystick number being addressed.
*
* dwEffect
*
* The effect to be destroyed.
*
* Returns:
*
* S_OK on success.
*
* Any other DIERR_* error code may be returned.
*
* Private driver-specific error codes in the range
* DIERR_DRIVERFIRST through DIERR_DRIVERLAST
* may be returned.
*
*
* Makes an effect Index available for reuse. Deallocates parameter block
* memory.
*
*****************************************************************************/
STDMETHODIMP
PID_DestroyEffect
(
IDirectInputEffectDriver *ped,
DWORD dwId,
DWORD dwEffect
)
{
CPidDrv *this = (CPidDrv *)ped;
HRESULT hres=S_OK;
EnterProc(PID_DestroyEffectIndex, (_"xx", this, dwEffect));
DllEnterCrit();
// Stop the Effect
hres = PID_EffectOperation
(
ped,
dwId,
dwEffect,
PID_DIES_STOP,
0x0,
TRUE,
0,
1
);
if(SUCCEEDED(hres) &&
( this->uDeviceManaged & PID_DEVICEMANAGED ) )
{
// Device Managed memory needs to be freed explicitly.
USHORT cbReport;
PUCHAR pReport;
PIDREPORT BlockIndex = g_BlockIndex;
USHORT LinkCollection;
cbReport = this->cbReport[BlockIndex.HidP_Type];
pReport = this->pReport[BlockIndex.HidP_Type];
ZeroBuf(pReport, cbReport);
BlockIndex.Collection = HID_USAGE_PID_BLOCK_FREE_REPORT;
BlockIndex.HidP_Type = HidP_Output;
PID_GetLinkCollectionIndex
(ped,
BlockIndex.UsagePage,
BlockIndex.Collection,
0x0,
&LinkCollection);
hres = PID_PackValue
(
ped,
&BlockIndex,
LinkCollection,
&dwEffect,
cbX(dwEffect),
pReport,
cbReport
);
if(SUCCEEDED(hres) )
{
hres = PID_SendReport(ped, pReport, cbReport, BlockIndex.HidP_Type, TRUE, 0, 1);
}
}
if( SUCCEEDED(hres) )
{
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this,dwEffect);
UINT nAlloc, uParam;
WaitForSingleObject(g_hmtxShared, INFINITE);
pEffectState->lEfState = PID_EFFECT_RESET;
for( uParam = 0x0; uParam < this->cMaxParameters; uParam++ )
{
PPIDMEM pMem = &pEffectState->PidMem[uParam] ;
if( PIDMEM_SIZE(pMem) )
{
PPIDMEM pTmp;
PUNITSTATE pUnitState = (PUNITSTATE)(g_pshmem + this->iUnitStateOffset);
for(nAlloc = 0x0, pTmp = &(pUnitState->Guard[0]);
nAlloc < pUnitState->nAlloc;
nAlloc++, pTmp = (PPIDMEM)((PUCHAR)pUnitState + pTmp->iNext))
{
if( (PPIDMEM)(pTmp->iNext) == (PPIDMEM)((PUCHAR)pMem - (PUCHAR)pUnitState ))
{
pTmp->iNext = pMem->iNext;
pUnitState->nAlloc--;
pUnitState->cbAlloc -= PIDMEM_SIZE(pMem);
pMem->iNext = 0;
pMem->uOfSz = 0x0;
break;
}
}
}
}
ReleaseMutex(g_hmtxShared);
}
if( SUCCEEDED(hres) )
{
((PUNITSTATE)(g_pshmem + this->iUnitStateOffset))->cEfDownloaded--;
}
DllLeaveCrit();
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* PID_SanitizeEffect
*
* Sanitize the parameters in the DIEFFECT structure.
* Clip values of magnitude, time, etc ..
* Convert the axes array to usage, usage page from the DINPUT obj instances.
* Convert and scale angles.
*
*****************************************************************************/
HRESULT PID_SanitizeEffect
(
IDirectInputEffectDriver *ped,
LPDIEFFECT lpeff,
DWORD dwFlags
)
{
CPidDrv *this = (CPidDrv *)ped;
HRESULT hres = S_OK;
UINT nAxis;
EnterProc( PID_SanitizeEffect, (_"xxx", ped, lpeff, dwFlags));
if( ( dwFlags & DIEP_TRIGGERBUTTON )
&& lpeff->dwTriggerButton != -1 )
{
DWORD dwUsage;
hres = hresFinddwUsageFromdwFlags(ped, lpeff->dwTriggerButton, &dwUsage);
if( SUCCEEDED(hres) )
{
USAGE Usage = DIGETUSAGE(dwUsage);
USAGE UsagePage = DIGETUSAGEPAGE(dwUsage);
lpeff->dwTriggerButton = Usage;
} else
{
lpeff->dwTriggerButton = 0x0;
}
} else
{
lpeff->dwTriggerButton = 0x0;
}
for(nAxis = 0x0;
nAxis < lpeff->cAxes;
nAxis++ )
{
DWORD dwUsage;
hres = hresFinddwUsageFromdwFlags(ped, lpeff->rgdwAxes[nAxis], &dwUsage);
if(SUCCEEDED(hres) )
{
lpeff->rgdwAxes[nAxis] = dwUsage;
}
//if we have only 1 axis and direction of 0 or 360, make sure the direction matches the axis!
//if direction is not 0, we do not know what the app wants, so let it be.
if ((lpeff->cAxes == 1) && (lpeff->rglDirection[nAxis] % 360*DI_DEGREES == 0))
{
#ifndef HID_USAGE_SIMULATION_STEERING
#define HID_USAGE_SIMULATION_STEERING ((USAGE) 0xC8)
#endif
#ifndef HID_USAGE_SIMULATION_ACCELERATOR
#define HID_USAGE_SIMULATION_ACCELERATOR ((USAGE) 0xC4)
#endif
#ifndef HID_USAGE_SIMULATION_BRAKE
#define HID_USAGE_SIMULATION_BRAKE ((USAGE) 0xC5)
#endif
//if it is X-axis or steering on the wheel, set direction to 90 degrees
if ((DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_GENERIC_X) || (DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_SIMULATION_STEERING))
{
lpeff->rglDirection[nAxis] = 90*DI_DEGREES;
}
//if it is Y-axis or accelerator or brake, set direction to 0
else if ((DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_GENERIC_Y) || (DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_SIMULATION_ACCELERATOR) ||
(DIGETUSAGE(lpeff->rgdwAxes[nAxis]) == HID_USAGE_SIMULATION_BRAKE))
{
lpeff->rglDirection[nAxis] = 0x0;
}
}
else
//we have more than 1 axes or direction is non-0 for 1-axis effect; leave the direction along
{
lpeff->rglDirection[nAxis] %= 360*DI_DEGREES;
if(lpeff->rglDirection[nAxis] < 0)
{
lpeff->rglDirection[nAxis] += 360*DI_DEGREES;
}
}
}
// Clip the values to min / max
lpeff->dwGain = Clip(lpeff->dwGain, DI_FFNOMINALMAX);
// Scale to units that device expects
PID_ApplyScalingFactors(ped, &g_Effect, &this->DiSEffectScale, this->DiSEffectScale.dwSize, &this->DiSEffectOffset, this->DiSEffectOffset.dwSize, lpeff, lpeff->dwSize );
ExitOleProc();
return hres;
}
/*****************************************************************************
*
* CPidDrv_DownloadEffect
*
* Send an effect to the device.
*
* dwId
*
* The external joystick number being addressed.
*
* dwEffectId
*
* Internal identifier for the effect, taken from
* the DIEFFECTATTRIBUTES structure for the effect
* as stored in the registry.
*
* pdwEffect
*
* On entry, contains the handle of the effect being
* downloaded. If the value is zero, then a new effect
* is downloaded. If the value is nonzero, then an
* existing effect is modified.
*
* On exit, contains the new effect handle.
*
* On failure, set to zero if the effect is lost,
* or left alone if the effect is still valid with
* its old parameters.
*
* Note that zero is never a valid effect handle.
*
* peff
*
* The new parameters for the effect. The axis and button
* values have been converted to object identifiers
* as follows:
*
* - One type specifier:
*
* DIDFT_RELAXIS,
* DIDFT_ABSAXIS,
* DIDFT_PSHBUTTON,
* DIDFT_TGLBUTTON,
* DIDFT_POV.
*
* - One instance specifier:
*
* DIDFT_MAKEINSTANCE(n).
*
* Other bits are reserved and should be ignored.
*
* For example, the value 0x0200104 corresponds to
* the type specifier DIDFT_PSHBUTTON and
* the instance specifier DIDFT_MAKEINSTANCE(1),
* which together indicate that the effect should
* be associated with button 1. Axes, buttons, and POVs
* are each numbered starting from zero.
*
* dwFlags
*
* Zero or more DIEP_* flags specifying which
* portions of the effect information has changed from
* the effect already on the device.
*
* This information is passed to drivers to allow for
* optimization of effect modification. If an effect
* is being modified, a driver may be able to update
* the effect in situ and transmit to the device
* only the information that has changed.
*
* Drivers are not, however, required to implement this
* optimization. All fields in the DIEFFECT structure
* pointed to by the peff parameter are valid, and
* a driver may choose simply to update all parameters of
* the effect at each download.
*
* Returns:
*
* S_OK on success.
*
* DI_TRUNCATED if the parameters of the effect were
* successfully downloaded, but some of them were
* beyond the capabilities of the device and were truncated.
*
* DI_EFFECTRESTARTED if the parameters of the effect
* were successfully downloaded, but in order to change
* the parameters, the effect needed to be restarted.
*
* DI_TRUNCATEDANDRESTARTED if both DI_TRUNCATED and
* DI_EFFECTRESTARTED apply.
*
* Any other DIERR_* error code may be returned.
*
* Private driver-specific error codes in the range
* DIERR_DRIVERFIRST through DIERR_DRIVERLAST
* may be returned.
*
*****************************************************************************/
STDMETHODIMP
PID_DownloadEffect
(
IDirectInputEffectDriver *ped,
DWORD dwId,
DWORD dwEffectId,
LPDWORD pdwEffect,
LPCDIEFFECT peff,
DWORD dwFlags
)
{
CPidDrv *this = (CPidDrv *)ped;
HRESULT hres = S_OK;
DIEFFECT eff;
DWORD rgdwAxes[MAX_AXES];
LONG rglDirection[MAX_AXES];
UINT uParameter = 0x0 ;
UINT totalBlocks = 0x0;
BOOL bBlocking = FALSE;
EnterProcI( PID_DownloadEffectBlock, (_"xxxxxx", ped, dwId, dwEffectId, pdwEffect, peff, dwFlags));
AssertF(peff->cAxes <= MAX_AXES);
DllEnterCrit();
// If new effect is being downloaded
if( *pdwEffect == 0x0 )
{
// Verify that dwEffectId is supported
DWORD dwJunk;
PIDSUPPORT pidSupport;
pidSupport.dwPidUsage = dwEffectId;
pidSupport.HidP_Type = HidP_Output;
pidSupport.Type = HID_BUTTON;
hres = PID_Support
(
ped,
0x1,
&pidSupport,
&dwJunk
);
if(FAILED(hres))
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s:FAIL dwEffectId(0x%x) not supported"),
s_tszProc, dwEffectId );
}
}
if( SUCCEEDED(hres) )
{
// Make a local copy of the effect structure
// And sanitize the effect struct
eff = *peff;
memcpy(rgdwAxes, peff->rgdwAxes,eff.cAxes*cbX(*(eff.rgdwAxes)));
memcpy(rglDirection, peff->rglDirection, eff.cAxes*cbX(*(eff.rglDirection)));
eff.rgdwAxes = rgdwAxes;
eff.rglDirection = rglDirection;
hres = PID_SanitizeEffect(ped, &eff, dwFlags);
}
// Allocate new effect index or Validate Existing index
if( SUCCEEDED(hres) )
{
if( *pdwEffect != 0x0 )
{
hres = PID_ValidateEffectIndex(ped, *pdwEffect);
}
else
{
if (! (dwFlags & DIEP_NODOWNLOAD))
{
hres = PID_NewEffectIndex(ped, &eff, dwEffectId, pdwEffect);
//block the first time around
bBlocking = TRUE;
}
}
}
if (dwFlags & DIEP_NODOWNLOAD)
{
goto done;
}
//if the DIEP_NORESTART flag is passed, we have no block because this may fail
//if the device can't update the parameters on the fly
if (dwFlags & DIEP_NORESTART)
{
bBlocking = TRUE;
}
if( SUCCEEDED(hres) )
{
//count up how many total blocks we will have in this download
//check wether we're sending the effect block
if (dwFlags & ( DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_TRIGGERBUTTON | DIEP_TRIGGERREPEATINTERVAL | DIEP_AXES | DIEP_DIRECTION | DIEP_STARTDELAY ) )
{
totalBlocks ++;
}
//check whether we're sending the type-specific params
if (dwFlags & DIEP_TYPESPECIFICPARAMS)
{
//this is slightly different, in that conditions can have 1 type-specific block PER AXIS,
//i.e. currently up to 2
//so if we have a DICONDITION, we check how many type-specific blocks we've got
if ((dwEffectId == PIDMAKEUSAGEDWORD(ET_SPRING)) ||
(dwEffectId == PIDMAKEUSAGEDWORD(ET_DAMPER)) ||
(dwEffectId == PIDMAKEUSAGEDWORD(ET_INERTIA)) ||
(dwEffectId == PIDMAKEUSAGEDWORD(ET_FRICTION)))
{
totalBlocks +=(eff.cbTypeSpecificParams)/sizeof(DICONDITION);
//DICONDITIONS also can't have envelopes
dwFlags &= ~DIEP_ENVELOPE;
}
else
{
totalBlocks++;
}
}
//check whether we're sending the envelope
if ((dwFlags & DIEP_ENVELOPE) && (eff.lpEnvelope != NULL))
{
totalBlocks++;
}
//check whether we need to send the start reprot
if (dwFlags & DIEP_START)
{
totalBlocks++;
}
//make sure that we haven't got more than the maximum
AssertF(totalBlocks <= MAX_BLOCKS);
// Do the parameter block
if( SUCCEEDED(hres)
&& ( dwFlags & ( DIEP_TYPESPECIFICPARAMS | DIEP_ENVELOPE) )
)
{
hres = PID_DoParameterBlocks
(
ped,
dwId,
dwEffectId,
*pdwEffect,
&eff,
dwFlags,
&uParameter,
bBlocking,
totalBlocks
);
}
// Now do the effect report
if( SUCCEEDED(hres)
&& ( dwFlags & ( DIEP_DURATION | DIEP_SAMPLEPERIOD | DIEP_GAIN | DIEP_TRIGGERBUTTON | DIEP_TRIGGERREPEATINTERVAL | DIEP_AXES | DIEP_DIRECTION | DIEP_STARTDELAY ) ) )
{
USHORT cbReport;
PUCHAR pReport;
AssertF(g_Effect.HidP_Type == HidP_Output);
cbReport = this->cbReport[g_Effect.HidP_Type];
pReport = this->pReport[g_Effect.HidP_Type];
// Set the Effect Structure
if( SUCCEEDED(hres) )
{
USHORT LinkCollection;
PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, g_Effect.Collection, 0x0, &LinkCollection );
ZeroBuf(pReport, cbReport);
// Do the common elements of the effect structure
hres = PID_PackValue
(
ped,
&g_Effect,
LinkCollection,
&eff,
eff.dwSize,
pReport,
cbReport
);
// Set the Effect Block Index
if( SUCCEEDED(hres) )
{
hres = PID_PackValue
(
ped,
&g_BlockIndex,
LinkCollection,
pdwEffect,
cbX(*pdwEffect),
pReport,
cbReport
);
}
// Set Direction and axis attributes
if( SUCCEEDED(hres) )
{
USHORT DirectionCollection;
PID_GetLinkCollectionIndex(ped, g_Direction.UsagePage, g_Direction.Collection, 0x0, &DirectionCollection );
PID_ApplyScalingFactors(ped, &g_Direction, &this->DiSEffectAngleScale, cbX(this->DiSEffectAngleScale), &this->DiSEffectAngleOffset, cbX(this->DiSEffectAngleOffset), eff.rglDirection, eff.cAxes*cbX(LONG) );
hres = PID_PackValue
(
ped,
&g_Direction,
DirectionCollection,
eff.rglDirection,
eff.cAxes * cbX(LONG),
pReport,
cbReport
);
if(SUCCEEDED(hres) &&
! ( eff.dwFlags & DIEFF_CARTESIAN ) )
{
// Direction Enable
USHORT Usage;
USHORT UsagePage;
UINT nUsages = 0x1;
NTSTATUS ntStat;
// Direction Enable is in the set effect collection
UsagePage = g_Effect.UsagePage;
Usage = HID_USAGE_PID_DIRECTION_ENABLE;
ntStat = HidP_SetUsages
(
HidP_Output,
UsagePage,
LinkCollection,
&Usage,
&nUsages,
this->ppd,
pReport,
cbReport);
if( FAILED(ntStat) )
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
s_tszProc, ntStat,
LinkCollection, UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
} else
{
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
s_tszProc, ntStat,
LinkCollection,UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
}
} else //if( dwFlags & DIEP_AXES )
{
UINT nAxis;
USHORT LinkCollection_AE=0x0;
if(SUCCEEDED(PID_GetLinkCollectionIndex(ped, HID_USAGE_PAGE_PID, HID_USAGE_PID_AXES_ENABLE, 0x0, &LinkCollection_AE)))
{
// ISSUE-2001/03/29-timgill Need to support axes within pointer collections
// PID spec indicates a pointer collection,
// Do we want to support axes enables within a pointer
// collection ?
// See if there is a pointer collection
}
for(nAxis = 0x0;
nAxis < eff.cAxes;
nAxis++ )
{
UINT nUsages = 0x1;
USHORT Usage = DIGETUSAGE(eff.rgdwAxes[nAxis]);
USHORT UsagePage = DIGETUSAGEPAGE(eff.rgdwAxes[nAxis]);
NTSTATUS ntStat;
//ISSUE-2001/03/29-timgill For now we assume any collection
ntStat = HidP_SetUsages
(
HidP_Output,
UsagePage,
0x0,
&Usage,
&nUsages,
this->ppd,
pReport,
cbReport);
if( FAILED(ntStat) )
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
s_tszProc, ntStat,
0x0, UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
hres = ntStat;
break;
} else
{
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
s_tszProc, ntStat,
0x0, UsagePage, Usage,
PIDUSAGETXT(UsagePage, Usage) );
}
}
}
}
if( SUCCEEDED(hres)
&& !( this->uDeviceManaged & PID_DEVICEMANAGED )
)
{
// Need parameter block offsets
UINT indx;
USHORT LinkCollection;
LONG rglValue[MAX_ORDINALS];
PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, g_TypeSpBlockOffset.Collection, 0x0, &LinkCollection );
for(indx = 0x0; indx < this->cMaxParameters; indx++ )
{
hres = PID_GetParameterOffset(ped, *pdwEffect, indx, 0x0, &rglValue[indx]);
if(FAILED(hres))
{
break;
}
}
if(SUCCEEDED(hres))
{
hres = PID_PackValue
(
ped,
&g_TypeSpBlockOffset,
LinkCollection,
rglValue,
this->cMaxParameters*cbX(LONG),
pReport,
cbReport
);
}
}
// Set the Effect Type
if( SUCCEEDED(hres) )
{
USAGE UsagePage = DIGETUSAGEPAGE(dwEffectId);
USAGE Usage = DIGETUSAGE(dwEffectId);
UINT nUsages = 0x1;
USHORT LinkCollection_ET;
NTSTATUS ntStat;
PID_GetLinkCollectionIndex(ped, g_Effect.UsagePage, HID_USAGE_PID_EFFECT_TYPE, 0x0, &LinkCollection_ET);
ntStat = HidP_SetUsages
(
HidP_Output,
UsagePage,
LinkCollection_ET,
&Usage,
&nUsages,
this->ppd,
pReport,
cbReport);
if( FAILED(ntStat) )
{
SquirtSqflPtszV(sqfl | sqflError,
TEXT("%s: FAIL HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
s_tszProc, ntStat,
LinkCollection_ET, UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
hres = ntStat;
} else
{
SquirtSqflPtszV(sqfl | sqflVerbose,
TEXT("%s: HidP_SetUsages:0x%x for(%x,%x,%x:%s)"),
s_tszProc, ntStat,
LinkCollection_ET, UsagePage, Usage,
PIDUSAGETXT(UsagePage,Usage) );
}
}
if( SUCCEEDED(hres) )
{
hres = PID_SendReport(ped, pReport, cbReport, g_Effect.HidP_Type, bBlocking, uParameter, totalBlocks);
uParameter ++;
}
}
}
}
if( FAILED(hres) )
{
PID_DestroyEffect(ped, dwId, *pdwEffect);
}
if( SUCCEEDED(hres)
&& (dwFlags & DIEP_START) )
{
hres = PID_EffectOperation
(
ped,
dwId,
*pdwEffect,
PID_DIES_START,
0x1,
bBlocking,
uParameter,
totalBlocks
);
if (SUCCEEDED(hres))
{
//set the status to DIEGES_PLAYING.
//we do this because of the following: if an app calls Start(), and then immediately
//calls GetEffectStatus(), it might happen that our second thread (pidrd.c)
//would not have time to update the status of the effect to DIEGES_PLAYING
//(see Whistler bug 287035).
//GetEffectStatus() returns (pEffectState->lEfState & DIEGES_PLAYING).
//in the blocking case, we know that the call to WriteFile() has succeeded, and that
//all the data has been written (see PID_SendReportBl() in pidhid.c) --
//so we might as well set the status.
//in the non-blocking case, the data can be buffered anyway -- so we might as well set the status.
PEFFECTSTATE pEffectState = PeffectStateFromBlockIndex(this, *pdwEffect);
pEffectState->lEfState |= DIEGES_PLAYING;
}
}
done:;
DllLeaveCrit();
ExitOleProc();
return hres;
}