1555 lines
52 KiB
C++
1555 lines
52 KiB
C++
/****f* Controller.Input.SDK/ControllerInputSDK[1.00.001]
|
|
* NAME
|
|
* Logitech Controller Input SDK
|
|
* COPYRIGHT
|
|
* The Logitech Controller Input SDK, including all accompanying
|
|
* documentation, is protected by intellectual property laws. All
|
|
* rights not expressly granted by Logitech are reserved.
|
|
* PURPOSE
|
|
* The Logitech Controller Input SDK's purpose is to create a
|
|
* paradigm shift. Today users must have their controller(s) plugged
|
|
* in when launching a PC game, and if they forget, they have to know
|
|
* to exit the game, plug in the controller, and start again.
|
|
* The Controller Input SDK enables users to hot plug/unplug
|
|
* any controllers at any time, mimicking the user-friendly behavior
|
|
* of consoles.
|
|
* The SDK provides a simple interface for:
|
|
* - support of both DirectInput and XInput hot plug/unplug.
|
|
* - seamless integration of a total of 4 DInput and XInput
|
|
* controllers.
|
|
* - getting controller positional information as well as general
|
|
* info such as friendly name, VID, PID, connection status
|
|
* based on various parameters such as controller type,
|
|
* manufacturer, and model name, and whether it supports force
|
|
* feedback/rumble.
|
|
* - getting hooks to add force feedback or rumble (DirectInput
|
|
* device interface and XInput ID).
|
|
* EXAMPLE
|
|
* Build and run the sample program to see some of the code usage, or
|
|
* run ControllerInputSDKDemo.exe to quickly see what it can do.
|
|
* AUTHOR
|
|
* Christophe Juncker (cj@wingmanteam.com)
|
|
******
|
|
*/
|
|
|
|
#include "LogiControllerInput.h"
|
|
#include "LogiGameControllerDI.h"
|
|
#include "LogiGameControllerXInput.h"
|
|
#include "LogiControllerInputUtils.h"
|
|
#include <dbt.h>
|
|
|
|
using namespace LogitechControllerInput;
|
|
|
|
LPDIRECTINPUT8 g_DIInterface = NULL;
|
|
LPDIRECTINPUTDEVICE8 g_deviceHandlesLocal[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
DWORD g_numForceFeedbackAxisLocal[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
BOOL g_joystickConnectedLocal[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
BOOL g_wheelConnectedLocal[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
BOOL g_gamepadConnectedLocal[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
STRING g_deviceIDStringLocal[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
|
|
BOOL g_controllerNeedsToBeRemoved[LogitechControllerInput::LG_MAX_CONTROLLERS];
|
|
|
|
BOOL g_ignoreXInputControllers = FALSE;
|
|
|
|
STRING_VECTOR g_uniqueIDsRemoved;
|
|
|
|
DWORD g_appGotActivated = FALSE;
|
|
DWORD g_deviceWasChanged = FALSE;
|
|
BOOL g_gameControllerWasPluggedIn = FALSE;
|
|
|
|
LogiGameController* g_controller[LG_MAX_CONTROLLERS];
|
|
|
|
typedef enum
|
|
{
|
|
TIMER_EXTRA_ADD_CONTROLLERS
|
|
} LG_TIMER;
|
|
|
|
WNDPROC g_OldWnd;
|
|
LRESULT CALLBACK NewWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
|
|
// stolen from the DDK
|
|
#include <InitGuid.h>
|
|
DEFINE_GUID( GUID_DEVINTERFACE_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, \
|
|
0x11, 0x11, 0x00, 0x00, 0x30);
|
|
|
|
/****f* Controller.Input.SDK/ControllerInput(HWND.hwnd,BOOL.ignoreXInputControllers)
|
|
* NAME
|
|
* ControllerInput(HWND hwnd, BOOL ignoreXInputControllers) -- does
|
|
* necessary initialization.
|
|
* INPUTS
|
|
* hwnd - game window handle used to initialize DirectInput.
|
|
*
|
|
* ignoreXInputControllers - if set to TRUE, XInput controllers will be
|
|
* ignored alltogether.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
ControllerInput::ControllerInput(CONST HWND hwnd, CONST BOOL ignoreXInputControllers)
|
|
{
|
|
_ASSERT(NULL != hwnd);
|
|
|
|
m_gameHWnd = hwnd;
|
|
m_hDevNotify = NULL;
|
|
m_intervalTimerHit = FALSE;
|
|
g_DIInterface = NULL;
|
|
g_ignoreXInputControllers = ignoreXInputControllers;
|
|
|
|
CreateDIInterface();
|
|
|
|
//Replace the Window Procedure and Store the Old Window Procedure
|
|
g_OldWnd = (WNDPROC)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_WNDPROC);
|
|
SetWindowLongPtr(hwnd, GWLP_WNDPROC, (__int3264)(LONG_PTR)NewWindowProc);
|
|
|
|
DEV_BROADCAST_DEVICEINTERFACE bcInterface_;
|
|
ZeroMemory(&bcInterface_, sizeof(bcInterface_));
|
|
bcInterface_.dbcc_size = sizeof(bcInterface_);
|
|
bcInterface_.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
|
|
bcInterface_.dbcc_classguid = GUID_DEVINTERFACE_HID;
|
|
|
|
m_hDevNotify = RegisterDeviceNotification(hwnd, (LPVOID)&bcInterface_, DEVICE_NOTIFY_WINDOW_HANDLE);
|
|
if (m_hDevNotify == NULL)
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: RegisterDeviceNotification returned error %d\n"), GetLastError());
|
|
}
|
|
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
g_controller[ii] = NULL;
|
|
g_controllerNeedsToBeRemoved[ii] = FALSE;
|
|
}
|
|
|
|
AddNewControllers(TRUE);
|
|
}
|
|
|
|
ControllerInput::~ControllerInput()
|
|
{
|
|
FreeDirectInput();
|
|
UnregisterDeviceNotification(m_hDevNotify);
|
|
}
|
|
|
|
HRESULT ControllerInput::CreateDIInterface()
|
|
{
|
|
HRESULT hr_;
|
|
// Register with the DirectInput subsystem and get a pointer
|
|
// to a IDirectInput interface we can use.
|
|
// Create a DInput object
|
|
hr_ = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION,
|
|
IID_IDirectInput8, (VOID**)&g_DIInterface, NULL );
|
|
if( FAILED( hr_ ))
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: failed to create a DInput object\n"));
|
|
FreeDirectInput();
|
|
return hr_;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT ControllerInput::AddNewControllers(CONST BOOL calledOnStartup)
|
|
{
|
|
HRESULT hr_ = E_FAIL;
|
|
if (FAILED(hr_ = EnumerateDevices()))
|
|
return hr_;
|
|
|
|
DEVICE_INFO_VECTOR devicesInfo_;
|
|
if (FAILED(hr_ = PopulateDeviceDescriptions(devicesInfo_)))
|
|
return hr_;
|
|
|
|
if (FAILED(hr_ = ReOrderDeviceDescriptions(devicesInfo_)))
|
|
return hr_;
|
|
|
|
if (FAILED(hr_ = CreateNewControllers(devicesInfo_, calledOnStartup)))
|
|
return hr_;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT ControllerInput::EnumerateDevices()
|
|
{
|
|
HRESULT hr_;
|
|
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
g_deviceHandlesLocal[ii] = NULL;
|
|
g_numForceFeedbackAxisLocal[ii] = 0;
|
|
g_joystickConnectedLocal[ii] = FALSE;
|
|
g_wheelConnectedLocal[ii] = FALSE;
|
|
g_gamepadConnectedLocal[ii] = FALSE;
|
|
g_deviceIDStringLocal[ii] = _T("");
|
|
}
|
|
|
|
if (NULL == g_DIInterface)
|
|
return E_FAIL;
|
|
|
|
hr_ = g_DIInterface->EnumDevices( DI8DEVCLASS_GAMECTRL,
|
|
EnumDevicesCallback,
|
|
NULL, DIEDFL_ATTACHEDONLY );
|
|
if( FAILED( hr_ ) )
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: could not enumerate devices\n"));
|
|
return hr_;
|
|
}
|
|
|
|
for (INT ctrlNbr_ = 0; ctrlNbr_ < LogitechControllerInput::LG_MAX_CONTROLLERS; ctrlNbr_++)
|
|
{
|
|
if (NULL != g_deviceHandlesLocal[ctrlNbr_])
|
|
{
|
|
if (FAILED(hr_ = g_deviceHandlesLocal[ctrlNbr_]->EnumObjects( EnumAxesCallback,
|
|
(VOID*)&g_numForceFeedbackAxisLocal[ctrlNbr_], DIDFT_AXIS )))
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: failed to enumerate force feedback axes for device on channel %d\n"), ctrlNbr_);
|
|
return hr_;
|
|
}
|
|
|
|
EnumDeviceObjects(ctrlNbr_);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT ControllerInput::PopulateDeviceDescriptions(DEVICE_INFO_VECTOR& devicesInfo)
|
|
{
|
|
devicesInfo.clear();
|
|
|
|
for (UINT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
if (NULL == g_deviceHandlesLocal[ii])
|
|
continue;
|
|
|
|
DeviceInfo deviceInfo_;
|
|
deviceInfo_.device = g_deviceHandlesLocal[ii];
|
|
deviceInfo_.index = ii;
|
|
deviceInfo_.numFFAxis = g_numForceFeedbackAxisLocal[ii];
|
|
|
|
DIDEVICEINSTANCE instance_;
|
|
ZeroMemory(&instance_, sizeof(DIDEVICEINSTANCE));
|
|
instance_.dwSize = sizeof(DIDEVICEINSTANCE);
|
|
g_deviceHandlesLocal[ii]->GetDeviceInfo(&instance_);
|
|
|
|
DWORD vidPid_ = instance_.guidProduct.Data1;
|
|
deviceInfo_.vid = LOWORD(vidPid_);
|
|
deviceInfo_.pid = HIWORD(vidPid_);
|
|
|
|
// Vista breaks DInput capability of telling whether a device
|
|
// is a gamepad, joystick or wheel. So let's just set it
|
|
// manually for our devices.
|
|
if (VID_LOGITECH == deviceInfo_.vid &&
|
|
(PID_RUMBLEPAD == deviceInfo_.pid
|
|
|| PID_RUMBLEPAD_2 == deviceInfo_.pid
|
|
|| PID_CORDLESS_RUMBLEPAD_2 == deviceInfo_.pid
|
|
|| PID_CORDLESS_GAMEPAD == deviceInfo_.pid
|
|
|| PID_DUAL_ACTION_GAMEPAD == deviceInfo_.pid
|
|
|| PID_PRECISION_GAMEPAD_2 == deviceInfo_.pid
|
|
|| PID_CHILLSTREAM == deviceInfo_.pid))
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_GAMEPAD;
|
|
}
|
|
else if (VID_LOGITECH == deviceInfo_.vid &&
|
|
(PID_FORCE_3D_PRO == deviceInfo_.pid
|
|
|| PID_EXTREME_3D_PRO == deviceInfo_.pid
|
|
|| PID_FREEDOM_24 == deviceInfo_.pid
|
|
|| PID_ATTACK_3 == deviceInfo_.pid
|
|
|| PID_FORCE_3D == deviceInfo_.pid
|
|
|| PID_STRIKE_FORCE_3D == deviceInfo_.pid
|
|
|| PID_G940_JOYSTICK == deviceInfo_.pid))
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_JOYSTICK;
|
|
}
|
|
else if (VID_LOGITECH == deviceInfo_.vid &&
|
|
(PID_G940_THROTTLE == deviceInfo_.pid
|
|
|| PID_G940_PEDALS == deviceInfo_.pid))
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_OTHER;
|
|
}
|
|
else if (VID_LOGITECH == deviceInfo_.vid &&
|
|
(PID_G27 == deviceInfo_.pid
|
|
|| PID_DRIVING_FORCE_GT == deviceInfo_.pid
|
|
|| PID_G25 == deviceInfo_.pid
|
|
|| PID_MOMO_RACING == deviceInfo_.pid
|
|
|| PID_MOMO_FORCE == deviceInfo_.pid
|
|
|| PID_DRIVING_FORCE_PRO == deviceInfo_.pid
|
|
|| PID_DRIVING_FORCE == deviceInfo_.pid
|
|
|| PID_NASCAR_RACING_WHEEL == deviceInfo_.pid
|
|
|| PID_DRIVING_FORCE == deviceInfo_.pid
|
|
|| PID_FORMULA_FORCE == deviceInfo_.pid
|
|
|| PID_FORMULA_FORCE_GP == deviceInfo_.pid))
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_WHEEL;
|
|
}
|
|
else
|
|
{
|
|
if (g_gamepadConnectedLocal[ii])
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_GAMEPAD;
|
|
}
|
|
else if(g_joystickConnectedLocal[ii])
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_JOYSTICK;
|
|
}
|
|
else if(g_wheelConnectedLocal[ii])
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_WHEEL;
|
|
}
|
|
else
|
|
{
|
|
deviceInfo_.deviceType = LG_DEVICE_TYPE_OTHER;
|
|
}
|
|
}
|
|
|
|
deviceInfo_.friendlyName = instance_.tszProductName;
|
|
|
|
deviceInfo_.deviceIDString = g_deviceIDStringLocal[ii];
|
|
|
|
deviceInfo_.IG_nbr = Utils::Instance()->FindIG_Number(deviceInfo_.deviceIDString);
|
|
|
|
if (-1 != deviceInfo_.IG_nbr)
|
|
{
|
|
deviceInfo_.isXinput = TRUE;
|
|
}
|
|
|
|
deviceInfo_.uniqueID = Utils::Instance()->GetUniqueID(deviceInfo_.deviceIDString);
|
|
|
|
devicesInfo.push_back(deviceInfo_);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// When game starts and multiple XInput devices are plugged in, the
|
|
// enumeration order is likely not equal to the XInput ID. To get
|
|
// around this problem, we change the order of the enumeration as follows:
|
|
// - Microsoft XInput devices come first, in order of their IG_ number
|
|
// - other XInput devices come next, in order of their IG_ number
|
|
// - DInput devices come next, in order of their enumeration.
|
|
// - If there are multiple XInput devices with same Vid/Pid and successive
|
|
// ID, they don't need to be re-ordered.
|
|
HRESULT ControllerInput::ReOrderDeviceDescriptions(DEVICE_INFO_VECTOR& devicesInfo)
|
|
{
|
|
if (0 == devicesInfo.size())
|
|
return S_OK;
|
|
|
|
// foreach device
|
|
|
|
// Check to see if there are any Microsoft XInput devices. If so, order them by IG_number
|
|
// Check to see if there are other XInput devices. If so, order them by IG_ number.
|
|
// Check for DInput devices. keep them in enumeration order.
|
|
|
|
DEVICE_INFO_MAP xinputMicrosoftDevices_;
|
|
DEVICE_INFO_MAP xinputNonMicrosoftDevices_;
|
|
DEVICE_INFO_MAP dinputDevices_;
|
|
for (UINT index_ = 0; index_ < devicesInfo.size(); index_++)
|
|
{
|
|
if (devicesInfo[index_].isXinput)
|
|
{
|
|
if (devicesInfo[index_].vid == VID_MICROSOFT)
|
|
{
|
|
xinputMicrosoftDevices_[devicesInfo[index_].IG_nbr] = devicesInfo[index_];
|
|
}
|
|
else
|
|
{
|
|
xinputNonMicrosoftDevices_[devicesInfo[index_].IG_nbr] = devicesInfo[index_];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dinputDevices_[index_] = devicesInfo[index_];
|
|
}
|
|
}
|
|
|
|
// rebuild devicesInfo
|
|
devicesInfo.clear();
|
|
|
|
for(DEVICE_INFO_MAP::const_iterator it_ = xinputMicrosoftDevices_.begin(); it_ != xinputMicrosoftDevices_.end(); ++it_)
|
|
{
|
|
devicesInfo.push_back(it_->second);
|
|
}
|
|
|
|
for(DEVICE_INFO_MAP::const_iterator it_ = xinputNonMicrosoftDevices_.begin(); it_ != xinputNonMicrosoftDevices_.end(); ++it_)
|
|
{
|
|
devicesInfo.push_back(it_->second);
|
|
}
|
|
|
|
for(DEVICE_INFO_MAP::const_iterator it_ = dinputDevices_.begin(); it_ != dinputDevices_.end(); ++it_)
|
|
{
|
|
devicesInfo.push_back(it_->second);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT ControllerInput::CreateNewControllers(CONST DEVICE_INFO_VECTOR& devicesInfo, CONST BOOL calledOnStartup)
|
|
{
|
|
HRESULT hr_;
|
|
INT counterXInputViaGetState_ = 0;
|
|
|
|
for (UINT ll = 0; ll < devicesInfo.size(); ll++)
|
|
{
|
|
// if device already exists, ignore.
|
|
if (IsAlreadyPresent(devicesInfo[ll]))
|
|
continue;
|
|
|
|
if (NULL == devicesInfo[ll].device)
|
|
return E_FAIL;
|
|
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
if (NULL == g_controller[ii])
|
|
{
|
|
if (devicesInfo[ll].isXinput)
|
|
{
|
|
g_controller[ii] = new LogiGameControllerXInput(ii, m_gameHWnd);
|
|
|
|
if (NULL == g_controller[ii])
|
|
return E_FAIL;
|
|
|
|
if (calledOnStartup)
|
|
{
|
|
INT localCounter_ = 0;
|
|
// For first device, let's set ID for first device we find via XInputGetState. For the second device, the second found ID, and so on.
|
|
for (INT xinputCount_ = 0; xinputCount_ < LG_NBR_XINPUT_CONTROLLERS; xinputCount_++)
|
|
{
|
|
XINPUT_STATE tempState;
|
|
DWORD dwResult_ = XInputGetState( xinputCount_, &tempState );
|
|
if (ERROR_DEVICE_NOT_CONNECTED != dwResult_)
|
|
{
|
|
++localCounter_;
|
|
if (localCounter_ - 1 == counterXInputViaGetState_)
|
|
{
|
|
g_controller[ii]->SetDeviceXInputID(xinputCount_);
|
|
++counterXInputViaGetState_;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
INT_VECTOR existingXInputDevicesID_;
|
|
// Find IDs of all exisitng XInput devices
|
|
for (INT jj = 0; jj < LogitechControllerInput::LG_MAX_CONTROLLERS; jj++)
|
|
{
|
|
if (ii != jj)
|
|
{
|
|
if (NULL != g_controller[jj])
|
|
{
|
|
if (g_controller[jj]->IsXInputDevice())
|
|
{
|
|
existingXInputDevicesID_.push_back(g_controller[jj]->GetDeviceXInputID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Since we know that the lowest available ID is the one to get assigned, search for it
|
|
for (INT kk = 0; kk < LG_NBR_XINPUT_CONTROLLERS; kk++)
|
|
{
|
|
BOOL foundSameID_ = FALSE;
|
|
for (UINT jj = 0; jj < existingXInputDevicesID_.size(); jj++)
|
|
{
|
|
if (kk == existingXInputDevicesID_[jj])
|
|
{
|
|
foundSameID_ = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!foundSameID_)
|
|
{
|
|
g_controller[ii]->SetDeviceXInputID(kk);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_controller[ii] = new LogiGameControllerDI(ii, m_gameHWnd);
|
|
|
|
if (NULL == g_controller[ii])
|
|
return E_FAIL;
|
|
|
|
g_controller[ii]->SetNumberFFAxes(devicesInfo[ll].numFFAxis);
|
|
|
|
hr_ = devicesInfo[ll].device->SetDataFormat( &c_dfDIJoystick2 );
|
|
if( FAILED( hr_ ) )
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: couldn't set data format for gaming device on channel %d\n"), ii);
|
|
return hr_;
|
|
}
|
|
|
|
// Set the cooperative level to let DInput know how this device should
|
|
// interact with the system and with other DInput applications.
|
|
hr_ = devicesInfo[ll].device->SetCooperativeLevel( m_gameHWnd, DISCL_EXCLUSIVE |
|
|
DISCL_FOREGROUND );
|
|
if( FAILED( hr_ ) )
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: couldn't set cooperative level for gaming device on channel %d\n"), ii);
|
|
return hr_;
|
|
}
|
|
}
|
|
g_controller[ii]->SetDeviceHandle(devicesInfo[ll].device);
|
|
|
|
g_controller[ii]->SetVid(devicesInfo[ll].vid);
|
|
g_controller[ii]->SetPid(devicesInfo[ll].pid);
|
|
g_controller[ii]->SetFriendlyProductName(devicesInfo[ll].friendlyName.c_str());
|
|
|
|
g_controller[ii]->SetDeviceType(devicesInfo[ll].deviceType);
|
|
|
|
g_controller[ii]->SetDeviceUniqueID(devicesInfo[ll].uniqueID.c_str());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL ControllerInput::IsAlreadyPresent(DeviceInfo deviceInfo)
|
|
{
|
|
// First check if unique ID already exists.
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
if (NULL != g_controller[ii])
|
|
{
|
|
if (0 == _tcscmp(g_controller[ii]->GetDeviceUniqueID(), deviceInfo.uniqueID.c_str()))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CALLBACK ControllerInput::EnumDevicesCallback(CONST DIDEVICEINSTANCE* pDevInst, VOID* pContext)
|
|
{
|
|
UNREFERENCED_PARAMETER(pContext);
|
|
if (NULL == pDevInst || NULL == g_DIInterface)
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: EnumDevicesCallback: trying to use NULL pointer\n"));
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
DWORD vidPid_ = pDevInst->guidProduct.Data1;
|
|
|
|
// Get unique ID
|
|
STRING_VECTOR deviceIDString_;
|
|
Utils::Instance()->GetDeviceIDStringFromSetupDI(deviceIDString_, LOWORD(vidPid_), HIWORD(vidPid_));
|
|
|
|
if (g_ignoreXInputControllers)
|
|
{
|
|
if (-1 != Utils::Instance()->FindIG_Number(deviceIDString_[0]))
|
|
{
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
}
|
|
|
|
// count how many instances of the same device we already found
|
|
INT deviceCounter_ = 0;
|
|
for (INT index_ = 0; index_ < LogitechControllerInput::LG_MAX_CONTROLLERS; index_++)
|
|
{
|
|
DWORD vid_ = 0;
|
|
DWORD pid_ = 0;
|
|
if (SUCCEEDED(Utils::Instance()->GetVidPid(g_deviceIDStringLocal[index_], vid_, pid_)))
|
|
{
|
|
if (vid_ == LOWORD(vidPid_) && pid_ == HIWORD(vidPid_))
|
|
{
|
|
++deviceCounter_;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
// Obtain an interface to the enumerated wheels.
|
|
if (g_deviceHandlesLocal[ii] == NULL)
|
|
{
|
|
// If it failed, then we can't use this FF device. (Maybe the user unplugged
|
|
// it while we were in the middle of enumerating it.)
|
|
if( FAILED(g_DIInterface->CreateDevice( pDevInst->guidInstance, &g_deviceHandlesLocal[ii], NULL )))
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: Failed to obtain an interface for the enumerated FF device %d.\n"), ii);
|
|
return DIENUM_CONTINUE;
|
|
} else
|
|
{
|
|
// check if we have a wheel or a joystick
|
|
switch (GET_DIDEVICE_TYPE(pDevInst->dwDevType))
|
|
{
|
|
case DI8DEVTYPE_DRIVING:
|
|
g_wheelConnectedLocal[ii] = TRUE;
|
|
break;
|
|
case DI8DEVTYPE_JOYSTICK:
|
|
g_joystickConnectedLocal[ii] = TRUE;
|
|
break;
|
|
case DI8DEVTYPE_GAMEPAD:
|
|
g_gamepadConnectedLocal[ii] = TRUE;
|
|
break;
|
|
}
|
|
|
|
_ASSERT(deviceCounter_ < static_cast<INT>(deviceIDString_.size()));
|
|
g_deviceIDStringLocal[ii] = deviceIDString_[deviceCounter_];
|
|
STRING uniqueID_ = Utils::Instance()->GetUniqueID(deviceIDString_[deviceCounter_]);
|
|
|
|
if (NULL == g_deviceHandlesLocal[ii])
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: EnumDevicesCallback: g_deviceHandlesLocal[%d] is NULL\n"), ii);
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
// Disable centering spring, but only for devices that aren't already present
|
|
BOOL deviceAlreadyPresent_ = FALSE;
|
|
for (INT index_ = 0; index_ < LogitechControllerInput::LG_MAX_CONTROLLERS; index_++)
|
|
{
|
|
if (NULL != g_controller[index_])
|
|
{
|
|
if (0 == _tcscmp(uniqueID_.c_str(), g_controller[index_]->GetDeviceUniqueID()))
|
|
{
|
|
deviceAlreadyPresent_ = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!deviceAlreadyPresent_)
|
|
{
|
|
DIPROPDWORD DIPropAutoCenter_;
|
|
DIPropAutoCenter_.diph.dwSize = sizeof(DIPROPDWORD);
|
|
DIPropAutoCenter_.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
DIPropAutoCenter_.diph.dwObj = 0;
|
|
DIPropAutoCenter_.diph.dwHow = DIPH_DEVICE;
|
|
DIPropAutoCenter_.dwData = DIPROPAUTOCENTER_OFF;
|
|
if( FAILED( g_deviceHandlesLocal[ii]->SetProperty(DIPROP_AUTOCENTER,
|
|
&DIPropAutoCenter_.diph) ) )
|
|
{
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: EnumAxesCallback()
|
|
// Desc: Callback function for enumerating the axes on a joystick and counting
|
|
// each force feedback enabled axis
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CALLBACK ControllerInput::EnumAxesCallback( CONST DIDEVICEOBJECTINSTANCE* pdidoi,
|
|
VOID* pContext )
|
|
{
|
|
DWORD* pdwNumForceFeedbackAxis = (DWORD*) pContext;
|
|
|
|
if( (pdidoi->dwFlags & DIDOI_FFACTUATOR) != 0 )
|
|
(*pdwNumForceFeedbackAxis)++;
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
HRESULT ControllerInput::EnumDeviceObjects(INT index)
|
|
{
|
|
HRESULT hr_ = NULL;
|
|
// Enumerate the wheel objects. The callback function enabled user
|
|
// interface elements for objects that are found, and sets the
|
|
// min/max values property for discovere66d axes.
|
|
if (g_deviceHandlesLocal[index] != NULL)
|
|
{
|
|
if( FAILED(hr_ = g_deviceHandlesLocal[index]->EnumObjects( EnumObjectsCallback,
|
|
(VOID*)g_deviceHandlesLocal[index], DIDFT_ALL )))
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: couldn't enumerate objects for gaming device on channel %d\n"), index);
|
|
return hr_;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
BOOL ControllerInput::EnumObjectsCB(CONST DIDEVICEOBJECTINSTANCE* pdidoi, LPVOID pvRef)
|
|
{
|
|
HRESULT hr_;
|
|
LPDIRECTINPUTDEVICE8 WheelHandle_ = (LPDIRECTINPUTDEVICE8) pvRef;
|
|
|
|
if (NULL != WheelHandle_ && NULL != pdidoi)
|
|
{
|
|
// For axes that are returned, set the DIPROP_RANGE property for the
|
|
// enumerated axis in order to scale min/max values.
|
|
if( pdidoi->dwType & DIDFT_AXIS )
|
|
{
|
|
DIPROPRANGE diprg;
|
|
diprg.diph.dwSize = sizeof(DIPROPRANGE);
|
|
diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
|
|
diprg.diph.dwHow = DIPH_BYID;
|
|
diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis
|
|
diprg.lMin = LG_DINPUT_RANGE_MIN;
|
|
diprg.lMax = LG_DINPUT_RANGE_MAX;
|
|
|
|
// Set the range for the axis
|
|
if( FAILED(hr_ = WheelHandle_->SetProperty( DIPROP_RANGE, &diprg.diph )))
|
|
{
|
|
LOGICONTROLLERTRACE(_T("ERROR: SetProperty failed\n"));
|
|
return DIENUM_STOP;
|
|
}
|
|
}
|
|
}
|
|
|
|
return DIENUM_CONTINUE;
|
|
}
|
|
|
|
BOOL FAR PASCAL ControllerInput::EnumObjectsCallback(CONST DIDEVICEOBJECTINSTANCE* pdidoi, LPVOID pvRef)
|
|
{
|
|
ControllerInput *pThis_ = (ControllerInput *) pvRef;
|
|
if(NULL != pThis_)
|
|
return pThis_->EnumObjectsCB(pdidoi, pvRef);
|
|
return DIENUM_STOP;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/Update()
|
|
* NAME
|
|
* VOID Update() -- update controller status (connected or
|
|
* disconnected) and read each controller's positional information.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
VOID ControllerInput::Update()
|
|
{
|
|
DWORD currentTicks_ = GetTickCount();
|
|
|
|
if (g_gameControllerWasPluggedIn)
|
|
{
|
|
g_gameControllerWasPluggedIn = FALSE;
|
|
|
|
INT currentNumberControllersConnected_ = GetCurrentNumberControllersConnected();
|
|
AddNewControllers(FALSE);
|
|
|
|
// If number of controllers after AddNewControllers is the
|
|
// same than before, it could be because device was plugged
|
|
// into a USB for first time and didn't get enumerated on
|
|
// first try. So in that case, let's try again regularly
|
|
// for a few seconds.
|
|
if (currentNumberControllersConnected_ == GetCurrentNumberControllersConnected())
|
|
{
|
|
Utils::Instance()->SetRecurringTimer(TIMER_EXTRA_ADD_CONTROLLERS, currentTicks_, currentTicks_ + LG_MAX_TIME_RESET_OR_ADD_CONTROLLERS, LG_TIME_INTERVAL_RESET_OR_ADD_CONTROLLERS);
|
|
}
|
|
}
|
|
|
|
if (Utils::Instance()->TimerTriggered(TIMER_EXTRA_ADD_CONTROLLERS))
|
|
{
|
|
AddNewControllers(FALSE);
|
|
}
|
|
|
|
// Remove controllers that need removing
|
|
for (UINT index_ = 0; index_ < g_uniqueIDsRemoved.size(); index_++)
|
|
{
|
|
g_uniqueIDsRemoved[index_] = Utils::Instance()->StringToUpper(g_uniqueIDsRemoved[index_]);
|
|
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
if (NULL != g_controller[ii])
|
|
{
|
|
STRING localUniqueID_(g_controller[ii]->GetDeviceUniqueID());
|
|
localUniqueID_ = Utils::Instance()->StringToUpper(localUniqueID_);
|
|
if (0 == _tcscmp(g_uniqueIDsRemoved[index_].c_str(), localUniqueID_.c_str()))
|
|
{
|
|
RemoveController(ii);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// re-init our vector containing IDs to be removed
|
|
g_uniqueIDsRemoved.clear();
|
|
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
if (NULL != g_controller[ii])
|
|
{
|
|
g_controller[ii]->Read();
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL ControllerInput::ControllerExists(CONST INT index)
|
|
{
|
|
if (0 <= index && index < LogitechControllerInput::LG_MAX_CONTROLLERS)
|
|
{
|
|
if (NULL != g_controller[index])
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetStateDInput(INT.index)
|
|
* NAME
|
|
* DIJOYSTATE2* GetStateDInput(INT index) -- get a DirectInput
|
|
* controller's positional information.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* DIJOYSTATE2 pointer to the most recent information obtained through
|
|
* Update() if successful.
|
|
* NULL otherwise.
|
|
* SEE ALSO
|
|
* Update()
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
DIJOYSTATE2* ControllerInput::GetStateDInput(CONST INT index)
|
|
{
|
|
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetStateDInput();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetStateXInput(INT.index)
|
|
* NAME
|
|
* XINPUT_STATE* GetStateXInput(INT index) -- get a XInput
|
|
* controller's positional information.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* XINPUT_STATE pointer to the most recent information obtained
|
|
* through Update() if successful.
|
|
* NULL otherwise.
|
|
* SEE ALSO
|
|
* Update()
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
XINPUT_STATE* ControllerInput::GetStateXInput(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetStateXInput();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetFriendlyProductName(INT.index)
|
|
* NAME
|
|
* LPCTSTR GetFriendlyProductName(INT index) -- get the friendly
|
|
* name of a controller (as found in the USB Device Descriptor).
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* String containing friendly name if successful.
|
|
* Empy string otherwise.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
LPCTSTR ControllerInput::GetFriendlyProductName(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetFriendlyProductName();
|
|
}
|
|
|
|
return _T("");
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/IsConnected(INT.index)
|
|
* NAME
|
|
* BOOL IsConnected(INT index) -- check if specified game controller
|
|
* is currently connected.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* TRUE if connected.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* IsConnected(INT.index,DeviceType.deviceType)
|
|
* IsConnected(INT.index,ManufacturerName.manufacturerName)
|
|
* IsConnected(INT.index,ModelName.modelName)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::IsConnected(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/IsConnected(INT.index,DeviceType.deviceType)
|
|
* NAME
|
|
* BOOL IsConnected(INT index, DeviceType deviceType) -- check if
|
|
* specified game controller is currently connected.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* deviceType - type of the device. Possible types are:
|
|
* - LG_DEVICE_TYPE_WHEEL
|
|
* - LG_DEVICE_TYPE_JOYSTICK
|
|
* - DEVICE_TYPE_GAMEPAD
|
|
* - LG_DEVICE_TYPE_OTHER
|
|
* RETURN VALUE
|
|
* TRUE if device is connected.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* IsConnected(INT.index)
|
|
* IsConnected(INT.index,ManufacturerName.manufacturerName)
|
|
* IsConnected(INT.index,ModelName.modelName)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::IsConnected(CONST INT index, CONST DeviceType deviceType)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->IsConnected(deviceType);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/IsConnected(INT.index,ManufacturerName.manufacturerName)
|
|
* NAME
|
|
* BOOL IsConnected(INT index, ManufacturerName manufacturerName) --
|
|
* check if specified game controller is currently connected.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* manufacturerName - name of the manufacturer of the device. Possible
|
|
* names are:
|
|
* - LG_MANUFACTURER_LOGITECH
|
|
* - LG_MANUFACTURER_MICROSOFT
|
|
* - LG_MANUFACTURER_OTHER
|
|
* RETURN VALUE
|
|
* TRUE if device is connected.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* IsConnected(INT.index)
|
|
* IsConnected(INT.index,DeviceType.deviceType)
|
|
* IsConnected(INT.index,ModelName.modelName)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::IsConnected(CONST INT index, CONST ManufacturerName manufacturerName)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->IsConnected(manufacturerName);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/IsConnected(INT.index,ModelName.modelName)
|
|
* NAME
|
|
* BOOL IsConnected(INT index, ModelName modelName) -- check if
|
|
* specified game controller is currently connected.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* modelName - model name of the device. Possible names are:
|
|
* - LG_MODEL_G27
|
|
* - LG_MODEL_DRIVING_FORCE_GT
|
|
* - LG_MODEL_G25
|
|
* - LG_MODEL_MOMO_RACING
|
|
* - LG_MODEL_MOMO_FORCE
|
|
* - LG_MODEL_DRIVING_FORCE_PRO
|
|
* - LG_MODEL_DRIVING_FORCE
|
|
* - LG_MODEL_NASCAR_RACING_WHEEL
|
|
* - LG_MODEL_FORMULA_FORCE
|
|
* - LG_MODEL_FORMULA_FORCE_GP
|
|
* - LG_MODEL_FORCE_3D_PRO
|
|
* - LG_MODEL_EXTREME_3D_PRO
|
|
* - LG_MODEL_FREEDOM_24
|
|
* - LG_MODEL_ATTACK_3
|
|
* - LG_MODEL_FORCE_3D
|
|
* - LG_MODEL_STRIKE_FORCE_3D
|
|
* - LG_MODEL_G940_JOYSTICK
|
|
* - LG_MODEL_G940_THROTTLE
|
|
* - LG_MODEL_G940_PEDALS
|
|
* - LG_MODEL_RUMBLEPAD
|
|
* - LG_MODEL_RUMBLEPAD_2
|
|
* - LG_MODEL_CORDLESS_RUMBLEPAD_2
|
|
* - LG_MODEL_CORDLESS_GAMEPAD
|
|
* - LG_MODEL_DUAL_ACTION_GAMEPAD
|
|
* - LG_MODEL_PRECISION_GAMEPAD_2
|
|
* - LG_MODEL_CHILLSTREAM
|
|
* RETURN VALUE
|
|
* TRUE if device is connected.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* IsConnected(INT.index)
|
|
* IsConnected(INT.index,DeviceType.deviceType)
|
|
* IsConnected(INT.index,ManufacturerName.manufacturerName)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::IsConnected(CONST INT index, CONST ModelName modelName)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->IsConnected(modelName);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/IsXInputDevice(INT.index)
|
|
* NAME
|
|
* BOOL IsXInputDevice(INT index) -- check if the controller is a
|
|
* XInput device or a DirectInput device.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* TRUE if controller is a XInput device.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::IsXInputDevice(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->IsXInputDevice();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/HasForceFeedback(INT.index)
|
|
* NAME
|
|
* BOOL HasForceFeedback(INT index) -- check if the controller can
|
|
* do force feedback or rumble.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* TRUE if controller is force feedback/rumble capable.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::HasForceFeedback(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->HasForceFeedback();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetVendorID(INT.index)
|
|
* NAME
|
|
* DWORD GetVendorID(INT index) -- Get controller's Vendor ID.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* Vendor ID if successful.
|
|
* 0 otherwise.
|
|
* SEE ALSO
|
|
* GetProductID(INT.index)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
DWORD ControllerInput::GetVendorID(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetVid();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetProductID(INT.index)
|
|
* NAME
|
|
* DWORD GetProductID(INT index) -- Get controller's Product ID.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* Product ID if successful.
|
|
* 0 otherwise.
|
|
* SEE ALSO
|
|
* GetVendorID(INT.index)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
DWORD ControllerInput::GetProductID(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetPid();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetDeviceHandle(INT.index)
|
|
* NAME
|
|
* LPDIRECTINPUTDEVICE8 GetDeviceHandle(INT index) -- Get handle to
|
|
* controller's corresponding DirectInput device. This handle can be
|
|
* used to do force feedback.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* Device handle if successful.
|
|
* NULL otherwise.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
LPDIRECTINPUTDEVICE8 ControllerInput::GetDeviceHandle(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetDeviceHandle();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetDeviceXinputID(INT.index)
|
|
* NAME
|
|
* INT GetDeviceXinputID(INT index) -- Get ID of XInput
|
|
* controller. This ID can be used as the dwUserIndex parameter to use
|
|
* any of the XInput functions.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* XInput ID if successful ( 0 to 3).
|
|
* LG_XINPUT_ID_NONE otherwise.
|
|
* SEE ALSO
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
INT ControllerInput::GetDeviceXInputID(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetDeviceXInputID();
|
|
}
|
|
|
|
return LG_XINPUT_ID_NONE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GenerateNonLinearValues(INT.index,INT.nonLinCoeff)
|
|
* NAME
|
|
* HRESULT GenerateNonLinearValues(int index, int nonLinCoeff) --
|
|
* Generate non-linear values for the game controller's axis.
|
|
* FUNCTION
|
|
* Gaming wheels/joysticks/game pads have very different behavior from
|
|
* real steering wheels. The reason for single-turn wheels is that
|
|
* they only do up to three quarters of a turn lock to lock, compared
|
|
* to about 3 turns for a real car.
|
|
* This directly affects the steering ratio (15:1 to 20:1 for a real
|
|
* car, but only 4:1 for a gaming wheel!). Joysticks and game pads
|
|
* have a much shorter range of movement than a real steering wheel as
|
|
* well.
|
|
* Because of this very short steering ratio or short range, the
|
|
* gaming wheel/joystick/game pad will feel highly sensitive which may
|
|
* make game play very difficult.
|
|
* Especially it may be difficult to drive in a straight line at speed
|
|
* (tendency to swerve back and forth).
|
|
* One way to get around this problem is to use a sensitivity
|
|
* curve. This is a curve that defines the sensitivity of the game
|
|
* controller depending on speed. This type of curve is usually used
|
|
* for game pads to make up for their low physical range. The result
|
|
* of applying such a curve is that at high speed the car's wheels
|
|
* will physically turn less than if the car is moving very slowly.
|
|
* For example the car's wheels may turn 60 degrees lock to lock at
|
|
* low speed but only 10 degrees lock to lock at higher speeds. If
|
|
* you calculate the resulting steering ratio for 10 degrees lock to
|
|
* lock you find that if you use a steering wheel that turns 180
|
|
* degrees lock to lock the ratio is equal to 180/10 = 18, which
|
|
* corresponds to a real car's steering ratio.
|
|
* If the sensitivity curve has been implemented for the
|
|
* wheel/joystick, adding a non-linear curve probably is not
|
|
* necessary. But you may find that even after applying a sensitivity
|
|
* curve, the car still feels a little twitchy on a straight line when
|
|
* driving fast. This may be because in your game you need more than
|
|
* 10 degrees lock to lock even at high speeds. Or maybe the car is
|
|
* moving at very high speeds where even a normal steering ratio is
|
|
* not good enough to eliminate high sensitivity.
|
|
* The best way at this point is to add a non-linear curve on top of
|
|
* the sensitivity curve.
|
|
* The effect of the non-linear curve with positive nonLinCoeff is
|
|
* that around center position the wheel/joystick will be less
|
|
* sensitive. Yet at locked position left or right the car's wheels
|
|
* will turn the same amount of degrees as without the non-linear
|
|
* response curve. Therefore the car will become more controllable on
|
|
* a straight line and game-play will be improved.
|
|
* There can sometimes be cases where the wheel does not feel
|
|
* sensitive enough. In that case it is possible to add a non-linear
|
|
* curve with the inverse effect (makes the steering more sensitive
|
|
* around center position) by using negative values for
|
|
* nonLinCoeff. This method lets you define a non-linearity
|
|
* coefficient which will determine how strongly non-linear the curve
|
|
* will be. When running the method it will generate a mapping table
|
|
* in the form of an array. For each of the 1024 entries in this array
|
|
* there will be a corresponding non-linear value which can be used as
|
|
* the wheel/joystick's axis position instead of the original
|
|
* value. See Sample_In-game_Implementation.cs for an example.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* nonLinCoeff - value representing how much non-linearity should be
|
|
* applied. Range is -100 to 100. 0 = linear curve, 100 = maximum
|
|
* non-linear curve with less sensitivity around center, -100 =
|
|
* maximum non-linearity with more sensitivity around center position.
|
|
* SEE ALSO
|
|
* GetNonLinearValue(INT.index,INT.inputValue)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
HRESULT ControllerInput::GenerateNonLinearValues(CONST INT index, CONST INT nonLinCoeff)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
g_controller[index]->GenerateNonLinearValues(nonLinCoeff);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetNonLinearValue(INT.index,INT.inputValue)
|
|
* NAME
|
|
* INT GetNonLinearValue(INT index, INT inputValue) -- Get a
|
|
* non-linear value from a table previously generated. This can be
|
|
* used for the response of a steering wheel.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* inputValue - value between LG_DINPUT_RANGE_MIN and
|
|
* LG_DINPUT_RANGE_MAX corresponding to original value of an axis.
|
|
* RETURN VALUE
|
|
* Value between LG_DINPUT_RANGE_MIN and LG_DINPUT_RANGE_MAX
|
|
* corresponding to the level of non-linearity previously set with
|
|
* GenerateNonLinearValues(...).
|
|
* SEE ALSO
|
|
* GenerateNonLinearValues(INT.index,INT.nonLinCoeff)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
INT ControllerInput::GetNonLinearValue(CONST INT index, CONST INT inputValue)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetNonLinearValue(inputValue);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/ButtonIsPressed(INT.index,INT.buttonOrMask)
|
|
* NAME
|
|
* BOOL ButtonIsPressed(CONST INT index, CONST INT buttonOrMask) --
|
|
* check if the button is currently pressed.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* buttonOrMask - button number between 0 and 127 for DInput, one of
|
|
* the following bitmasks for XInput: XINPUT_GAMEPAD_DPAD_UP,
|
|
* XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT,
|
|
* XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_START,
|
|
* XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB,
|
|
* XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_LEFT_SHOULDER,
|
|
* XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B,
|
|
* XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y
|
|
* RETURN VALUE
|
|
* TRUE if buttons is currently pressed.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* ButtonTriggered(INT.index,INT.buttonOrMask)
|
|
* ButtonReleased(INT.index,INT.buttonOrMask)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::ButtonIsPressed(CONST INT index, CONST INT buttonOrMask)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->ButtonIsPressed(buttonOrMask);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/ButtonTriggered(INT.index,INT.buttonOrMask)
|
|
* NAME
|
|
* BOOL ButtonTriggered(CONST INT index, CONST INT buttonOrMask) --
|
|
* check if the button has been triggered.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* buttonOrMask - button number between 0 and 127 for DInput, one of
|
|
* the following bitmasks for XInput: XINPUT_GAMEPAD_DPAD_UP,
|
|
* XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT,
|
|
* XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_START,
|
|
* XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB,
|
|
* XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_LEFT_SHOULDER,
|
|
* XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B,
|
|
* XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y
|
|
* RETURN VALUE
|
|
* TRUE if button was triggered.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* ButtonIsPressed(INT.index,INT.buttonOrMask)
|
|
* ButtonReleased(INT.index,INT.buttonOrMask)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::ButtonTriggered(CONST INT index, CONST INT buttonOrMask)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->ButtonTriggered(buttonOrMask);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/ButtonReleased(INT.index,INT.buttonOrMask)
|
|
* NAME
|
|
* BOOL ButtonReleased(CONST INT index, CONST INT buttonOrMask) --
|
|
* check if the button has been released.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
*
|
|
* buttonOrMask - button number between 0 and 127 for DInput, one of
|
|
* the following bitmasks for XInput: XINPUT_GAMEPAD_DPAD_UP,
|
|
* XINPUT_GAMEPAD_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_LEFT,
|
|
* XINPUT_GAMEPAD_DPAD_RIGHT, XINPUT_GAMEPAD_START,
|
|
* XINPUT_GAMEPAD_BACK, XINPUT_GAMEPAD_LEFT_THUMB,
|
|
* XINPUT_GAMEPAD_RIGHT_THUMB, XINPUT_GAMEPAD_LEFT_SHOULDER,
|
|
* XINPUT_GAMEPAD_RIGHT_SHOULDER, XINPUT_GAMEPAD_A, XINPUT_GAMEPAD_B,
|
|
* XINPUT_GAMEPAD_X, XINPUT_GAMEPAD_Y
|
|
|
|
* RETURN VALUE
|
|
* TRUE if button was released.
|
|
* FALSE otherwise.
|
|
* SEE ALSO
|
|
* ButtonIsPressed(INT.index,INT.buttonOrMask)
|
|
* ButtonTriggered(INT.index,INT.buttonOrMask)
|
|
* SampleInGameImplementation.cpp to see an example.
|
|
******
|
|
*/
|
|
BOOL ControllerInput::ButtonReleased(CONST INT index, CONST INT buttonOrMask)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->ButtonReleased(buttonOrMask);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/****f* Controller.Input.SDK/GetNumberFFAxesDInput(INT.index)
|
|
* NAME
|
|
* INT GetNumberFFAxesDInput(INT index) -- find out how many force
|
|
* feedback axes the controller has.
|
|
* INPUTS
|
|
* index - index of the controller (between 0 and
|
|
* LG_MAX_CONTROLLERS).
|
|
* RETURN VALUE
|
|
* Number of FF axes.
|
|
******
|
|
*/
|
|
INT ControllerInput::GetNumberFFAxesDInput(CONST INT index)
|
|
{
|
|
if (ControllerExists(index))
|
|
{
|
|
return g_controller[index]->GetNumberFFAxes();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID ControllerInput::FreeDirectInput()
|
|
{
|
|
for (INT ii = 0; ii < LogitechControllerInput::LG_MAX_CONTROLLERS; ii++)
|
|
{
|
|
RemoveController(ii);
|
|
}
|
|
|
|
if (g_DIInterface)
|
|
{
|
|
g_DIInterface = NULL;
|
|
}
|
|
}
|
|
|
|
HWND ControllerInput::GetGameHWnd()
|
|
{
|
|
return m_gameHWnd;
|
|
}
|
|
|
|
HRESULT ControllerInput::RemoveController(CONST INT index)
|
|
{
|
|
if (NULL == g_controller[index])
|
|
return E_FAIL;
|
|
|
|
if (g_controller[index]->GetDeviceHandle())
|
|
{
|
|
g_controller[index]->GetDeviceHandle()->Unacquire();
|
|
}
|
|
delete g_controller[index];
|
|
g_controller[index] = NULL;
|
|
|
|
/* following feature was removed because if the extra device happens to be a XInput device and its XInput ID is different of 0, it will fail */
|
|
//// See if number of controllers before removal was = to
|
|
//// LG_MAX_CONTROLLERS. In this case let's re-enumerate
|
|
//// because it could be that there extra controllers
|
|
//// present that didn't get an available spot before.
|
|
//INT counter_ = 0;
|
|
//for (INT jj = 0; jj < LG_MAX_CONTROLLERS; jj++)
|
|
//{
|
|
// if (NULL != g_controller[jj])
|
|
// ++counter_;
|
|
//}
|
|
|
|
//if (counter_ == LG_MAX_CONTROLLERS - 1)
|
|
// g_gameControllerWasPluggedIn = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
INT ControllerInput::GetCurrentNumberControllersConnected()
|
|
{
|
|
INT counter_ = 0;
|
|
|
|
for (INT index_ = 0; index_ < LogitechControllerInput::LG_MAX_CONTROLLERS; index_++)
|
|
{
|
|
if (IsConnected(index_))
|
|
{
|
|
++counter_;
|
|
}
|
|
}
|
|
|
|
return counter_;
|
|
}
|
|
|
|
LRESULT CALLBACK NewWindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
//LONG_PTR ptr_ = GetWindowLongPtr(hwnd, GWLP_USERDATA);
|
|
//ControllerInput* controllerInput_ = (ControllerInput*)ptr_;
|
|
|
|
DEV_BROADCAST_HDR* pHeader_ = NULL;
|
|
DEV_BROADCAST_DEVICEINTERFACE* deviceInterface_ = NULL;
|
|
|
|
BOOL activateValue_ = FALSE;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_ACTIVATEAPP:
|
|
activateValue_ = static_cast<BOOL>(wParam);
|
|
if (activateValue_)
|
|
{
|
|
//LOGICONTROLLERTRACE(_T("WM_ACTIVATEAPP: window got activated\n"));
|
|
XInputEnable(TRUE);
|
|
g_appGotActivated = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//LOGICONTROLLERTRACE(_T("WM_ACTIVATEAPP: window got DEactivated\n"));
|
|
XInputEnable(FALSE);
|
|
}
|
|
break;
|
|
case WM_DESTROY:
|
|
//LOGICONTROLLERTRACE(_T("WindowProc()->WM_DESTROY\n"));
|
|
KillTimer(hwnd, 1);
|
|
break;
|
|
|
|
case WM_CREATE:
|
|
{
|
|
CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
|
|
if (pcs)
|
|
{
|
|
|
|
SetWindowLongPtr(hwnd, GWLP_USERDATA, (__int3264)(LONG_PTR)pcs->lpCreateParams);
|
|
}
|
|
|
|
SetTimer(hwnd,1,200,NULL);
|
|
}
|
|
break;
|
|
case WM_NCDESTROY:
|
|
//LOGICONTROLLERTRACE("WindowProc()->WM_NCDESTROY\n");
|
|
break;
|
|
case WM_NCCREATE:
|
|
break;
|
|
case WM_DEVICECHANGE:
|
|
// disconnecting or connecting any device, including my pda, can trigger the centering spring, so let's do something about it
|
|
g_deviceWasChanged = TRUE;
|
|
|
|
switch (wParam)
|
|
{
|
|
case DBT_DEVICEARRIVAL:
|
|
//LOGICONTROLLERTRACE(_T("DBT_DEVICEARRIVAL\n"));
|
|
pHeader_ = (DEV_BROADCAST_HDR*)lParam;
|
|
if (pHeader_)
|
|
{
|
|
if (pHeader_->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
|
{
|
|
g_gameControllerWasPluggedIn = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case DBT_DEVICEREMOVECOMPLETE:
|
|
//LOGICONTROLLERTRACE(_T("DBT_DEVICEREMOVECOMPLETE\n"));
|
|
|
|
pHeader_ = (DEV_BROADCAST_HDR*)lParam;
|
|
if (pHeader_)
|
|
{
|
|
if (pHeader_->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
|
{
|
|
deviceInterface_ = (DEV_BROADCAST_DEVICEINTERFACE*)lParam;
|
|
|
|
//LOGICONTROLLERTRACE(_T("Need to remove %s\n"), deviceInterface_->dbcc_name);
|
|
TCHAR uniqueIDRemoved_[MAX_PATH] = {'\0'};
|
|
if (FAILED(Utils::Instance()->GetUniqueIDFromDbcc_name(uniqueIDRemoved_, deviceInterface_->dbcc_name)))
|
|
break;
|
|
|
|
STRING uniqueIDRemovedWString_(uniqueIDRemoved_);
|
|
g_uniqueIDsRemoved.push_back(uniqueIDRemovedWString_);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return CallWindowProc (g_OldWnd, hwnd, message, wParam, lParam);
|
|
}
|