1295 lines
42 KiB
C
1295 lines
42 KiB
C
/*****************************************************************************
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
|