You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1136 lines
37 KiB
1136 lines
37 KiB
// @doc
|
|
/**********************************************************************
|
|
*
|
|
* @module IDirectInputEffectDriver.cpp |
|
|
*
|
|
* Contains Class Implementation of CIDirectInputEffectDriverClassFactory:
|
|
* Factory for Creating Proper Effect Driver
|
|
*
|
|
* History
|
|
* ----------------------------------------------------------
|
|
* Matthew L. Coill (mlc) Original Jul-7-1999
|
|
*
|
|
* (c) 1999 Microsoft Corporation. All right reserved.
|
|
*
|
|
* @topic This IDirectInputEffectDriver |
|
|
* This Driver sits on top of the standard PID driver (which is also
|
|
* an IDirectInputEffectDriver) and passes most requests to the PID driver.
|
|
* Some requests such as, DownloadEffect and SendForceFeedback command are
|
|
* modified for our use. Modification purposes are described at each function
|
|
* definition.
|
|
*
|
|
**********************************************************************/
|
|
|
|
#include "IDirectInputEffectDriverClassFactory.h"
|
|
#include "IDirectInputEffectDriver.h"
|
|
#include <WinIOCTL.h> // For CTL_CODE definition
|
|
#include "..\\GCKernel.sys\\GckExtrn.h"
|
|
#include <crtdbg.h>
|
|
#include <objbase.h> // For CoUninitialize
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <tchar.h>
|
|
|
|
const GUID IID_IDirectInputEffectDriver = {
|
|
0x02538130,
|
|
0x898F,
|
|
0x11D0,
|
|
{ 0x9A,0xD0,0x00,0xA0,0xC9,0xA0,0x6E,0x35 }
|
|
};
|
|
|
|
extern TCHAR CLSID_SWPIDDriver_String[];
|
|
|
|
LONG DllAddRef();
|
|
LONG DllRelease();
|
|
|
|
DWORD __stdcall DoWaitForForceSchemeChange(void* pParameter);
|
|
const DWORD c_dwShutdownWait = 500; // (0.5 Seconds)
|
|
|
|
struct DIHIDFFINITINFO_STRUCT {
|
|
DWORD dwSize;
|
|
LPWSTR pwszDeviceInterface;
|
|
GUID GuidInstance;
|
|
};
|
|
|
|
// PID Defines for Effect Tyoes
|
|
#define PID_CONSTANT_FORCE 0x26
|
|
#define PID_RAMP 0x27
|
|
#define PID_SQUARE 0x30
|
|
#define PID_SINE 0x31
|
|
#define PID_TRIANGLE 0x32
|
|
#define PID_SAWTOOTHUP 0x33
|
|
#define PID_SAWTOOTHDOWN 0x34
|
|
#define PID_SPRING 0x40
|
|
#define PID_DAMPER 0x41
|
|
#define PID_INTERTIA 0x42
|
|
#define PID_FRICTION 0x43
|
|
|
|
|
|
struct PercentageEntry
|
|
{
|
|
DWORD dwAngle;
|
|
DWORD dwPercentageX;
|
|
// DWORD dwPercentageY; Y == 10000 - X
|
|
};
|
|
|
|
// Array of Fixed value data
|
|
const PercentageEntry g_PercentagesArray[] =
|
|
{
|
|
// Angle, Sin^2(Angle)
|
|
{ 0, 0}, // 0 Degrees
|
|
{ 1125, 381}, // 11.25 Degrees
|
|
{ 2250, 1465}, // 22.5 Degrees
|
|
{ 3375, 3087}, // 33.75 Degrees
|
|
{ 4500, 5000}, // 45 Degrees
|
|
{ 5625, 6913}, // 56.25 Degrees
|
|
{ 6750, 8536}, // 67.50 Degrees
|
|
{ 7875, 9619}, // 78.75 Degrees
|
|
{ 9000, 10000}, // 90 Degrees
|
|
};
|
|
|
|
const DWORD c_dwTableQuantization = g_PercentagesArray[1].dwAngle;
|
|
const LONG c_lContributionY = 2; // (1/2 = 50%)
|
|
|
|
const BYTE c_bSideWinderPIDReportID_SetEffect = 1;
|
|
|
|
// Usage Pages (just PID)
|
|
const USAGE c_HidUsagePage_PID = 0x0F;
|
|
|
|
// Usages
|
|
const USAGE c_HidUsage_EffectType = 0x25;
|
|
const USAGE c_HidUsage_EffectType_Spring = 0x40;
|
|
const USAGE c_HidUsage_EffectBlock_Gain = 0x52;
|
|
const USAGE c_HidUsage_EffectBlock_Index = 0x22; // This is the ID of the effect
|
|
|
|
// Preloaded Effects
|
|
const BYTE c_EffectID_RTCSpring = 1;
|
|
|
|
// Local Debugging Streaming Function that works in release
|
|
#undef UseMyDebugOut
|
|
void __cdecl myDebugOut (LPCSTR lpszFormat, ...)
|
|
{
|
|
#ifdef UseMyDebugOut
|
|
//Stolen from inline void _cdecl AtlTrace(LPCSTR lpszFormat, ...) in AtlBase.h
|
|
va_list args;
|
|
va_start(args, lpszFormat);
|
|
|
|
int nBuf;
|
|
char szBuffer[1024];
|
|
|
|
nBuf = _vsnprintf(szBuffer, sizeof(szBuffer), lpszFormat, args);
|
|
_ASSERTE(nBuf < sizeof(szBuffer)); //Output truncated as it was > sizeof(szBuffer)
|
|
|
|
#ifdef _NDEBUG
|
|
OutputDebugStringA(szBuffer);
|
|
#else
|
|
_RPTF0 (_CRT_WARN, szBuffer);
|
|
#endif
|
|
|
|
va_end(args);
|
|
#else
|
|
UNREFERENCED_PARAMETER (lpszFormat);
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
/******************** Class CIDirectInputEffectDriver ***********************/
|
|
|
|
/*****************************************************************************
|
|
**
|
|
** CIDirectInputEffectDriverClassFactory::CIDirectInputEffectDriverClassFactory()
|
|
**
|
|
** @mfunc Constructor
|
|
**
|
|
*****************************************************************************/
|
|
CIDirectInputEffectDriver::CIDirectInputEffectDriver
|
|
(
|
|
IDirectInputEffectDriver* pIPIDEffectDriver, //@parm [IN] Pointer to PID Effect Driver
|
|
IClassFactory* pIPIDClassFactory //@parm [IN] Pointer to PID Class Factory
|
|
) :
|
|
|
|
m_ulReferenceCount(1),
|
|
m_dwDIVersion(0xFFFFFFFF),
|
|
m_dwExternalDeviceID(0xFFFFFFFF),
|
|
m_dwInternalDeviceID(0xFFFFFFFF),
|
|
m_pIPIDEffectDriver(pIPIDEffectDriver),
|
|
m_pIPIDClassFactory(pIPIDClassFactory),
|
|
m_hKernelDeviceDriver(NULL),
|
|
m_hKernelDeviceDriverDuplicate(NULL),
|
|
m_hHidDeviceDriver(NULL),
|
|
m_dwGcKernelDevice(0),
|
|
m_hForceSchemeChangeWaitThread(NULL),
|
|
m_dwForceSchemeChangeThreadID(0),
|
|
m_pPreparsedData(NULL)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::Constructor (pIPIDEffectDriver:0x%0p)\n", pIPIDEffectDriver);
|
|
|
|
// Add to gobal object count
|
|
DllAddRef();
|
|
|
|
// Add references for objects we are holding
|
|
m_pIPIDClassFactory->AddRef();
|
|
m_pIPIDEffectDriver->AddRef();
|
|
|
|
|
|
::memset((void*)&m_HidAttributes, 0, sizeof(m_HidAttributes));
|
|
|
|
m_ForceMapping.AssignmentBlock.CommandHeader.eID = eForceMap;
|
|
m_ForceMapping.AssignmentBlock.CommandHeader.ulByteSize = sizeof(m_ForceMapping);
|
|
m_ForceMapping.AssignmentBlock.ulVidPid = 0; // Irrelevant
|
|
m_ForceMapping.bMapYToX = FALSE;
|
|
m_ForceMapping.usRTC = 10000;
|
|
m_ForceMapping.usGain = 10000;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
**
|
|
** CIDirectInputEffectDriver::~CIDirectInputEffectDriver()
|
|
**
|
|
** @mfunc Destructor
|
|
**
|
|
*****************************************************************************/
|
|
CIDirectInputEffectDriver::~CIDirectInputEffectDriver()
|
|
{
|
|
_ASSERTE(m_pIPIDEffectDriver == NULL);
|
|
_ASSERTE(m_ulReferenceCount == 0);
|
|
|
|
DllRelease(); // Remove our object from the global object count
|
|
|
|
myDebugOut ("CIDirectInputEffectDriver::Destructor\n");
|
|
}
|
|
|
|
|
|
//IUnknown members
|
|
/***********************************************************************************
|
|
**
|
|
** ULONG CIDirectInputEffectDriver::QueryInterface(REFIID refiid, void** ppvObject)
|
|
**
|
|
** @func Query an IUnknown for a particular type. This causes reference count increase locally only.
|
|
** If it is a type we don't know, should we give the PID driver a crack (the PID driver
|
|
** might have a customized private interface, we don't want to ruin that). Currently not
|
|
** going to pass on the Query because this could screwup Symmetry.
|
|
**
|
|
** @rdesc S_OK : all is well
|
|
** E_INVALIDARG : if (ppvObject == NULL)
|
|
** E_NOINTERFACE : If requested interface is unsupported
|
|
**
|
|
*************************************************************************************/
|
|
HRESULT __stdcall CIDirectInputEffectDriver::QueryInterface
|
|
(
|
|
REFIID refiid, //@parm [IN] Identifier of the requested interface
|
|
void** ppvObject //@parm [OUT] Address to place requested interface pointer
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::QueryInterface (refiid:0x%0p, ppvObject:0x%0p)\n", refiid, ppvObject);
|
|
|
|
HRESULT hrPidQuery = m_pIPIDEffectDriver->QueryInterface(refiid, ppvObject);
|
|
if (SUCCEEDED(hrPidQuery))
|
|
{
|
|
// Don't perform a real addref (PID.dll::QueryInterface will do its own)
|
|
::InterlockedIncrement((LONG*)&m_ulReferenceCount);
|
|
*ppvObject = this;
|
|
}
|
|
return hrPidQuery;
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** ULONG CIDirectInputEffectDriver::AddRef()
|
|
**
|
|
** @func Bumps up the reference count
|
|
** The PID driver reference count is left alone. We only decrement it when
|
|
** this object is ready to go away.
|
|
**
|
|
** @rdesc New reference count
|
|
**
|
|
*************************************************************************************/
|
|
ULONG __stdcall CIDirectInputEffectDriver::AddRef()
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::AddRef (Early) 0x%0p\n", m_ulReferenceCount);
|
|
m_pIPIDEffectDriver->AddRef();
|
|
return (ULONG)(::InterlockedIncrement((LONG*)&m_ulReferenceCount));
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** ULONG CIDirectInputEffectDriver::Release()
|
|
**
|
|
** @func Decrements the reference count.
|
|
** if the reference count becomes zero this object is destroyed.
|
|
** The PID Factory reference is only effected if it is time to release all.
|
|
**
|
|
** @rdesc New reference count
|
|
**
|
|
*************************************************************************************/
|
|
ULONG __stdcall CIDirectInputEffectDriver::Release()
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::Release (Early) 0x%0p\n", m_ulReferenceCount);
|
|
if (m_ulReferenceCount == 0)
|
|
{
|
|
return m_ulReferenceCount;
|
|
}
|
|
|
|
if ((::InterlockedDecrement((LONG*)&m_ulReferenceCount)) != 0)
|
|
{
|
|
m_pIPIDEffectDriver->Release();
|
|
return m_ulReferenceCount;
|
|
}
|
|
|
|
|
|
// Tell the driver to complete outstanding IOCTLs to this device
|
|
if (m_hKernelDeviceDriver == NULL)
|
|
{ // Don't have a handle to PID driver, so open one
|
|
m_hKernelDeviceDriver = ::CreateFile(TEXT(GCK_CONTROL_W32Name), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (m_hKernelDeviceDriver == INVALID_HANDLE_VALUE)
|
|
{
|
|
m_hKernelDeviceDriver = NULL;
|
|
}
|
|
}
|
|
if (m_hKernelDeviceDriver != NULL) // Handle should be open, but check just incase
|
|
{
|
|
DWORD dwReturnDataSize;
|
|
BOOL fSuccess = DeviceIoControl(m_hKernelDeviceDriver,
|
|
IOCTL_GCK_END_FF_NOTIFICATION,
|
|
(void*)(&m_dwGcKernelDevice), sizeof(DWORD), // In
|
|
NULL, 0, &dwReturnDataSize, // Out
|
|
NULL);
|
|
|
|
if (!fSuccess)
|
|
myDebugOut ("CIDirectInputEffectDriver::Release : GCK IOCTL_GCK_END_FF_NOTIFICATION failed!\n");
|
|
|
|
Sleep(c_dwShutdownWait);
|
|
|
|
::CloseHandle(m_hKernelDeviceDriver);
|
|
}
|
|
else
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::Release : Could not Open GCK for IOCTL_GCK_END_FF_NOTIFICATION\n");
|
|
}
|
|
|
|
// Free up the preparsed data
|
|
if (m_pPreparsedData != NULL)
|
|
{
|
|
::HidD_FreePreparsedData(m_pPreparsedData);
|
|
m_pPreparsedData = NULL;
|
|
}
|
|
|
|
// Close the handle to the HID path of the driver
|
|
::CloseHandle(m_hHidDeviceDriver);
|
|
m_hHidDeviceDriver = NULL;
|
|
|
|
// Close the thread handle (which should be done by now)
|
|
if (m_hForceSchemeChangeWaitThread != NULL)
|
|
{
|
|
::CloseHandle(m_hForceSchemeChangeWaitThread);
|
|
m_hForceSchemeChangeWaitThread = NULL;
|
|
m_dwForceSchemeChangeThreadID = 0;
|
|
}
|
|
else
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::Release() m_hForceSchemeCHangeWaitThread did not finish!\n");
|
|
}
|
|
|
|
// Release the low level pid driver and delete ourselves
|
|
m_pIPIDEffectDriver->Release();
|
|
m_pIPIDEffectDriver = NULL;
|
|
|
|
// Release the low level factory (include extra release to fix bug in PID.dll)
|
|
if (m_pIPIDClassFactory->Release() > 0)
|
|
{
|
|
m_pIPIDClassFactory->Release();
|
|
}
|
|
m_pIPIDClassFactory = NULL;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
//IDirectInputEffectDriver members
|
|
HRESULT __stdcall CIDirectInputEffectDriver::DeviceID
|
|
(
|
|
DWORD dwDIVersion,
|
|
DWORD dwExternalID,
|
|
DWORD dwIsBegining,
|
|
DWORD dwInternalID,
|
|
void* pReserved
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::DeviceID (dwDIVersion:0x%08p dwExternalID:0x%08p dwIsBeginning:0x%08p dwInternalID:0x%08p pReserved:0x%08p)\n",
|
|
dwDIVersion, dwExternalID, dwIsBegining, dwInternalID, pReserved);
|
|
|
|
// Store off some data
|
|
m_dwExternalDeviceID = dwExternalID;
|
|
m_dwInternalDeviceID = dwInternalID;
|
|
|
|
bool bPossiblyFirstTime = false;
|
|
// Get a handle to the Kernel Device and activate the thread
|
|
if (m_hKernelDeviceDriver == NULL)
|
|
{
|
|
bPossiblyFirstTime = true;
|
|
m_hKernelDeviceDriver = ::CreateFile(TEXT(GCK_CONTROL_W32Name), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (m_hKernelDeviceDriver == INVALID_HANDLE_VALUE)
|
|
{
|
|
m_hKernelDeviceDriver = NULL;
|
|
myDebugOut ("CIDirectInputEffectDriver::DeviceID Create GCK File Failed!\n");
|
|
}
|
|
else
|
|
{
|
|
InitHidInformation((LPDIHIDFFINITINFO)pReserved); // Set up the HID stuff (preparsed data et al)
|
|
|
|
if (NULL == pReserved ||
|
|
IsBadReadPtr ((const void*)pReserved, (UINT) sizeof (DIHIDFFINITINFO_STRUCT)) )
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::DeviceID E_INVALIDARG (pReserved is NULL!)\n");
|
|
return E_INVALIDARG;
|
|
// Call the default guy
|
|
//return m_pIPIDEffectDriver->DeviceID(dwDIVersion, dwExternalID, dwIsBegining, dwInternalID, pReserved);
|
|
}
|
|
|
|
//
|
|
// get the handle for this device
|
|
//
|
|
WCHAR* pwcInstanceName = ((DIHIDFFINITINFO_STRUCT*)(pReserved))->pwszDeviceInterface;
|
|
DWORD dwBytesReturned;
|
|
BOOL fSuccess = ::DeviceIoControl(m_hKernelDeviceDriver, IOCTL_GCK_GET_HANDLE,
|
|
pwcInstanceName, ::wcslen(pwcInstanceName)*sizeof(WCHAR),
|
|
&m_dwGcKernelDevice, sizeof(m_dwGcKernelDevice), &dwBytesReturned,
|
|
NULL);
|
|
|
|
if (fSuccess != FALSE)
|
|
{
|
|
// Update the force block
|
|
fSuccess =::DeviceIoControl(m_hKernelDeviceDriver, IOCTL_GCK_GET_FF_SCHEME_DATA,
|
|
(void*)(&m_dwGcKernelDevice), sizeof(DWORD),
|
|
(void*)(&m_ForceMapping), sizeof(m_ForceMapping), &dwBytesReturned,
|
|
NULL);
|
|
|
|
// Get the duplicate handle for the thread
|
|
BOOL bDuplicated = ::DuplicateHandle(::GetCurrentProcess(), m_hKernelDeviceDriver, ::GetCurrentProcess(), &m_hKernelDeviceDriverDuplicate, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
|
if ((m_hKernelDeviceDriverDuplicate == INVALID_HANDLE_VALUE) || (bDuplicated == FALSE))
|
|
{
|
|
m_hKernelDeviceDriverDuplicate = NULL;
|
|
}
|
|
else
|
|
{
|
|
m_hForceSchemeChangeWaitThread = ::CreateThread(NULL, 0, DoWaitForForceSchemeChange, (void*)this, 0, &m_dwForceSchemeChangeThreadID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::DeviceID IOCTL_GCK_GET_HANDLE Failed!\n");
|
|
}
|
|
|
|
// Close since I need to reopen at the end (why is this happening?)
|
|
::CloseHandle(m_hKernelDeviceDriver);
|
|
m_hKernelDeviceDriver = NULL;
|
|
}
|
|
}
|
|
|
|
// Hack to get PID.DLL to place keys in registry.
|
|
// -- It won't place them if OEM-FF Key is already there
|
|
/*
|
|
if (bPossiblyFirstTime == true)
|
|
{
|
|
HKEY hkeyOEM = NULL;
|
|
::RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Joystick\\OEM"), 0, KEY_ALL_ACCESS, &hkeyOEM);
|
|
if (hkeyOEM != NULL)
|
|
{
|
|
// Open key specific to the current device (VIDPID is in m_HidAttributes)
|
|
HKEY hkeyOEMForceFeedback = NULL;
|
|
TCHAR rgtcDeviceName[64];
|
|
::wsprintf(rgtcDeviceName, TEXT("VID_%04X&PID_%04X\\OEMForceFeedback"), m_HidAttributes.VendorID, m_HidAttributes.ProductID);
|
|
::RegOpenKeyEx(hkeyOEM, rgtcDeviceName, 0, KEY_ALL_ACCESS, &hkeyOEMForceFeedback);
|
|
|
|
if (hkeyOEMForceFeedback != NULL)
|
|
{
|
|
// Check to see if the effects key is already there
|
|
HKEY hkeyEffects = NULL;
|
|
::RegOpenKeyEx(hkeyOEMForceFeedback, TEXT("Effects"), 0, KEY_READ, &hkeyEffects);
|
|
::RegCloseKey(hkeyOEMForceFeedback);
|
|
if (hkeyEffects != NULL)
|
|
{
|
|
// Effects key is there, this is not the first time we have run
|
|
::RegCloseKey(hkeyEffects);
|
|
bPossiblyFirstTime = false;
|
|
}
|
|
else // Delete the whole OEM ForceFeedback key
|
|
{
|
|
::RegDeleteKey(hkeyOEM, rgtcDeviceName);
|
|
}
|
|
}
|
|
}
|
|
::RegCloseKey(hkeyOEM);
|
|
}
|
|
*/
|
|
// Call the drivers DeviceID (if we have removed the OEMFF Key it will repopulate)
|
|
HRESULT hrPID = m_pIPIDEffectDriver->DeviceID(dwDIVersion, dwExternalID, dwIsBegining, dwInternalID, pReserved);
|
|
|
|
// Do we need to put ourselves back as the DIEffectDriver?
|
|
/* if (bPossiblyFirstTime == true)
|
|
{
|
|
HKEY hkeyOEM = NULL;
|
|
::RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Joystick\\OEM"), 0, KEY_ALL_ACCESS, &hkeyOEM);
|
|
if (hkeyOEM != NULL)
|
|
{
|
|
HKEY hkeyOEMForceFeedback = NULL;
|
|
TCHAR rgtcDeviceName[64];
|
|
::wsprintf(rgtcDeviceName, TEXT("VID_%04X&PID_%04X\\OEMForceFeedback"), m_HidAttributes.VendorID, m_HidAttributes.ProductID);
|
|
::RegOpenKeyEx(hkeyOEM, rgtcDeviceName, 0, KEY_ALL_ACCESS, &hkeyOEMForceFeedback);
|
|
|
|
// Set the registry CLSID value to us
|
|
if (hkeyOEMForceFeedback != NULL)
|
|
{
|
|
::RegSetValueEx(hkeyOEMForceFeedback, TEXT("CLSID"), 0, REG_SZ, (BYTE*)CLSID_SWPIDDriver_String, _tcslen(CLSID_SWPIDDriver_String) * sizeof(TCHAR));
|
|
::RegCloseKey(hkeyOEMForceFeedback);
|
|
}
|
|
::RegCloseKey(hkeyOEM);
|
|
}
|
|
}
|
|
*/
|
|
return hrPID; // Value from the System PID driver
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::GetVersions
|
|
(
|
|
DIDRIVERVERSIONS* pDriverVersions
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::GetVersions (pDriverVersions:0x%08p)\n", pDriverVersions);
|
|
return m_pIPIDEffectDriver->GetVersions(pDriverVersions);
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::Escape
|
|
(
|
|
DWORD dwDeviceID,
|
|
DWORD dwEffectID,
|
|
DIEFFESCAPE* pEscape
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::Escape (dwDeviceID:0x%08p, dwEffectID:0x%08p, pEscape:0x%08p)\n", dwDeviceID, dwEffectID, pEscape);
|
|
return m_pIPIDEffectDriver->Escape(dwDeviceID, dwEffectID, pEscape);
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void CIDirectInputEffectDriver::SetGain(DWORD dwDeviceID, DWORD dwGain)
|
|
**
|
|
** @func Modifies the user gain based on settings and sends it down to the lower PID driver
|
|
**
|
|
** @rdesc Nothing
|
|
**
|
|
*************************************************************************************/
|
|
HRESULT __stdcall CIDirectInputEffectDriver::SetGain
|
|
(
|
|
DWORD dwDeviceID, //@parm [IN] ID for device of interest
|
|
DWORD dwGain //@parm [IN] User selected gain
|
|
)
|
|
{
|
|
dwGain *= m_ForceMapping.usGain/1000; // 0 - 100K
|
|
dwGain /= 10; // 0 - 10K
|
|
myDebugOut ("CIDirectInputEffectDriver::SetGain (dwDeviceID:%d, dwGain:%05d:)\n", dwDeviceID, dwGain);
|
|
return m_pIPIDEffectDriver->SetGain(dwDeviceID, dwGain);
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** HRESULT CopyW2T(LPWSTR pswDest, UINT *puDestSize, LPTSTR ptszSrc)
|
|
**
|
|
** @mfunc Copies a WCHAR into a TCHAR while checking buffer length
|
|
**
|
|
** @rdesc S_OK on success, MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INSUFFICIENT_BUFFER)
|
|
** if destination buffer is too small
|
|
**
|
|
*************************************************************************************/
|
|
HRESULT CopyW2T
|
|
(
|
|
LPTSTR ptszDest, // @parm pointer to WCHAR destination buffer
|
|
UINT& ruDestSize, // @parm size of dest in WCHAR's
|
|
LPCWSTR pwcszSrc // @parm pointer to NULL terminated source string
|
|
)
|
|
{
|
|
|
|
UINT uSizeRequired;
|
|
HRESULT hr = S_OK;
|
|
|
|
uSizeRequired = wcslen(pwcszSrc)+1; //the one is for a NULL character
|
|
if(ruDestSize < uSizeRequired)
|
|
{
|
|
hr = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// we always return wide, but TCHAR can be WCHAR or char
|
|
// this compile time so use preprocessor
|
|
//
|
|
#ifdef UNICODE
|
|
wcscpy(ptszDest, pwcszSrc);
|
|
#else
|
|
int iRetVal=WideCharToMultiByte
|
|
(
|
|
CP_ACP,
|
|
0,
|
|
pwcszSrc,
|
|
-1,
|
|
ptszDest,
|
|
ruDestSize,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if(0==iRetVal)
|
|
hr=GetLastError();
|
|
#endif //UNICODE
|
|
}
|
|
//Copy size required, or chars copied (same thing)
|
|
ruDestSize = uSizeRequired;
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void CIDirectInputEffectDriver::InitHidInformation(void* HidInformation)
|
|
**
|
|
** @func Open a hid path to the driver, and get preparsed data and hid caps.
|
|
**
|
|
** @rdesc Nothing
|
|
**
|
|
*************************************************************************************/
|
|
void CIDirectInputEffectDriver::InitHidInformation
|
|
(
|
|
LPDIHIDFFINITINFO pHIDInitInfo //@parm [IN] Pointer to structure containing the HID device name
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::InitHidInformation (pHIDInitInfo: 0x%08p)\n", pHIDInitInfo);
|
|
if (pHIDInitInfo != NULL)
|
|
{
|
|
TCHAR ptchHidDeviceName[MAX_PATH];
|
|
unsigned int dwSize = MAX_PATH;
|
|
::CopyW2T(ptchHidDeviceName, dwSize, pHIDInitInfo->pwszDeviceInterface);
|
|
m_hHidDeviceDriver = ::CreateFile(ptchHidDeviceName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
|
if (m_hHidDeviceDriver == INVALID_HANDLE_VALUE)
|
|
{
|
|
m_hHidDeviceDriver = NULL;
|
|
return;
|
|
}
|
|
if (m_pPreparsedData == NULL)
|
|
{
|
|
::HidD_GetPreparsedData(m_hHidDeviceDriver, &m_pPreparsedData);
|
|
if (m_pPreparsedData == NULL)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
::HidP_GetCaps(m_pPreparsedData, &m_HidCaps);
|
|
|
|
// Find VID/PID the USB way!
|
|
::HidD_GetAttributes(m_hHidDeviceDriver, &m_HidAttributes);
|
|
}
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void CIDirectInputEffectDriver::SendSpringChange()
|
|
**
|
|
** @func Sends a new Spring Modify report to the driver
|
|
**
|
|
** @rdesc Nothing
|
|
**
|
|
*************************************************************************************/
|
|
void CIDirectInputEffectDriver::SendSpringChange()
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::SendSpringChange ()\n");
|
|
if ((m_hHidDeviceDriver != NULL) && (m_pPreparsedData != NULL))
|
|
{
|
|
// Setup the spring report
|
|
// 1. Allocate an array of max output size
|
|
BYTE* pbOutReport = new BYTE[m_HidCaps.OutputReportByteLength];
|
|
if (pbOutReport == NULL)
|
|
{
|
|
return;
|
|
}
|
|
// 2. Zero out array
|
|
::memset(pbOutReport, 0, m_HidCaps.OutputReportByteLength);
|
|
// 3. Set the proper report ID
|
|
pbOutReport[0] = c_bSideWinderPIDReportID_SetEffect;
|
|
// 4. Cheat since we know what the firmware is expecting (Use usage Gunk where easy)
|
|
pbOutReport[1] = c_EffectID_RTCSpring; // Effect Block Index (ID)
|
|
unsigned short usRTC = m_ForceMapping.usRTC; // 0 - 10K
|
|
usRTC /= 100; // 0 - 100
|
|
usRTC *= 255; // 0 - 25500
|
|
usRTC /= 100; // 0 - 255
|
|
if (usRTC > 255)
|
|
{
|
|
usRTC = 255;
|
|
}
|
|
pbOutReport[9] = BYTE(usRTC); // Effect Gain - Only item the RTC Spring will look at
|
|
// Now that the firmware has change for Godzilla it looks at bunches o stuff
|
|
pbOutReport[7] = 1; //sample period
|
|
pbOutReport[11] =132; //direction-axis + FW only force polar flag
|
|
pbOutReport[13] = 255; //Y - direction
|
|
|
|
myDebugOut ("CIDirectInputEffectDriver::SendSpringChange -> usRTC:%03d\n", usRTC);
|
|
|
|
// 5. Send the report down
|
|
DWORD dwBytesWritten;
|
|
::WriteFile(m_hHidDeviceDriver, pbOutReport, m_HidCaps.OutputReportByteLength, &dwBytesWritten, NULL);
|
|
|
|
// 6. Deallocate report array
|
|
delete[] pbOutReport;
|
|
}
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void CIDirectInputEffectDriver::SendForceFeedbackCommand()
|
|
**
|
|
** @func Intercepting this call gives us the chance to set the force level of the
|
|
** RTC Spring after a reset
|
|
**
|
|
** @rdesc Result of SendForceFeedbackCommand (from lower driver)
|
|
**
|
|
*************************************************************************************/
|
|
HRESULT __stdcall CIDirectInputEffectDriver::SendForceFeedbackCommand
|
|
(
|
|
DWORD dwDeviceID, //@parm [IN] ID of device this is for
|
|
DWORD dwState //@parm [IN] The command (we are interested in reset)
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::SendForceFeedbackCommand Enter (dwDeviceID:%x, dwState:0x%08p)\n", dwDeviceID, dwState);
|
|
HRESULT hr = m_pIPIDEffectDriver->SendForceFeedbackCommand(dwDeviceID, dwState);
|
|
myDebugOut ("CIDirectInputEffectDriver::SendForceFeedbackCommand Calling Base (hr:0x%08p)\n", hr);
|
|
if (dwState == DISFFC_RESET) // This is how they turn on the RTC Spring
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::SendForceFeedbackCommand RESET sent!\n");
|
|
SendSpringChange();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::GetForceFeedbackState
|
|
(
|
|
DWORD dwDeviceID,
|
|
DIDEVICESTATE* pDeviceState
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::GetForceFeedbackState Begin (dwDeviceID:%d, pDeviceState:0x%08p)\n", dwDeviceID, pDeviceState);
|
|
HRESULT hrPidDriver = S_OK;
|
|
|
|
__try
|
|
{
|
|
hrPidDriver = m_pIPIDEffectDriver->GetForceFeedbackState(dwDeviceID, pDeviceState);
|
|
//
|
|
// Invert safety switch and user FF switch bits to compensate for PID/DI mismatch
|
|
// which caused Fighter Ace II to ignore X/Y axes.
|
|
// Note IF clause which will cause fix to be ignored if DI fixes it
|
|
//
|
|
if (pDeviceState->dwState & DIGFFS_USERFFSWITCHOFF)
|
|
pDeviceState->dwState=pDeviceState->dwState^(DIGFFS_SAFETYSWITCHON|DIGFFS_SAFETYSWITCHOFF|DIGFFS_USERFFSWITCHON|DIGFFS_USERFFSWITCHOFF);
|
|
|
|
}
|
|
__except ((GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
|
|
{
|
|
hrPidDriver = DIERR_INPUTLOST;
|
|
_RPT0(_CRT_WARN, "!!! Caught EXCEPTION_INT_DIVIDE_BY_ZERO !!!\n");
|
|
}
|
|
|
|
myDebugOut ("CIDirectInputEffectDriver::GetForceFeedbackState End (dwDeviceID:%d, pDeviceState:0x%08p; hr: 0x%08x)\n",
|
|
dwDeviceID, pDeviceState, hrPidDriver);
|
|
|
|
return hrPidDriver;
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void PercentagesFromAngle()
|
|
**
|
|
** @func Extrapolate the percentages from the table. Makes use of the fact that
|
|
** sin^2(angle) + cos^2(angle) = 1 and xPercentage + yPercentage = 1
|
|
**
|
|
** @rdesc Result of download (from lower driver)
|
|
**
|
|
*************************************************************************************/
|
|
void PercentagesFromAngle
|
|
(
|
|
DWORD dwAngle, //@parm [IN] Angle to convert to percentages
|
|
LONG& lXPercent, //@parm [OUT] Resultant X Percentage
|
|
LONG& lYPercent //@parm [OUT] Resultant Y Percentage
|
|
)
|
|
{
|
|
// Get the angle mapping into the first quadrant
|
|
DWORD dwMappingAngle = dwAngle; // 0 - 9000
|
|
bool bFlipSignX = false; // X is negative in 3rd and 4th quadrants
|
|
bool bFlipSignY = true; // Y is negative in 1st and 4th quadrants
|
|
if (dwAngle > 9000)
|
|
{
|
|
bFlipSignY = false;
|
|
if (dwAngle > 18000)
|
|
{
|
|
bFlipSignX = true;
|
|
if (dwAngle > 27000) // 27000 - 36000
|
|
{
|
|
bFlipSignY = true;
|
|
dwMappingAngle = 36000 - dwAngle;
|
|
}
|
|
else // 18000 - 27000
|
|
{
|
|
dwMappingAngle = dwAngle - 18000;
|
|
}
|
|
}
|
|
else // 9000 - 18000
|
|
{
|
|
dwMappingAngle = 18000 - dwAngle;
|
|
}
|
|
}
|
|
|
|
_ASSERTE(dwMappingAngle <= 9000);
|
|
|
|
DWORD quantizedEntry = dwMappingAngle / c_dwTableQuantization;
|
|
DWORD quantizedAngle = quantizedEntry * c_dwTableQuantization;
|
|
if (dwMappingAngle == quantizedAngle)
|
|
{
|
|
lXPercent = g_PercentagesArray[quantizedEntry].dwPercentageX;
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(quantizedAngle < dwMappingAngle);
|
|
_ASSERTE(dwMappingAngle < 9000);
|
|
|
|
DWORD lValue = g_PercentagesArray[quantizedEntry].dwPercentageX;
|
|
DWORD rValue = g_PercentagesArray[quantizedEntry + 1].dwPercentageX;
|
|
long int lSlope = ((rValue - lValue) * 1000)/c_dwTableQuantization;
|
|
lXPercent = lValue + lSlope * (dwMappingAngle - quantizedAngle);
|
|
}
|
|
|
|
lYPercent = 10000 - lXPercent;
|
|
if (bFlipSignX == true)
|
|
{
|
|
lXPercent *= -1;
|
|
}
|
|
if (bFlipSignY == true)
|
|
{
|
|
lYPercent *= -1;
|
|
}
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void CIDirectInputEffectDriver::DownloadEffect()
|
|
**
|
|
** @func Intercepting this call gives us the chance to map the Y forces to the X
|
|
** axis. Switches off the type to determine if the mapping is done.
|
|
**
|
|
** @rdesc Result of download (from lower driver)
|
|
**
|
|
*************************************************************************************/
|
|
HRESULT __stdcall CIDirectInputEffectDriver::DownloadEffect
|
|
(
|
|
DWORD dwDeviceID, //@parm [IN] ID of device this is for
|
|
DWORD dwInternalEffectType, //@parm [IN] Type of effect (major, minor)
|
|
DWORD* pdwDnloadID, //@parm [IN, OUT] >0 - ID of effect to modify. 0 new effect ID returned
|
|
DIEFFECT* pEffect, //@parm [IN, OUT] Structure containing effect information
|
|
DWORD dwFlags //@parm [IN] Download flags
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::DownloadEffect (<NOT DEBUGGED>)\n");
|
|
|
|
if (pEffect == NULL)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
WORD wType = WORD(dwInternalEffectType & 0x0000FFFF);
|
|
bool bGainTruncation = false;
|
|
|
|
// case EF_BEHAVIOR: // We don't axis-map behaviour
|
|
// case EF_USER_DEFINED: // We don't axis-map user defined
|
|
// case EF_RTC_SPRING: // We don't axis-map RTC spring
|
|
// case EF_VFX_EFFECT: // Visual force VFX Effect!!! Danger Will Robinson!
|
|
if ((m_ForceMapping.bMapYToX) && ((wType >= PID_CONSTANT_FORCE) && (wType <= PID_SAWTOOTHDOWN)))
|
|
{
|
|
// We don't support more than 2 axes (currently), and 0 is probably an error
|
|
if ((pEffect->cAxes > 2) || (pEffect->cAxes == 0))
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// We don't support sperical (3 axis force)
|
|
if (pEffect->dwFlags & DIEFF_SPHERICAL)
|
|
{
|
|
return E_NOTIMPL; // .. since got by axis check, programmer goofed up anyway
|
|
}
|
|
|
|
// Are the axes reversed?
|
|
bool bAxesReversed = (DIDFT_GETINSTANCE(pEffect->rgdwAxes[0]) == 1);
|
|
|
|
LONG lPercentX = 0;
|
|
LONG lPercentY = 0;
|
|
|
|
// Polar, figure out percentage that is X and percentage that is Y
|
|
if (pEffect->dwFlags & DIEFF_POLAR)
|
|
{
|
|
if (pEffect->cAxes == 1) // Polar coordinate must have two axes of data (because DX says so)
|
|
{
|
|
_RPT0(_CRT_WARN, "POLAR effect that has only one AXIS\n");
|
|
// return E_INVALIDARG;
|
|
}
|
|
long int lEffectAngle = pEffect->rglDirection[0]; // in [0] even if reversed
|
|
if (bAxesReversed == true) { // Indicates (-1, 0) as origin instead of (0, -1)
|
|
lEffectAngle += 27000;
|
|
}
|
|
while (lEffectAngle < 0) // Make it positive
|
|
{
|
|
lEffectAngle += 36000;
|
|
}
|
|
lEffectAngle %= 36000; // Make it from 0 to 35900
|
|
|
|
PercentagesFromAngle(DWORD(lEffectAngle), lPercentX, lPercentY);
|
|
|
|
// Not going to bother reseting the angle, since PID.dll just sends it down and wheel ignores Y component
|
|
}
|
|
else if (pEffect->dwFlags & DIEFF_CARTESIAN)
|
|
{
|
|
// Here I remove the Y component in case PID.dll maps this to an angle.
|
|
if (bAxesReversed == true)
|
|
{
|
|
lPercentX = pEffect->rglDirection[1];
|
|
lPercentY = pEffect->rglDirection[0];
|
|
pEffect->rglDirection[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
lPercentX = pEffect->rglDirection[0];
|
|
lPercentY = pEffect->rglDirection[1];
|
|
pEffect->rglDirection[1] = 0;
|
|
}
|
|
LONG lTotal = abs(lPercentX) + abs(lPercentY);
|
|
// DIV ZERO Bug
|
|
// If both of the percentages are zero then do nothing
|
|
// Jen-Hung Ho
|
|
if (lTotal)
|
|
{
|
|
lPercentX = (lPercentX * 10000)/lTotal;
|
|
if ( lPercentY > 0 )
|
|
lPercentY = 10000 - abs(lPercentX);
|
|
else
|
|
lPercentY = abs(lPercentX) - 10000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_ASSERTE(FALSE);
|
|
return E_NOTIMPL; // Some new fangled coordinate system
|
|
}
|
|
#if 0 // tempory remove by Jen-Hung Ho
|
|
long int lContributionY = lPercentY/c_lContributionY;
|
|
long int lTotal = lPercentX + lContributionY;
|
|
#else
|
|
long int lTotal;
|
|
long int lContributionY = lPercentY/c_lContributionY;
|
|
#endif
|
|
|
|
// If POLAR set proper angle
|
|
if (pEffect->dwFlags & DIEFF_POLAR)
|
|
{
|
|
// Keep as orginal code, add by Jen-Hung Ho
|
|
lTotal = lPercentX + lContributionY;
|
|
if (lTotal < 0)
|
|
{
|
|
pEffect->rglDirection[0] = (bAxesReversed == true) ? 0 : 27000;
|
|
}
|
|
else
|
|
{
|
|
pEffect->rglDirection[0] = (bAxesReversed == true) ? 18000 : 9000;
|
|
}
|
|
}
|
|
else // Cartesian
|
|
{
|
|
// use X axis force to determain direction, add by Jen-Hung Ho
|
|
// Y axis force follow X axis direction
|
|
if ( lPercentX > 0 )
|
|
lTotal = lPercentX + abs(lContributionY);
|
|
else if ( lPercentX < 0 )
|
|
lTotal = lPercentX - abs(lContributionY);
|
|
else
|
|
lTotal = lContributionY;
|
|
|
|
// Already removed Y above
|
|
if (bAxesReversed == true)
|
|
{
|
|
pEffect->rglDirection[1] = lTotal;
|
|
}
|
|
else
|
|
{
|
|
pEffect->rglDirection[0] = lTotal;
|
|
}
|
|
}
|
|
|
|
// Allmost all the time we are changing the angle (and pid always sends it anyways)
|
|
dwFlags |= DIEP_DIRECTION;
|
|
|
|
// We avoid causing truncation - what if there was truncation? Need to check and return
|
|
if (pEffect->dwGain > 10000)
|
|
{
|
|
bGainTruncation = true;
|
|
}
|
|
|
|
if (pEffect->dwFlags & DIEFF_POLAR)
|
|
{
|
|
// Modify the gain based on lPercentX and lPercentY
|
|
pEffect->dwGain = pEffect->dwGain * abs(lTotal);
|
|
pEffect->dwGain /= 10000; // Put back in range 0 - 10000
|
|
}
|
|
|
|
// Make sure we don't go out of range and cause DI_TRUNCATED to be returned from below
|
|
if (pEffect->dwGain > 10000)
|
|
{
|
|
pEffect->dwGain = 10000;
|
|
}
|
|
}
|
|
else // We are not mapping fix cartesian pid bug
|
|
{
|
|
// Cartesian
|
|
if (pEffect->dwFlags & DIEFF_CARTESIAN)
|
|
{
|
|
short int xAxisIndex = 0;
|
|
short int yAxisIndex = 1;
|
|
|
|
// Are the axes reversed?
|
|
if (DIDFT_GETINSTANCE(pEffect->rgdwAxes[0]) == 1)
|
|
{
|
|
xAxisIndex = 1;
|
|
yAxisIndex = 0;
|
|
}
|
|
|
|
LONG lTotal = abs(pEffect->rglDirection[0]) + abs(pEffect->rglDirection[1]);
|
|
|
|
// Fixup the X component so the total maginitude is base on 10K
|
|
if (lTotal)
|
|
{
|
|
pEffect->rglDirection[xAxisIndex] = (10000 * pEffect->rglDirection[xAxisIndex])/lTotal;
|
|
}
|
|
|
|
// Remove the Y component to keep PID.dll from playing with it.
|
|
pEffect->rglDirection[yAxisIndex] = 0;
|
|
}
|
|
}
|
|
|
|
HRESULT hr = m_pIPIDEffectDriver->DownloadEffect(dwDeviceID, dwInternalEffectType, pdwDnloadID, pEffect, dwFlags);
|
|
if ((hr == S_OK) && (bGainTruncation == true))
|
|
{
|
|
hr = DI_TRUNCATED;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::DestroyEffect
|
|
(
|
|
DWORD dwDeviceID,
|
|
DWORD dwDnloadID
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::DestroyEffect Enter(dwDeviceID:%d, dwDnloadID:%d)\n",
|
|
dwDeviceID, dwDnloadID);
|
|
|
|
HRESULT hr = m_pIPIDEffectDriver->DestroyEffect(dwDeviceID, dwDnloadID);
|
|
myDebugOut ("CIDirectInputEffectDriver::DestroyEffect Exit (hr:0x%08p)\n", hr);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::StartEffect
|
|
(
|
|
DWORD dwDeviceID,
|
|
DWORD dwDnloadID,
|
|
DWORD dwMode,
|
|
DWORD dwIterations
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::StartEffect (<NOT DEBUGGED>)\n");
|
|
return m_pIPIDEffectDriver->StartEffect(dwDeviceID, dwDnloadID, dwMode, dwIterations);
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::StopEffect
|
|
(
|
|
DWORD dwDeviceID,
|
|
DWORD dwDnloadID
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::StopEffect (<NOT DEBUGGED>)\n");
|
|
return m_pIPIDEffectDriver->StopEffect(dwDeviceID, dwDnloadID);
|
|
}
|
|
|
|
HRESULT __stdcall CIDirectInputEffectDriver::GetEffectStatus
|
|
(
|
|
DWORD dwDeviceID,
|
|
DWORD dwDnloadID,
|
|
DWORD* pdwStatusCode
|
|
)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver::GetEffectStatus (<NOT DEBUGGED>)\n");
|
|
return m_pIPIDEffectDriver->GetEffectStatus(dwDeviceID, dwDnloadID, pdwStatusCode);
|
|
}
|
|
|
|
DWORD __stdcall DoWaitForForceSchemeChange(void* pParameter)
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver DoWaitForForceSchemeChange (pParameter: 0x%08p)\n", pParameter);
|
|
|
|
CIDirectInputEffectDriver* pIDirectInputEffectDriver = (CIDirectInputEffectDriver*)pParameter;
|
|
//TODO remove this it could be really slow!
|
|
if (IsBadReadPtr ((const void*)pParameter, sizeof CIDirectInputEffectDriver))
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver DoWaitForForceSchemeChange pParameter is not a valid read ptr!\n");
|
|
}
|
|
if (pIDirectInputEffectDriver != NULL)
|
|
{
|
|
pIDirectInputEffectDriver->WaitForForceSchemeChange();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************************
|
|
**
|
|
** void CIDirectInputEffectDriver::WaitForForceSchemeChange()
|
|
**
|
|
** @func Thread waits on the Event signal for force scheme change until the object goes away.
|
|
** If event is signaled, WaitForForceSchemeChange() is called
|
|
**
|
|
** @rdesc Nothing
|
|
**
|
|
*************************************************************************************/
|
|
void CIDirectInputEffectDriver::WaitForForceSchemeChange()
|
|
{
|
|
_ASSERTE(m_hKernelDeviceDriverDuplicate != NULL);
|
|
if (IsBadReadPtr ((const void*)this, sizeof CIDirectInputEffectDriver))
|
|
{
|
|
myDebugOut ("CIDirectInputEffectDriver WaitForForceSchemeChange is not a valid read ptr!\n");
|
|
}
|
|
|
|
FORCE_BLOCK forceMap;
|
|
DWORD dwReturnDataSize = 0;
|
|
for (;m_ulReferenceCount != 0;)
|
|
{
|
|
// Set up the IOCTL
|
|
BOOL bRet = ::DeviceIoControl(m_hKernelDeviceDriverDuplicate, IOCTL_GCK_NOTIFY_FF_SCHEME_CHANGE,
|
|
(void*)(&m_dwGcKernelDevice), sizeof(DWORD), // In
|
|
(void*)(&forceMap), sizeof(forceMap), &dwReturnDataSize, // Out
|
|
NULL);
|
|
_RPT0(_CRT_WARN, "Returned from Scheme Change!\n");
|
|
if ((m_ulReferenceCount != 0) && (bRet != FALSE) && (dwReturnDataSize == sizeof(forceMap)))
|
|
{
|
|
// Need a mutext here
|
|
m_ForceMapping = forceMap;
|
|
SendSpringChange();
|
|
SetGain(m_dwInternalDeviceID, 10000);
|
|
}
|
|
else
|
|
{ // We are done
|
|
::CloseHandle(m_hKernelDeviceDriverDuplicate);
|
|
m_hKernelDeviceDriverDuplicate = NULL;
|
|
ExitThread(2);
|
|
}
|
|
}
|
|
}
|