2011-09-14 19:08:43 +00:00
/**
* * Supermodel
* * A Sega Model 3 Arcade Emulator .
* * Copyright 2011 Bart Trzynadlowski , Nik Henson
* *
* * This file is part of Supermodel .
* *
* * Supermodel is free software : you can redistribute it and / or modify it under
* * the terms of the GNU General Public License as published by the Free
* * Software Foundation , either version 3 of the License , or ( at your option )
* * any later version .
* *
* * Supermodel is distributed in the hope that it will be useful , but WITHOUT
* * ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* * FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* * more details .
* *
* * You should have received a copy of the GNU General Public License along
* * with Supermodel . If not , see < http : //www.gnu.org/licenses/>.
* */
/*
* DirectInputSystem . cpp
*
* Implementation of the DirectInput - based input system . Also provides support
* for XInput and Raw Input .
*/
2011-04-24 01:14:00 +00:00
# include "DirectInputSystem.h"
2016-06-02 00:13:42 +00:00
# include "Util/Format.h"
2011-04-24 01:14:00 +00:00
# include "Supermodel.h"
2016-06-02 00:13:42 +00:00
# include <array>
2016-03-21 04:07:32 +00:00
# include <algorithm>
2011-06-05 20:53:39 +00:00
# include <wbemidl.h>
# include <oleauto.h>
2011-04-24 01:14:00 +00:00
# include <SDL.h>
# include <SDL_syswm.h>
2012-01-18 04:59:42 +00:00
/*
2016-06-02 00:13:42 +00:00
* There seem to be three versions of XInput floating around , all of which
* ought to provide the functionality we need . We try them all in sequence ,
* in order of newest / most feature - laden first .
2012-01-18 04:59:42 +00:00
*/
2016-06-02 00:13:42 +00:00
static std : : array < const char * , 3 > s_xinput_dlls = { TEXT ( " xinput1_4.dll " ) , TEXT ( " xinput1_3.dll " ) , TEXT ( " xinput9_1_0.dll " ) } ;
static std : : array < const char * , 3 > s_xinput_dlls_a = { " xinput1_4.dll " , " xinput1_3.dll " , " xinput9_1_0.dll " } ;
2012-01-18 04:59:42 +00:00
2011-04-24 01:14:00 +00:00
// TODO - need to double check these all correct and see if can fill in any missing codes (although most just don't exist)
DIKeyMapStruct CDirectInputSystem : : s_keyMap [ ] =
{
// General keys
{ " BACKSPACE " , DIK_BACK } ,
{ " TAB " , DIK_TAB } ,
//{ "CLEAR", ?? },
{ " RETURN " , DIK_RETURN } ,
{ " PAUSE " , DIK_PAUSE } ,
{ " ESCAPE " , DIK_ESCAPE } ,
{ " SPACE " , DIK_SPACE } ,
//{ "EXCLAIM", ?? },
//{ "DBLQUOTE", ?? },
//{ "HASH", ?? },
//{ "DOLLAR", ?? },
//{ "AMPERSAND", ?? },
{ " QUOTE " , DIK_APOSTROPHE } ,
{ " LEFTPAREN " , DIK_LBRACKET } ,
{ " RIGHTPAREN " , DIK_RBRACKET } ,
//{ "ASTERISK", ?? },
//{ "PLUS", ?? },
{ " COMMA " , DIK_COMMA } ,
{ " MINUS " , DIK_MINUS } ,
{ " PERIOD " , DIK_PERIOD } ,
{ " SLASH " , DIK_SLASH } ,
{ " 0 " , DIK_0 } ,
{ " 1 " , DIK_1 } ,
{ " 2 " , DIK_2 } ,
{ " 3 " , DIK_3 } ,
{ " 4 " , DIK_4 } ,
{ " 5 " , DIK_5 } ,
{ " 6 " , DIK_6 } ,
{ " 7 " , DIK_7 } ,
{ " 8 " , DIK_8 } ,
{ " 9 " , DIK_9 } ,
//{ "COLON", ?? },
{ " SEMICOLON " , DIK_SEMICOLON } ,
{ " LESS " , DIK_OEM_102 } ,
{ " EQUALS " , DIK_EQUALS } ,
//{ "GREATER", ?? },
//{ "QUESTION", ?? },
//{ "AT", ?? },
//{ "LEFTBRACKET", ?? },
//{ "BACKSLASH", ?? },
//{ "RIGHTBRACKET", ?? },
//{ "CARET", ?? },
//{ "UNDERSCORE", ?? },
{ " BACKQUOTE " , DIK_GRAVE } ,
{ " A " , DIK_A } ,
{ " B " , DIK_B } ,
{ " C " , DIK_C } ,
{ " D " , DIK_D } ,
{ " E " , DIK_E } ,
{ " F " , DIK_F } ,
{ " G " , DIK_G } ,
{ " H " , DIK_H } ,
{ " I " , DIK_I } ,
{ " J " , DIK_J } ,
{ " K " , DIK_K } ,
{ " L " , DIK_L } ,
{ " M " , DIK_M } ,
{ " N " , DIK_N } ,
{ " O " , DIK_O } ,
{ " P " , DIK_P } ,
{ " Q " , DIK_Q } ,
{ " R " , DIK_R } ,
{ " S " , DIK_S } ,
{ " T " , DIK_T } ,
{ " U " , DIK_U } ,
{ " V " , DIK_V } ,
{ " W " , DIK_W } ,
{ " X " , DIK_X } ,
{ " Y " , DIK_Y } ,
{ " Z " , DIK_Z } ,
{ " DEL " , DIK_DELETE } ,
// Keypad
{ " KEYPAD0 " , DIK_NUMPAD0 } ,
{ " KEYPAD1 " , DIK_NUMPAD1 } ,
{ " KEYPAD2 " , DIK_NUMPAD2 } ,
{ " KEYPAD3 " , DIK_NUMPAD3 } ,
{ " KEYPAD4 " , DIK_NUMPAD4 } ,
{ " KEYPAD5 " , DIK_NUMPAD5 } ,
{ " KEYPAD6 " , DIK_NUMPAD6 } ,
{ " KEYPAD7 " , DIK_NUMPAD7 } ,
{ " KEYPAD8 " , DIK_NUMPAD8 } ,
{ " KEYPAD9 " , DIK_NUMPAD9 } ,
{ " KEYPADPERIOD " , DIK_DECIMAL } ,
{ " KEYPADDIVIDE " , DIK_DIVIDE } ,
{ " KEYPADMULTIPLY " , DIK_MULTIPLY } ,
{ " KEYPADMINUS " , DIK_SUBTRACT } ,
{ " KEYPADPLUS " , DIK_ADD } ,
{ " KEYPADENTER " , DIK_NUMPADENTER } ,
{ " KEYPADEQUALS " , DIK_NUMPADEQUALS } ,
// Arrows + Home/End Pad
{ " UP " , DIK_UP } ,
{ " DOWN " , DIK_DOWN } ,
{ " RIGHT " , DIK_RIGHT } ,
{ " LEFT " , DIK_LEFT } ,
{ " INSERT " , DIK_INSERT } ,
{ " HOME " , DIK_HOME } ,
{ " END " , DIK_END } ,
{ " PGUP " , DIK_PRIOR } ,
{ " PGDN " , DIK_NEXT } ,
// Function Key
{ " F1 " , DIK_F1 } ,
{ " F2 " , DIK_F2 } ,
{ " F3 " , DIK_F3 } ,
{ " F4 " , DIK_F4 } ,
{ " F5 " , DIK_F5 } ,
{ " F6 " , DIK_F6 } ,
{ " F7 " , DIK_F7 } ,
{ " F8 " , DIK_F8 } ,
{ " F9 " , DIK_F9 } ,
{ " F10 " , DIK_F10 } ,
{ " F11 " , DIK_F11 } ,
{ " F12 " , DIK_F12 } ,
{ " F13 " , DIK_F13 } ,
{ " F14 " , DIK_F14 } ,
{ " F15 " , DIK_F15 } ,
// Modifier Keys
{ " NUMLOCK " , DIK_NUMLOCK } ,
{ " CAPSLOCK " , DIK_CAPITAL } ,
{ " SCROLLLOCK " , DIK_SCROLL } ,
{ " RIGHTSHIFT " , DIK_RSHIFT } ,
{ " LEFTSHIFT " , DIK_LSHIFT } ,
{ " RIGHTCTRL " , DIK_RCONTROL } ,
{ " LEFTCTRL " , DIK_LCONTROL } ,
{ " RIGHTALT " , DIK_RMENU } ,
{ " LEFTALT " , DIK_LMENU } ,
//{ "RIGHTMETA", ?? },
//{ "LEFTMETA", ?? },
{ " RIGHTWINDOWS " , DIK_RWIN } ,
{ " LEFTWINDOWS " , DIK_LWIN } ,
//{ "ALTGR", ?? },
//{ "COMPOSE", ?? },
// Other
//{ "HELP", ?? },
{ " PRINT " , DIK_SYSRQ } ,
//{ "SYSREQ", ?? },
//{ "BREAK", ?? },
//{ "MENU", ?? },
//{ "POWER", ?? },
//{ "EURO", ?? },
//{ "UNDO", ?? },
} ;
2011-09-21 22:08:36 +00:00
static bool IsXInputDevice ( const GUID & devProdGUID )
2011-06-05 20:53:39 +00:00
{
2011-06-22 13:06:52 +00:00
// Following code taken from MSDN
2011-06-05 20:53:39 +00:00
IWbemLocator * pIWbemLocator = NULL ;
IEnumWbemClassObject * pEnumDevices = NULL ;
IWbemClassObject * pDevices [ 20 ] = { 0 } ;
IWbemServices * pIWbemServices = NULL ;
BSTR bstrNamespace = NULL ;
BSTR bstrDeviceID = NULL ;
BSTR bstrClassName = NULL ;
// Create WMI
bool isXInpDev = false ;
2012-01-18 04:59:42 +00:00
HRESULT hr = CoCreateInstance ( CLSID_WbemLocator , NULL , CLSCTX_INPROC_SERVER , IID_IWbemLocator , ( LPVOID * ) & pIWbemLocator ) ; // this version does not use __uuidof() and works w/ gcc
//HRESULT hr = CoCreateInstance(__uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*)&pIWbemLocator);
2011-06-05 20:53:39 +00:00
if ( FAILED ( hr ) | | pIWbemLocator = = NULL )
2011-07-20 21:14:00 +00:00
goto Finish ;
2011-06-05 20:53:39 +00:00
2011-07-20 21:14:00 +00:00
if ( ( bstrNamespace = SysAllocString ( L " \\ \\ . \\ root \\ cimv2 " ) ) = = NULL ) goto Finish ;
if ( ( bstrClassName = SysAllocString ( L " Win32_PNPEntity " ) ) = = NULL ) goto Finish ;
if ( ( bstrDeviceID = SysAllocString ( L " DeviceID " ) ) = = NULL ) goto Finish ;
2011-06-05 20:53:39 +00:00
// Connect to WMI
hr = pIWbemLocator - > ConnectServer ( bstrNamespace , NULL , NULL , 0L , 0L , NULL , NULL , & pIWbemServices ) ;
if ( FAILED ( hr ) | | pIWbemServices = = NULL )
2011-07-20 21:14:00 +00:00
goto Finish ;
2011-06-05 20:53:39 +00:00
// Switch security level to IMPERSONATE
CoSetProxyBlanket ( pIWbemServices , RPC_C_AUTHN_WINNT , RPC_C_AUTHZ_NONE , NULL , RPC_C_AUTHN_LEVEL_CALL , RPC_C_IMP_LEVEL_IMPERSONATE , NULL , EOAC_NONE ) ;
hr = pIWbemServices - > CreateInstanceEnum ( bstrClassName , 0 , NULL , & pEnumDevices ) ;
if ( FAILED ( hr ) | | pEnumDevices = = NULL )
2011-07-20 21:14:00 +00:00
goto Finish ;
2011-06-05 20:53:39 +00:00
// Loop over all devices
2011-06-22 13:06:52 +00:00
for ( ; ; )
2011-06-05 20:53:39 +00:00
{
// Get 20 at a time
DWORD uReturned ;
hr = pEnumDevices - > Next ( 10000 , 20 , pDevices , & uReturned ) ;
if ( FAILED ( hr ) | | uReturned = = 0 )
2011-07-20 21:14:00 +00:00
goto Finish ;
2011-06-05 20:53:39 +00:00
for ( unsigned devNum = 0 ; devNum < uReturned ; devNum + + )
{
// For each device, get its device ID
VARIANT var ;
hr = pDevices [ devNum ] - > Get ( bstrDeviceID , 0L , & var , NULL , NULL ) ;
if ( SUCCEEDED ( hr ) & & var . vt = = VT_BSTR & & var . bstrVal ! = NULL )
{
// Check if the device ID contains "IG_", which means it's an XInput device (this can't be determined via DirectInput on its own)
if ( wcsstr ( var . bstrVal , L " IG_ " ) )
{
// If so, then get VID/PID from var.bstrVal
DWORD dwPid = 0 , dwVid = 0 ;
WCHAR * strVid = wcsstr ( var . bstrVal , L " VID_ " ) ;
if ( strVid & & swscanf ( strVid , L " VID_%4X " , & dwVid ) ! = 1 )
dwVid = 0 ;
WCHAR * strPid = wcsstr ( var . bstrVal , L " PID_ " ) ;
if ( strPid & & swscanf ( strPid , L " PID_%4X " , & dwPid ) ! = 1 )
dwPid = 0 ;
// Compare VID/PID to values held in DirectInput device's product GUID
DWORD dwVidPid = MAKELONG ( dwVid , dwPid ) ;
if ( dwVidPid = = devProdGUID . Data1 )
{
isXInpDev = true ;
2011-07-20 21:14:00 +00:00
goto Finish ;
2011-06-05 20:53:39 +00:00
}
}
}
if ( pDevices [ devNum ] ! = NULL )
{
pDevices [ devNum ] - > Release ( ) ;
pDevices [ devNum ] = NULL ;
}
}
}
2011-07-20 21:14:00 +00:00
Finish :
2011-06-05 20:53:39 +00:00
if ( bstrNamespace )
SysFreeString ( bstrNamespace ) ;
if ( bstrDeviceID )
SysFreeString ( bstrDeviceID ) ;
if ( bstrClassName )
SysFreeString ( bstrClassName ) ;
for ( unsigned devNum = 0 ; devNum < 20 ; devNum + + )
{
if ( pDevices [ devNum ] ! = NULL )
pDevices [ devNum ] - > Release ( ) ;
}
if ( pEnumDevices ! = NULL )
pEnumDevices - > Release ( ) ;
if ( pIWbemLocator ! = NULL )
pIWbemLocator - > Release ( ) ;
if ( pIWbemServices ! = NULL )
pIWbemServices - > Release ( ) ;
return isXInpDev ;
}
2011-09-21 22:08:36 +00:00
struct DIEnumDevsContext
{
2020-09-16 07:03:03 +00:00
std : : vector < DIJoyInfo > * infos ;
2011-09-21 22:08:36 +00:00
bool useXInput ;
} ;
static BOOL CALLBACK DI8EnumDevicesCallback ( LPCDIDEVICEINSTANCE instance , LPVOID context )
2011-04-24 01:14:00 +00:00
{
2011-09-21 22:08:36 +00:00
DIEnumDevsContext * diDevsContext = ( DIEnumDevsContext * ) context ;
2011-11-01 23:24:37 +00:00
// Keep track of all joystick device GUIDs
2011-06-05 20:53:39 +00:00
DIJoyInfo info ;
2012-02-27 20:32:15 +00:00
memset ( & info , 0 , sizeof ( info ) ) ;
2011-06-05 20:53:39 +00:00
info . guid = instance - > guidInstance ;
2011-06-22 13:06:52 +00:00
// If XInput is enabled, see if device is an XInput device
2011-09-21 22:08:36 +00:00
info . isXInput = diDevsContext - > useXInput & & IsXInputDevice ( instance - > guidProduct ) ;
diDevsContext - > infos - > push_back ( info ) ;
2011-04-24 01:14:00 +00:00
return DIENUM_CONTINUE ;
}
2011-11-01 23:24:37 +00:00
struct DIEnumObjsContext
2011-09-21 22:08:36 +00:00
{
JoyDetails * joyDetails ;
2011-11-01 23:24:37 +00:00
unsigned sliderCount ;
2011-09-21 22:08:36 +00:00
bool enumError ;
} ;
2011-11-01 23:24:37 +00:00
static BOOL CALLBACK DI8EnumObjectsCallback ( LPCDIDEVICEOBJECTINSTANCE instance , LPVOID context )
2011-04-24 01:14:00 +00:00
{
2011-11-01 23:24:37 +00:00
DIEnumObjsContext * diObjsContext = ( DIEnumObjsContext * ) context ;
// Get data format for object
int objNum = DIDFT_GETINSTANCE ( instance - > dwType ) ;
DIOBJECTDATAFORMAT fmt = c_dfDIJoystick2 . rgodf [ objNum ] ;
// Work out which axis or slider is currently being enumerated from the GUID
2011-06-05 20:53:39 +00:00
int axisNum ;
2011-09-21 22:08:36 +00:00
if ( instance - > guidType = = GUID_XAxis ) axisNum = AXIS_X ;
else if ( instance - > guidType = = GUID_YAxis ) axisNum = AXIS_Y ;
else if ( instance - > guidType = = GUID_ZAxis ) axisNum = AXIS_Z ;
else if ( instance - > guidType = = GUID_RxAxis ) axisNum = AXIS_RX ;
else if ( instance - > guidType = = GUID_RyAxis ) axisNum = AXIS_RY ;
else if ( instance - > guidType = = GUID_RzAxis ) axisNum = AXIS_RZ ;
2011-11-01 23:24:37 +00:00
else if ( instance - > guidType = = GUID_Slider )
{
// Work out which slider from count
switch ( diObjsContext - > sliderCount + + )
{
case 0 : axisNum = AXIS_S1 ; break ;
case 1 : axisNum = AXIS_S2 ; break ;
default :
// If couldn't match then ignore slider
return DIENUM_CONTINUE ;
}
}
else if ( instance - > dwType & DIDFT_AXIS )
2011-06-05 20:53:39 +00:00
{
2011-11-01 23:24:37 +00:00
// If is an axis but couldn't match GUID above (which, according to MSDN, is an optional attribute), then flag error and try matching via offset
2011-09-21 22:08:36 +00:00
int objNum = DIDFT_GETINSTANCE ( instance - > dwType ) ;
DIOBJECTDATAFORMAT fmt = c_dfDIJoystick2 . rgodf [ objNum ] ;
2011-11-01 23:24:37 +00:00
diObjsContext - > enumError = true ;
2012-01-18 04:59:42 +00:00
# ifdef _MSC_VER // MS VisualC++
2011-09-21 22:08:36 +00:00
switch ( fmt . dwOfs )
{
case DIJOFS_X : axisNum = AXIS_X ; break ;
case DIJOFS_Y : axisNum = AXIS_Y ; break ;
case DIJOFS_Z : axisNum = AXIS_Z ; break ;
case DIJOFS_RX : axisNum = AXIS_RX ; break ;
case DIJOFS_RY : axisNum = AXIS_RY ; break ;
case DIJOFS_RZ : axisNum = AXIS_RZ ; break ;
default :
// If still couldn't match then it is not an axis
return DIENUM_CONTINUE ;
}
2012-01-18 04:59:42 +00:00
# else // GCC
// DIJOFS_* are not technically constants (at least in the MinGW dinput.h that I'm using)
if ( DIJOFS_X = = fmt . dwOfs ) axisNum = AXIS_X ;
else if ( DIJOFS_Y = = fmt . dwOfs ) axisNum = AXIS_Y ;
else if ( DIJOFS_Z = = fmt . dwOfs ) axisNum = AXIS_Z ;
else if ( DIJOFS_Z = = fmt . dwOfs ) axisNum = AXIS_Z ;
else if ( DIJOFS_RX = = fmt . dwOfs ) axisNum = AXIS_RX ;
else if ( DIJOFS_RY = = fmt . dwOfs ) axisNum = AXIS_RY ;
else if ( DIJOFS_RZ = = fmt . dwOfs ) axisNum = AXIS_RZ ;
else
// If still couldn't match then it is not an axis
return DIENUM_CONTINUE ;
# endif
2011-06-05 20:53:39 +00:00
}
2011-11-01 23:24:37 +00:00
else
{
// Ignore all other types of object
return DIENUM_CONTINUE ;
}
2011-06-05 20:53:39 +00:00
2011-11-01 23:24:37 +00:00
// If axis overlaps with a previous one, flag error
JoyDetails * joyDetails = diObjsContext - > joyDetails ;
2011-09-21 22:08:36 +00:00
if ( joyDetails - > hasAxis [ axisNum ] )
2011-11-01 23:24:37 +00:00
diObjsContext - > enumError = true ;
2011-09-21 22:08:36 +00:00
2011-06-05 20:53:39 +00:00
// Record fact that axis is present and also whether it has force feedback available
joyDetails - > hasAxis [ axisNum ] = true ;
joyDetails - > axisHasFF [ axisNum ] = ! ! ( instance - > dwFlags & DIDOI_FFACTUATOR ) ;
2011-09-18 21:44:40 +00:00
2011-09-21 22:08:36 +00:00
// Get axis name from DirectInput and store that too
2011-09-18 21:44:40 +00:00
char * axisName = joyDetails - > axisName [ axisNum ] ;
strcpy ( axisName , CInputSystem : : GetDefaultAxisName ( axisNum ) ) ;
2011-11-01 23:24:37 +00:00
strcat ( axisName , " ( " ) ;
2011-09-18 21:44:40 +00:00
strncat ( axisName , instance - > tszName , MAX_NAME_LENGTH - strlen ( axisName ) - 1 ) ;
strcat ( axisName , " ) " ) ;
2011-06-05 20:53:39 +00:00
return DIENUM_CONTINUE ;
}
2011-09-21 22:08:36 +00:00
static BOOL CALLBACK DI8EnumEffectsCallback ( LPCDIEFFECTINFO effectInfo , LPVOID context )
2011-06-05 20:53:39 +00:00
{
2011-06-22 13:06:52 +00:00
// Check joystick has at least one of required types of effects
2011-09-21 22:08:36 +00:00
JoyDetails * joyDetails = ( JoyDetails * ) context ;
2011-06-22 13:06:52 +00:00
if ( ! ! ( effectInfo - > dwEffType & ( DIEFT_CONSTANTFORCE | DIEFT_PERIODIC | DIEFT_CONDITION ) ) )
2011-06-05 20:53:39 +00:00
joyDetails - > hasFFeedback = true ;
2011-04-24 01:14:00 +00:00
return DIENUM_CONTINUE ;
}
2011-09-18 21:44:40 +00:00
const char * CDirectInputSystem : : ConstructName ( bool useRawInput , bool useXInput )
2011-06-05 20:53:39 +00:00
{
if ( useRawInput )
return ( useXInput ? " RawInput/XInput " : " RawInput/DirectInput " ) ;
else
return ( useXInput ? " Xinput " : " DirectInput " ) ;
}
2020-04-19 08:34:58 +00:00
CDirectInputSystem : : CDirectInputSystem ( const Util : : Config : : Node & config , SDL_Window * window , bool useRawInput , bool useXInput ) :
2011-09-18 21:44:40 +00:00
CInputSystem ( ConstructName ( useRawInput , useXInput ) ) ,
2017-03-27 03:19:15 +00:00
m_config ( config ) ,
2011-09-18 21:44:40 +00:00
m_useRawInput ( useRawInput ) , m_useXInput ( useXInput ) , m_enableFFeedback ( true ) ,
2020-04-19 08:34:58 +00:00
m_initializedCOM ( false ) , m_activated ( false ) , m_window ( window ) , m_hwnd ( NULL ) , m_screenW ( 0 ) , m_screenH ( 0 ) ,
2011-06-05 20:53:39 +00:00
m_getRIDevListPtr ( NULL ) , m_getRIDevInfoPtr ( NULL ) , m_regRIDevsPtr ( NULL ) , m_getRIDataPtr ( NULL ) ,
2011-09-18 21:44:40 +00:00
m_xiGetCapabilitiesPtr ( NULL ) , m_xiGetStatePtr ( NULL ) , m_xiSetStatePtr ( NULL ) , m_di8 ( NULL ) , m_di8Keyboard ( NULL ) , m_di8Mouse ( NULL )
2011-04-24 01:14:00 +00:00
{
// Reset initial states
2012-02-27 20:32:15 +00:00
memset ( & m_combRawMseState , 0 , sizeof ( m_combRawMseState ) ) ;
memset ( & m_diKeyState , 0 , sizeof ( m_diKeyState ) ) ;
memset ( & m_diMseState , 0 , sizeof ( m_diMseState ) ) ;
2011-04-24 01:14:00 +00:00
}
CDirectInputSystem : : ~ CDirectInputSystem ( )
{
CloseKeyboardsAndMice ( ) ;
CloseJoysticks ( ) ;
2011-06-05 20:53:39 +00:00
if ( m_di8 )
2011-04-24 01:14:00 +00:00
{
m_di8 - > Release ( ) ;
m_di8 = NULL ;
2011-06-27 23:06:08 +00:00
if ( m_initializedCOM )
CoUninitialize ( ) ;
2011-06-05 20:53:39 +00:00
}
}
2020-09-16 07:03:03 +00:00
bool CDirectInputSystem : : GetRegString ( HKEY regKey , const char * regPath , std : : string & str )
2011-06-05 20:53:39 +00:00
{
// Query to get the length
DWORD dataLen ;
LONG result = RegQueryValueEx ( regKey , regPath , NULL , NULL , NULL , & dataLen ) ;
if ( result ! = ERROR_SUCCESS )
return false ;
// Retrieve the actual data
char data [ MAX_PATH ] ;
2016-03-21 04:07:32 +00:00
dataLen = std : : min < DWORD > ( MAX_PATH - 1 , dataLen ) ;
2011-06-05 20:53:39 +00:00
result = RegQueryValueEx ( regKey , regPath , NULL , NULL , ( LPBYTE ) data , & dataLen ) ;
if ( result ! = ERROR_SUCCESS )
return false ;
data [ MAX_PATH - 1 ] = ' \0 ' ;
str . assign ( data ) ;
return true ;
}
bool CDirectInputSystem : : GetRegDeviceName ( const char * rawDevName , char * name )
{
// Check raw device string is in form that can be handled and remove initial 4-char sequence
// For XP this is: \??\TypeID#HardwareID#InstanceID#{DevicesClasses-id}
// For Vista/Win7 64bit this is: \\?\TypeID#HardwareID#InstanceID#{DevicesClasses-id}
2020-09-16 07:03:03 +00:00
std : : string devNameStr ( rawDevName ) ;
if ( devNameStr . find ( " \\ ?? \\ " ) ! = std : : string : : npos | | devNameStr . find ( " \\ \\ ? \\ " ) ! = std : : string : : npos )
2011-06-05 20:53:39 +00:00
devNameStr . erase ( 0 , 4 ) ;
else
return false ;
// Append raw device string to base registry path and convert all #'s to \ in the process
2020-09-16 07:03:03 +00:00
std : : string regPath = " SYSTEM \\ CurrentControlSet \\ Enum \\ " + devNameStr ;
2011-06-05 20:53:39 +00:00
for ( size_t i = 0 ; i < regPath . size ( ) ; i + + )
{
if ( regPath [ i ] = = ' # ' )
regPath [ i ] = ' \\ ' ;
}
// Remove part after last \ in path
size_t last = regPath . rfind ( ' \\ ' ) ;
2020-09-16 07:03:03 +00:00
if ( last ! = std : : string : : npos )
2011-06-05 20:53:39 +00:00
regPath = regPath . erase ( last ) ;
// Try and open registry key with this path
HKEY regKey ;
LONG result = RegOpenKeyEx ( HKEY_LOCAL_MACHINE , regPath . c_str ( ) , 0 , KEY_READ , & regKey ) ;
if ( result ! = ERROR_SUCCESS )
return false ;
2020-09-16 07:03:03 +00:00
std : : string parentIdStr ;
2011-06-05 20:53:39 +00:00
// Fetch device description from registry, if it exists, and use that for name
2020-09-16 07:03:03 +00:00
std : : string regStr ;
2011-06-05 20:53:39 +00:00
if ( GetRegString ( regKey , " DeviceDesc " , regStr ) )
goto Found ;
// If above failed, then try looking at USB key for HID devices
RegCloseKey ( regKey ) ;
// Check it is HID device
2020-09-16 07:03:03 +00:00
if ( devNameStr . find ( " HID " ) = = std : : string : : npos )
2011-06-05 20:53:39 +00:00
return false ;
// Get parent id, from after last \ in name
last = regPath . rfind ( ' \\ ' ) ;
2020-09-16 07:03:03 +00:00
if ( last = = regPath . size ( ) - 1 | | last = = std : : string : : npos )
2011-06-05 20:53:39 +00:00
return false ;
parentIdStr = regPath . substr ( last + 1 ) ;
// Open USB base key
result = RegOpenKeyEx ( HKEY_LOCAL_MACHINE , " SYSTEM \\ CurrentControlSet \\ Enum \\ USB " , 0 , KEY_READ , & regKey ) ;
if ( result ! = ERROR_SUCCESS )
return false ;
// Loop through all USB devices
for ( int usbIndex = 0 ; result = = ERROR_SUCCESS ; usbIndex + + )
{
// Get sub-key name
char keyName [ MAX_PATH ] ;
DWORD nameLen = MAX_PATH - 1 ;
result = RegEnumKeyEx ( regKey , usbIndex , keyName , & nameLen , NULL , NULL , NULL , NULL ) ;
if ( result = = ERROR_SUCCESS )
{
// Open sub-key
HKEY subRegKey ;
LONG subResult = RegOpenKeyEx ( regKey , keyName , 0 , KEY_READ , & subRegKey ) ;
if ( subResult ! = ERROR_SUCCESS )
continue ;
// Loop through all sub-keys
for ( int subIndex = 0 ; subResult = = ERROR_SUCCESS ; subIndex + + )
{
// the next enumerated subkey and scan it
nameLen = MAX_PATH - 1 ;
subResult = RegEnumKeyEx ( subRegKey , subIndex , keyName , & nameLen , NULL , NULL , NULL , NULL ) ;
if ( subResult = = ERROR_SUCCESS )
{
// Open final key
HKEY finalRegKey ;
LONG finalResult = RegOpenKeyEx ( subRegKey , keyName , 0 , KEY_READ , & finalRegKey ) ;
if ( finalResult ! = ERROR_SUCCESS )
continue ;
// Get parent id prefix and see if it matches
2020-09-16 07:03:03 +00:00
std : : string finalParentIdStr ;
2011-06-05 20:53:39 +00:00
if ( GetRegString ( finalRegKey , " ParentIdPrefix " , finalParentIdStr ) & & parentIdStr . compare ( 0 , finalParentIdStr . size ( ) , finalParentIdStr ) = = 0 )
{
// Get device description, if it exists, and use that for name
if ( GetRegString ( finalRegKey , " DeviceDesc " , regStr ) )
{
RegCloseKey ( finalRegKey ) ;
RegCloseKey ( subRegKey ) ;
goto Found ;
}
}
// Close final key
RegCloseKey ( finalRegKey ) ;
}
}
// Close sub-key
RegCloseKey ( subRegKey ) ;
}
2011-04-24 01:14:00 +00:00
}
2011-06-05 20:53:39 +00:00
RegCloseKey ( regKey ) ;
return false ;
Found :
// If found device description, name will be from final colon
last = regStr . rfind ( ' ; ' ) ;
2020-09-16 07:03:03 +00:00
if ( last = = regStr . size ( ) - 1 | | last = = std : : string : : npos )
2011-06-05 20:53:39 +00:00
last = 0 ;
else
last + + ;
strncpy ( name , regStr . c_str ( ) + last , MAX_NAME_LENGTH - 1 ) ;
name [ MAX_NAME_LENGTH - 1 ] = ' \0 ' ;
RegCloseKey ( regKey ) ;
return true ;
2011-04-24 01:14:00 +00:00
}
void CDirectInputSystem : : OpenKeyboardsAndMice ( )
{
if ( m_useRawInput )
{
// If RawInput enabled, get list of available devices
UINT nDevices ;
if ( m_getRIDevListPtr ( NULL , & nDevices , sizeof ( RAWINPUTDEVICELIST ) ) = = 0 & & nDevices > 0 )
{
PRAWINPUTDEVICELIST pDeviceList = new RAWINPUTDEVICELIST [ nDevices ] ;
if ( pDeviceList ! = NULL & & m_getRIDevListPtr ( pDeviceList , & nDevices , sizeof ( RAWINPUTDEVICELIST ) ) ! = ( UINT ) - 1 )
{
// Loop through devices backwards (since new devices are usually added at beginning)
for ( int devNum = nDevices - 1 ; devNum > = 0 ; devNum - - )
{
RAWINPUTDEVICELIST device = pDeviceList [ devNum ] ;
// Get device name
UINT nLength ;
if ( m_getRIDevInfoPtr ( device . hDevice , RIDI_DEVICENAME , NULL , & nLength ) ! = 0 )
continue ;
2016-03-21 04:07:32 +00:00
nLength = std : : min < int > ( MAX_NAME_LENGTH , nLength ) ;
2011-06-05 20:53:39 +00:00
char name [ MAX_NAME_LENGTH ] ;
2011-04-24 01:14:00 +00:00
if ( m_getRIDevInfoPtr ( device . hDevice , RIDI_DEVICENAME , name , & nLength ) = = - 1 )
continue ;
// Ignore any RDP devices
if ( strstr ( name , " Root#RDP_ " ) ! = NULL )
continue ;
2011-06-05 20:53:39 +00:00
// Store details and device handles for attached keyboards and mice
2011-04-24 01:14:00 +00:00
if ( device . dwType = = RIM_TYPEKEYBOARD )
{
m_rawKeyboards . push_back ( device . hDevice ) ;
2011-06-05 20:53:39 +00:00
KeyDetails keyDetails ;
if ( ! GetRegDeviceName ( name , keyDetails . name ) )
strcpy ( keyDetails . name , " Unknown Keyboard " ) ;
m_keyDetails . push_back ( keyDetails ) ;
2011-09-08 06:34:18 +00:00
bool * pKeyState = new bool [ 255 ] ;
memset ( pKeyState , 0 , sizeof ( bool ) * 255 ) ;
2011-06-05 20:53:39 +00:00
m_rawKeyStates . push_back ( pKeyState ) ;
2011-04-24 01:14:00 +00:00
}
else if ( device . dwType = = RIM_TYPEMOUSE )
{
m_rawMice . push_back ( device . hDevice ) ;
2011-06-05 20:53:39 +00:00
MouseDetails mseDetails ;
if ( ! GetRegDeviceName ( name , mseDetails . name ) )
strcpy ( mseDetails . name , " Unknown Mouse " ) ;
// TODO mseDetails.isAbsolute = ???
m_mseDetails . push_back ( mseDetails ) ;
RawMseState mseState ;
2012-02-27 20:32:15 +00:00
memset ( & mseState , 0 , sizeof ( mseState ) ) ;
2011-04-24 01:14:00 +00:00
m_rawMseStates . push_back ( mseState ) ;
}
}
DebugLog ( " RawInput - found %d keyboards and %d mice " , m_rawKeyboards . size ( ) , m_rawMice . size ( ) ) ;
// Check some devices were actually found
m_useRawInput = m_rawKeyboards . size ( ) > 0 & & m_rawMice . size ( ) > 0 ;
}
else
{
ErrorLog ( " Unable to query RawInput API for attached devices (error %d) - switching to DirectInput. \n " , GetLastError ( ) ) ;
m_useRawInput = false ;
}
if ( pDeviceList ! = NULL )
delete [ ] pDeviceList ;
}
else
{
ErrorLog ( " Unable to query RawInput API for attached devices (error %d) - switching to DirectInput. \n " , GetLastError ( ) ) ;
m_useRawInput = false ;
}
if ( m_useRawInput )
return ;
}
// If get here then either RawInput disabled or getting its devices failed so default to DirectInput.
// Open DirectInput system keyboard and set its data format
HRESULT hr ;
if ( FAILED ( hr = m_di8 - > CreateDevice ( GUID_SysKeyboard , & m_di8Keyboard , NULL ) ) )
{
ErrorLog ( " Unable to create DirectInput keyboard device (error %d) - key input will be unavailable. \n " , hr ) ;
m_di8Keyboard = NULL ;
}
else if ( FAILED ( hr = m_di8Keyboard - > SetDataFormat ( & c_dfDIKeyboard ) ) )
{
ErrorLog ( " Unable to set data format for DirectInput keyboard (error %d) - key input will be unavailable. \n " , hr ) ;
m_di8Keyboard - > Release ( ) ;
m_di8Keyboard = NULL ;
}
// Open DirectInput system mouse and set its data format
if ( FAILED ( hr = m_di8 - > CreateDevice ( GUID_SysMouse , & m_di8Mouse , NULL ) ) )
{
ErrorLog ( " Unable to create DirectInput mouse device (error %d) - mouse input will be unavailable. \n " , hr ) ;
m_di8Mouse = NULL ;
return ;
}
if ( FAILED ( hr = m_di8Mouse - > SetDataFormat ( & c_dfDIMouse2 ) ) )
{
ErrorLog ( " Unable to set data format for DirectInput mouse (error %d) - mouse input will be unavailable. \n " , hr ) ;
m_di8Mouse - > Release ( ) ;
m_di8Mouse = NULL ;
return ;
}
// Set mouse axis mode to relative
DIPROPDWORD dipdw ;
dipdw . diph . dwSize = sizeof ( DIPROPDWORD ) ;
dipdw . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
dipdw . diph . dwHow = DIPH_DEVICE ;
dipdw . diph . dwObj = 0 ;
dipdw . dwData = DIPROPAXISMODE_REL ;
if ( FAILED ( hr = m_di8Mouse - > SetProperty ( DIPROP_AXISMODE , & dipdw . diph ) ) )
{
ErrorLog ( " Unable to set axis mode for DirectInput mouse (error %d) - mouse input will be unavailable. \n " , hr ) ;
m_di8Mouse - > Release ( ) ;
m_di8Mouse = NULL ;
}
}
void CDirectInputSystem : : ActivateKeyboardsAndMice ( )
{
// Sync up all mice with current cursor position
ResetMice ( ) ;
if ( m_useRawInput )
{
// Register for RawInput
RAWINPUTDEVICE rid [ 2 ] ;
// Register for keyboard input
rid [ 0 ] . usUsagePage = 0x01 ;
rid [ 0 ] . usUsage = 0x06 ;
2011-06-05 20:53:39 +00:00
rid [ 0 ] . dwFlags = ( m_grabMouse ? RIDEV_CAPTUREMOUSE : RIDEV_INPUTSINK ) | RIDEV_NOLEGACY ;
2011-04-24 01:14:00 +00:00
rid [ 0 ] . hwndTarget = m_hwnd ;
// Register for mouse input
rid [ 1 ] . usUsagePage = 0x01 ;
rid [ 1 ] . usUsage = 0x02 ;
2011-06-05 20:53:39 +00:00
rid [ 1 ] . dwFlags = ( m_grabMouse ? RIDEV_CAPTUREMOUSE : RIDEV_INPUTSINK ) | RIDEV_NOLEGACY ;
2011-04-24 01:14:00 +00:00
rid [ 1 ] . hwndTarget = m_hwnd ;
if ( ! m_regRIDevsPtr ( rid , 2 , sizeof ( RAWINPUTDEVICE ) ) )
ErrorLog ( " Unable to register for keyboard and mouse input with RawInput API (error %d) - keyboard and mouse input will be unavailable. \n " , GetLastError ( ) ) ;
return ;
}
// Set DirectInput cooperative level of keyboard and mouse
if ( m_di8Keyboard ! = NULL )
2011-09-18 21:44:40 +00:00
{
m_di8Keyboard - > Unacquire ( ) ;
m_di8Keyboard - > SetCooperativeLevel ( m_hwnd , ( m_grabMouse ? DISCL_FOREGROUND : DISCL_BACKGROUND ) | DISCL_NONEXCLUSIVE ) ;
m_di8Keyboard - > Acquire ( ) ;
}
2011-04-24 01:14:00 +00:00
if ( m_di8Mouse ! = NULL )
2011-09-18 21:44:40 +00:00
{
m_di8Mouse - > Unacquire ( ) ;
m_di8Mouse - > SetCooperativeLevel ( m_hwnd , ( m_grabMouse ? DISCL_FOREGROUND : DISCL_BACKGROUND ) | DISCL_NONEXCLUSIVE ) ;
m_di8Mouse - > Acquire ( ) ;
}
2011-04-24 01:14:00 +00:00
}
void CDirectInputSystem : : PollKeyboardsAndMice ( )
{
if ( m_useRawInput )
{
// For RawInput, only thing to do is update wheelDir from wheelData for each mouse state. Everything else is updated via WM events.
2022-07-11 15:43:59 +00:00
for ( std : : vector < RawMseState > : : iterator it = m_rawMseStates . begin ( ) ; it ! = m_rawMseStates . end ( ) ; + + it )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
if ( it - > wheelDelta ! = 0 )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
it - > wheelDir = ( it - > wheelDelta > 0 ? 1 : - 1 ) ;
it - > wheelDelta = 0 ;
2011-04-24 01:14:00 +00:00
}
else
2011-06-05 20:53:39 +00:00
it - > wheelDir = 0 ;
2011-04-24 01:14:00 +00:00
}
if ( m_combRawMseState . wheelDelta ! = 0 )
{
m_combRawMseState . wheelDir = ( m_combRawMseState . wheelDelta > 0 ? 1 : - 1 ) ;
m_combRawMseState . wheelDelta = 0 ;
}
else
m_combRawMseState . wheelDir = 0 ;
return ;
}
// Get current keyboard state from DirectInput
HRESULT hr ;
if ( m_di8Keyboard ! = NULL )
{
if ( FAILED ( hr = m_di8Keyboard - > Poll ( ) ) )
{
hr = m_di8Keyboard - > Acquire ( ) ;
while ( hr = = DIERR_INPUTLOST )
hr = m_di8Keyboard - > Acquire ( ) ;
if ( hr = = DIERR_OTHERAPPHASPRIO | | hr = = DIERR_INVALIDPARAM | | hr = = DIERR_NOTINITIALIZED )
return ;
}
// Keep track of keyboard state
m_di8Keyboard - > GetDeviceState ( sizeof ( m_diKeyState ) , m_diKeyState ) ;
}
// Get current mouse state from DirectInput
if ( m_di8Mouse ! = NULL )
{
if ( FAILED ( hr = m_di8Mouse - > Poll ( ) ) )
{
hr = m_di8Mouse - > Acquire ( ) ;
while ( hr = = DIERR_INPUTLOST )
hr = m_di8Mouse - > Acquire ( ) ;
if ( hr = = DIERR_OTHERAPPHASPRIO | | hr = = DIERR_INVALIDPARAM | | hr = = DIERR_NOTINITIALIZED )
return ;
}
// Keep track of mouse absolute axis values, clamping them at display edges, aswell as wheel direction and buttons
DIMOUSESTATE2 mseState ;
m_di8Mouse - > GetDeviceState ( sizeof ( mseState ) , & mseState ) ;
m_diMseState . x = CInputSource : : Clamp ( m_diMseState . x + mseState . lX , m_dispX , m_dispX + m_dispW ) ;
m_diMseState . y = CInputSource : : Clamp ( m_diMseState . y + mseState . lY , m_dispY , m_dispY + m_dispH ) ;
if ( mseState . lZ ! = 0 )
{
// Z-axis is clamped to range -100 to 100 (DirectInput returns +120 & -120 for wheel delta which are scaled to +5 & -5)
LONG wheelDelta = 5 * mseState . lZ / 120 ;
m_diMseState . z = CInputSource : : Clamp ( m_diMseState . z + wheelDelta , - 100 , 100 ) ;
m_diMseState . wheelDir = ( wheelDelta > 0 ? 1 : - 1 ) ;
}
else
m_diMseState . wheelDir = 0 ;
memcpy ( & m_diMseState . buttons , mseState . rgbButtons , sizeof ( m_diMseState . buttons ) ) ;
}
}
void CDirectInputSystem : : CloseKeyboardsAndMice ( )
{
if ( m_useRawInput )
{
if ( m_activated )
{
// If RawInput was registered, then unregister now
RAWINPUTDEVICE rid [ 2 ] ;
// Unregister from keyboard input
rid [ 0 ] . usUsagePage = 0x01 ;
rid [ 0 ] . usUsage = 0x06 ;
rid [ 0 ] . dwFlags = RIDEV_REMOVE ;
rid [ 0 ] . hwndTarget = m_hwnd ;
// Unregister from mouse input
rid [ 1 ] . usUsagePage = 0x01 ;
rid [ 1 ] . usUsage = 0x02 ;
rid [ 1 ] . dwFlags = RIDEV_REMOVE ;
rid [ 1 ] . hwndTarget = m_hwnd ;
m_regRIDevsPtr ( rid , 2 , sizeof ( RAWINPUTDEVICE ) ) ;
}
2011-06-05 20:53:39 +00:00
// Delete storage for keyboards
2022-07-11 15:43:59 +00:00
for ( std : : vector < bool * > : : iterator it = m_rawKeyStates . begin ( ) ; it ! = m_rawKeyStates . end ( ) ; + + it )
2011-04-24 01:14:00 +00:00
delete [ ] * it ;
2011-06-05 20:53:39 +00:00
m_keyDetails . clear ( ) ;
m_rawKeyboards . clear ( ) ;
m_rawKeyStates . clear ( ) ;
// Delete storage for mice
m_mseDetails . clear ( ) ;
m_rawMice . clear ( ) ;
m_rawMseStates . clear ( ) ;
2011-04-24 01:14:00 +00:00
}
2011-06-05 20:53:39 +00:00
// If DirectInput keyboard and mouse were created, then release them too
2011-04-24 01:14:00 +00:00
if ( m_di8Keyboard ! = NULL )
{
m_di8Keyboard - > Unacquire ( ) ;
m_di8Keyboard - > Release ( ) ;
m_di8Keyboard = NULL ;
}
if ( m_di8Mouse ! = NULL )
{
m_di8Mouse - > Unacquire ( ) ;
m_di8Mouse - > Release ( ) ;
m_di8Mouse = NULL ;
}
}
void CDirectInputSystem : : ResetMice ( )
{
// Get current mouse cursor position in window
POINT p ;
if ( ! GetCursorPos ( & p ) | | ! ScreenToClient ( m_hwnd , & p ) )
return ;
// Set all mice coords to current cursor position
if ( m_useRawInput )
{
m_combRawMseState . x = p . x ;
m_combRawMseState . y = p . y ;
m_combRawMseState . z = 0 ;
2022-07-11 15:43:59 +00:00
for ( std : : vector < RawMseState > : : iterator it = m_rawMseStates . begin ( ) ; it ! = m_rawMseStates . end ( ) ; + + it )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
it - > x = p . x ;
it - > y = p . y ;
it - > z = 0 ;
2011-04-24 01:14:00 +00:00
}
}
m_diMseState . x = p . x ;
m_diMseState . y = p . y ;
m_diMseState . z = 0 ;
}
void CDirectInputSystem : : ProcessRawInput ( HRAWINPUT hInput )
{
// RawInput data event
BYTE buffer [ 4096 ] ;
LPBYTE pBuf = buffer ;
// Get size of data structure to receive
UINT dwSize ;
if ( m_getRIDataPtr ( hInput , RID_INPUT , NULL , & dwSize , sizeof ( RAWINPUTHEADER ) ) ! = 0 )
return ;
if ( dwSize > sizeof ( buffer ) )
{
pBuf = new BYTE [ dwSize ] ;
if ( pBuf = = NULL )
return ;
}
// Get data
if ( m_getRIDataPtr ( hInput , RID_INPUT , pBuf , & dwSize , sizeof ( RAWINPUTHEADER ) ) = = dwSize )
{
RAWINPUT * pData = ( RAWINPUT * ) pBuf ;
if ( pData - > header . dwType = = RIM_TYPEKEYBOARD )
{
// Keyboard event, so identify which keyboard produced event
2011-09-08 06:34:18 +00:00
bool * pKeyState = NULL ;
2011-04-24 01:14:00 +00:00
size_t kbdNum ;
for ( kbdNum = 0 ; kbdNum < m_rawKeyboards . size ( ) ; kbdNum + + )
{
if ( m_rawKeyboards [ kbdNum ] = = pData - > header . hDevice )
{
2011-06-05 20:53:39 +00:00
pKeyState = m_rawKeyStates [ kbdNum ] ;
2011-04-24 01:14:00 +00:00
break ;
}
}
// Check is a valid keyboard
2011-06-05 20:53:39 +00:00
if ( pKeyState ! = NULL )
2011-04-24 01:14:00 +00:00
{
// Get scancode of key and whether key was pressed or released
2011-09-08 06:34:18 +00:00
int isRight = ( pData - > data . keyboard . Flags & RI_KEY_E0 ) ;
2011-04-24 01:14:00 +00:00
UINT8 scanCode = ( pData - > data . keyboard . MakeCode & 0x7f ) | ( isRight ? 0x80 : 0x00 ) ;
2011-09-08 06:34:18 +00:00
bool pressed = ! ( pData - > data . keyboard . Flags & RI_KEY_BREAK ) ;
2011-04-24 01:14:00 +00:00
// Store current state for key
if ( scanCode ! = 0xAA )
2011-06-05 20:53:39 +00:00
pKeyState [ scanCode ] = pressed ;
2011-04-24 01:14:00 +00:00
}
}
else if ( pData - > header . dwType = = RIM_TYPEMOUSE )
{
// Mouse event, so identify which mouse produced event
2011-06-05 20:53:39 +00:00
RawMseState * pMseState = NULL ;
2011-04-24 01:14:00 +00:00
size_t mseNum ;
for ( mseNum = 0 ; mseNum < m_rawMice . size ( ) ; mseNum + + )
{
if ( m_rawMice [ mseNum ] = = pData - > header . hDevice )
{
2011-06-05 20:53:39 +00:00
pMseState = & m_rawMseStates [ mseNum ] ;
2011-04-24 01:14:00 +00:00
break ;
}
}
// Check is a valid mouse
2011-06-05 20:53:39 +00:00
if ( pMseState ! = NULL )
2011-04-24 01:14:00 +00:00
{
// Get X- & Y-axis data
LONG lx = pData - > data . mouse . lLastX ;
LONG ly = pData - > data . mouse . lLastY ;
if ( pData - > data . mouse . usFlags & MOUSE_MOVE_ABSOLUTE )
{
2011-06-05 20:53:39 +00:00
// If data is absolute, then scale source values (which range 0 to 65535) to screen coordinates and convert
// to be relative to game window origin
POINT p ;
p . x = CInputSource : : Scale ( lx , 0 , 0xFFFF , 0 , m_screenW ) ;
p . y = CInputSource : : Scale ( ly , 0 , 0xFFFF , 0 , m_screenH ) ;
if ( ScreenToClient ( m_hwnd , & p ) )
{
pMseState - > x = p . x ;
pMseState - > y = p . y ;
}
2011-04-24 01:14:00 +00:00
// Also update combined state
2011-06-05 20:53:39 +00:00
m_combRawMseState . x = pMseState - > x ;
m_combRawMseState . y = pMseState - > y ;
2011-04-24 01:14:00 +00:00
}
else
{
// If data is relative, then keep track of absolute position, clamping it at display edges
2011-06-05 20:53:39 +00:00
pMseState - > x = CInputSource : : Clamp ( pMseState - > x + lx , m_dispX , m_dispX + m_dispW ) ;
pMseState - > y = CInputSource : : Clamp ( pMseState - > y + ly , m_dispY , m_dispY + m_dispH ) ;
2011-04-24 01:14:00 +00:00
// Also update combined state
m_combRawMseState . x = CInputSource : : Clamp ( m_combRawMseState . x + lx , m_dispX , m_dispX + m_dispW ) ;
m_combRawMseState . y = CInputSource : : Clamp ( m_combRawMseState . y + ly , m_dispY , m_dispY + m_dispH ) ;
}
// Get button flags and wheel delta (RawInput returns +120 & -120 for the latter which are scaled to +5 & -5)
USHORT butFlags = pData - > data . mouse . usButtonFlags ;
LONG wheelDelta = 5 * ( SHORT ) pData - > data . mouse . usButtonData / 120 ;
// Update Z-axis (wheel) value
if ( butFlags & RI_MOUSE_WHEEL )
{
// Z-axis is clamped to range -100 to 100
2011-06-05 20:53:39 +00:00
pMseState - > z = CInputSource : : Clamp ( pMseState - > z + wheelDelta , - 100 , 100 ) ;
pMseState - > wheelDelta + = wheelDelta ;
2011-04-24 01:14:00 +00:00
}
// Keep track of buttons pressed/released
2011-06-05 20:53:39 +00:00
if ( butFlags & RI_MOUSE_LEFT_BUTTON_DOWN ) pMseState - > buttons | = 1 ;
else if ( butFlags & RI_MOUSE_LEFT_BUTTON_UP ) pMseState - > buttons & = ~ 1 ;
if ( butFlags & RI_MOUSE_MIDDLE_BUTTON_DOWN ) pMseState - > buttons | = 2 ;
else if ( butFlags & RI_MOUSE_MIDDLE_BUTTON_UP ) pMseState - > buttons & = ~ 2 ;
if ( butFlags & RI_MOUSE_RIGHT_BUTTON_DOWN ) pMseState - > buttons | = 4 ;
else if ( butFlags & RI_MOUSE_RIGHT_BUTTON_UP ) pMseState - > buttons & = ~ 4 ;
if ( butFlags & RI_MOUSE_BUTTON_4_DOWN ) pMseState - > buttons | = 8 ;
else if ( butFlags & RI_MOUSE_BUTTON_4_UP ) pMseState - > buttons & = ~ 8 ;
if ( butFlags & RI_MOUSE_BUTTON_5_DOWN ) pMseState - > buttons | = 16 ;
else if ( butFlags & RI_MOUSE_BUTTON_5_UP ) pMseState - > buttons & = ~ 16 ;
2011-04-24 01:14:00 +00:00
// Also update combined state for wheel axis and buttons
if ( butFlags & RI_MOUSE_WHEEL )
{
// Z-axis is clamped to range -100 to 100
m_combRawMseState . z = CInputSource : : Clamp ( m_combRawMseState . z + wheelDelta , - 100 , 100 ) ;
m_combRawMseState . wheelDelta + = wheelDelta ;
}
m_combRawMseState . buttons = 0 ;
2022-07-11 15:43:59 +00:00
for ( std : : vector < RawMseState > : : iterator it = m_rawMseStates . begin ( ) ; it ! = m_rawMseStates . end ( ) ; + + it )
2011-06-05 20:53:39 +00:00
m_combRawMseState . buttons | = it - > buttons ;
2011-04-24 01:14:00 +00:00
}
}
}
if ( pBuf ! = buffer )
delete [ ] pBuf ;
}
void CDirectInputSystem : : OpenJoysticks ( )
{
2011-06-05 20:53:39 +00:00
// Get the info about all attached joystick devices
2011-09-21 22:08:36 +00:00
DIEnumDevsContext diDevsContext ;
diDevsContext . infos = & m_diJoyInfos ;
diDevsContext . useXInput = m_useXInput ;
2011-04-24 01:14:00 +00:00
HRESULT hr ;
2011-09-21 22:08:36 +00:00
if ( FAILED ( hr = m_di8 - > EnumDevices ( DI8DEVCLASS_GAMECTRL , DI8EnumDevicesCallback , & diDevsContext , DIEDFL_ATTACHEDONLY ) ) )
2011-04-24 01:14:00 +00:00
return ;
// Loop through those found
int joyNum = 0 ;
2011-06-05 20:53:39 +00:00
int xNum = 0 ;
2022-07-11 15:43:59 +00:00
for ( std : : vector < DIJoyInfo > : : iterator it = m_diJoyInfos . begin ( ) ; it ! = m_diJoyInfos . end ( ) ; + + it )
2011-04-24 01:14:00 +00:00
{
joyNum + + ;
2011-06-05 20:53:39 +00:00
JoyDetails joyDetails ;
memset ( & joyDetails , 0 , sizeof ( joyDetails ) ) ;
2011-04-24 01:14:00 +00:00
2011-06-05 20:53:39 +00:00
// See if can use XInput for device
2011-06-22 13:06:52 +00:00
if ( it - > isXInput )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
// If so, set joystick details (currently XBox controller is only gamepad handled by XInput and so its capabilities are fixed)
sprintf ( joyDetails . name , " Xbox 360 Controller %d (via XInput) " , ( xNum + 1 ) ) ;
joyDetails . numAxes = 6 ; // Left & right triggers are mapped to axes in addition to the two analog sticks, giving a total of 6 axes
joyDetails . numPOVs = 1 ; // Digital D-pad
joyDetails . numButtons = 10 ;
2011-06-22 13:06:52 +00:00
joyDetails . hasFFeedback = m_enableFFeedback ;
2011-06-05 20:53:39 +00:00
joyDetails . hasAxis [ AXIS_X ] = true ;
joyDetails . hasAxis [ AXIS_Y ] = true ;
joyDetails . hasAxis [ AXIS_Z ] = true ;
joyDetails . hasAxis [ AXIS_RX ] = true ;
joyDetails . hasAxis [ AXIS_RY ] = true ;
joyDetails . hasAxis [ AXIS_RZ ] = true ;
2011-11-01 23:24:37 +00:00
joyDetails . hasAxis [ AXIS_S1 ] = false ;
joyDetails . hasAxis [ AXIS_S2 ] = false ;
2011-06-05 20:53:39 +00:00
joyDetails . axisHasFF [ AXIS_X ] = true ; // Force feedback simulated on left and right sticks
joyDetails . axisHasFF [ AXIS_Y ] = true ;
joyDetails . axisHasFF [ AXIS_Z ] = false ;
joyDetails . axisHasFF [ AXIS_RX ] = true ;
joyDetails . axisHasFF [ AXIS_RY ] = true ;
joyDetails . axisHasFF [ AXIS_RZ ] = false ;
2011-11-01 23:24:37 +00:00
joyDetails . axisHasFF [ AXIS_S1 ] = false ;
joyDetails . axisHasFF [ AXIS_S2 ] = false ;
2011-04-24 01:14:00 +00:00
2011-06-05 20:53:39 +00:00
// Keep track of XInput device number
it - > xInputNum = xNum + + ;
2011-04-24 01:14:00 +00:00
}
2011-06-05 20:53:39 +00:00
else
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
// Otherwise, open joystick with DirectInput for given GUID and set its data format
2011-06-22 13:06:52 +00:00
LPDIRECTINPUTDEVICE8 joystick ;
if ( FAILED ( hr = m_di8 - > CreateDevice ( it - > guid , & joystick , NULL ) ) )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
ErrorLog ( " Unable to create DirectInput joystick device %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
continue ;
}
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > SetDataFormat ( & c_dfDIJoystick2 ) ) )
2011-06-05 20:53:39 +00:00
{
ErrorLog ( " Unable to set data format for DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-04-24 01:14:00 +00:00
continue ;
}
2011-06-05 20:53:39 +00:00
// Get joystick's capabilities
DIDEVCAPS devCaps ;
devCaps . dwSize = sizeof ( DIDEVCAPS ) ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > GetCapabilities ( & devCaps ) ) )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
ErrorLog ( " Unable to query capabilities of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-04-24 01:14:00 +00:00
continue ;
}
2011-06-05 20:53:39 +00:00
// Gather joystick details (name, num POVs & buttons, which axes are available and whether force feedback is available)
2022-07-11 15:43:59 +00:00
DIPROPSTRING didps { } ;
2011-06-05 20:53:39 +00:00
didps . diph . dwSize = sizeof ( DIPROPSTRING ) ;
didps . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
didps . diph . dwHow = DIPH_DEVICE ;
didps . diph . dwObj = 0 ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > GetProperty ( DIPROP_INSTANCENAME , & didps . diph ) ) )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
ErrorLog ( " Unable to get name of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-04-24 01:14:00 +00:00
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-04-24 01:14:00 +00:00
continue ;
}
2011-06-05 20:53:39 +00:00
// DInput returns name as Unicode, convert to ASCII
2016-03-21 04:07:32 +00:00
int len = std : : min < int > ( MAX_NAME_LENGTH , ( int ) wcslen ( didps . wsz ) + 1 ) ;
2011-06-05 20:53:39 +00:00
WideCharToMultiByte ( CP_ACP , 0 , didps . wsz , len , joyDetails . name , len , NULL , NULL ) ;
joyDetails . name [ MAX_NAME_LENGTH - 1 ] = ' \0 ' ;
joyDetails . numPOVs = devCaps . dwPOVs ;
joyDetails . numButtons = devCaps . dwButtons ;
// Enumerate axes
2011-11-01 23:24:37 +00:00
DIEnumObjsContext diObjsContext ;
2012-02-27 20:32:15 +00:00
memset ( & diObjsContext , 0 , sizeof ( diObjsContext ) ) ;
2011-11-01 23:24:37 +00:00
diObjsContext . joyDetails = & joyDetails ;
if ( FAILED ( hr = joystick - > EnumObjects ( DI8EnumObjectsCallback , & diObjsContext , DIDFT_ALL ) ) )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
ErrorLog ( " Unable to enumerate axes of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-04-24 01:14:00 +00:00
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-04-24 01:14:00 +00:00
continue ;
}
2011-09-21 22:08:36 +00:00
// If enumeration failed for some reason then include all possible joystick axes so that no axis is left off due to error
2011-11-01 23:24:37 +00:00
if ( diObjsContext . enumError )
2011-09-21 22:08:36 +00:00
{
for ( int axisNum = 0 ; axisNum < NUM_JOY_AXES ; axisNum + + )
{
if ( ! joyDetails . hasAxis [ axisNum ] )
{
joyDetails . hasAxis [ axisNum ] = true ;
joyDetails . axisHasFF [ axisNum ] = false ;
char * axisName = joyDetails . axisName [ axisNum ] ;
strcpy ( axisName , CInputSystem : : GetDefaultAxisName ( axisNum ) ) ;
}
}
}
// Count number of axes
2011-06-05 20:53:39 +00:00
joyDetails . numAxes = 0 ;
for ( int axisNum = 0 ; axisNum < NUM_JOY_AXES ; axisNum + + )
joyDetails . numAxes + = joyDetails . hasAxis [ axisNum ] ;
2011-06-22 13:06:52 +00:00
// See if force feedback enabled and is available for joystick
if ( m_enableFFeedback & & ( devCaps . dwFlags & DIDC_FORCEFEEDBACK ) )
{
// If so, see what types of effects are available and for which axes
if ( FAILED ( hr = joystick - > EnumEffects ( DI8EnumEffectsCallback , & joyDetails , DIEFT_ALL ) ) )
ErrorLog ( " Unable to enumerate effects of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick. \n " , joyNum , hr ) ;
}
2011-06-05 20:53:39 +00:00
// Configure axes, if any
if ( joyDetails . numAxes > 0 )
{
// Set axis range to be from -32768 to 32767
DIPROPRANGE didpr ;
didpr . diph . dwSize = sizeof ( DIPROPRANGE ) ;
didpr . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
didpr . diph . dwHow = DIPH_DEVICE ;
didpr . diph . dwObj = 0 ;
didpr . lMin = - 32768 ;
didpr . lMax = 32767 ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > SetProperty ( DIPROP_RANGE , & didpr . diph ) ) )
2011-06-05 20:53:39 +00:00
{
ErrorLog ( " Unable to set axis range of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-06-05 20:53:39 +00:00
continue ;
}
// Set axis mode to absolute
DIPROPDWORD dipdw ;
dipdw . diph . dwSize = sizeof ( DIPROPDWORD ) ;
dipdw . diph . dwHeaderSize = sizeof ( DIPROPHEADER ) ;
dipdw . diph . dwHow = DIPH_DEVICE ;
dipdw . diph . dwObj = 0 ;
dipdw . dwData = DIPROPAXISMODE_ABS ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > SetProperty ( DIPROP_AXISMODE , & dipdw . diph ) ) )
2011-06-05 20:53:39 +00:00
{
ErrorLog ( " Unable to set axis mode of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-06-05 20:53:39 +00:00
continue ;
}
// Turn off deadzone as handled by this class
dipdw . dwData = 0 ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > SetProperty ( DIPROP_DEADZONE , & dipdw . diph ) ) )
2011-06-05 20:53:39 +00:00
{
ErrorLog ( " Unable to set deadzone of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-06-05 20:53:39 +00:00
continue ;
}
// Turn off saturation as handle by this class
dipdw . dwData = 10000 ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > SetProperty ( DIPROP_SATURATION , & dipdw . diph ) ) )
2011-06-05 20:53:39 +00:00
{
ErrorLog ( " Unable to set saturation of DirectInput joystick %d (error %d) - skipping joystick. \n " , joyNum , hr ) ;
2011-06-22 13:06:52 +00:00
joystick - > Release ( ) ;
2011-06-05 20:53:39 +00:00
continue ;
}
2011-06-22 13:06:52 +00:00
// If joystick has force feedback capabilities then disable auto-center
if ( joyDetails . hasFFeedback )
2011-06-05 20:53:39 +00:00
{
2011-09-08 06:34:18 +00:00
dipdw . dwData = false ;
2011-06-05 20:53:39 +00:00
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > SetProperty ( DIPROP_AUTOCENTER , & dipdw . diph ) ) )
2011-06-05 20:53:39 +00:00
{
ErrorLog ( " Unable to unset auto-center of DirectInput joystick %d (error %d) - force feedback will be unavailable for joystick. \n " , joyNum , hr ) ;
joyDetails . hasFFeedback = false ;
}
}
}
// Keep track of DirectInput device number
it - > dInputNum = m_di8Joysticks . size ( ) ;
2011-06-22 13:06:52 +00:00
m_di8Joysticks . push_back ( joystick ) ;
2011-04-24 01:14:00 +00:00
}
// Create initial blank joystick state
2011-06-05 20:53:39 +00:00
DIJOYSTATE2 joyState ;
2012-02-27 20:32:15 +00:00
memset ( & joyState , 0 , sizeof ( joyState ) ) ;
2011-04-24 01:14:00 +00:00
for ( int povNum = 0 ; povNum < 4 ; povNum + + )
2011-06-05 20:53:39 +00:00
joyState . rgdwPOV [ povNum ] = - 1 ;
2011-04-24 01:14:00 +00:00
m_joyDetails . push_back ( joyDetails ) ;
m_diJoyStates . push_back ( joyState ) ;
}
}
void CDirectInputSystem : : ActivateJoysticks ( )
{
// Set DirectInput cooperative level of joysticks
2011-06-22 13:06:52 +00:00
unsigned joyNum = 0 ;
2022-07-11 15:43:59 +00:00
for ( std : : vector < DIJoyInfo > : : iterator it = m_diJoyInfos . begin ( ) ; it ! = m_diJoyInfos . end ( ) ; + + it )
2011-06-22 13:06:52 +00:00
{
if ( ! it - > isXInput )
{
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks [ it - > dInputNum ] ;
2011-09-18 21:44:40 +00:00
joystick - > Unacquire ( ) ;
if ( m_grabMouse )
2011-06-22 13:06:52 +00:00
joystick - > SetCooperativeLevel ( m_hwnd , DISCL_EXCLUSIVE | DISCL_FOREGROUND ) ;
else
joystick - > SetCooperativeLevel ( m_hwnd , DISCL_NONEXCLUSIVE | DISCL_BACKGROUND ) ;
2011-09-18 21:44:40 +00:00
joystick - > Acquire ( ) ;
2011-06-22 13:06:52 +00:00
}
joyNum + + ;
}
2011-04-24 01:14:00 +00:00
}
void CDirectInputSystem : : PollJoysticks ( )
{
2011-06-05 20:53:39 +00:00
// Get current joystick states from XInput and DirectInput
int i = 0 ;
2022-07-11 15:43:59 +00:00
for ( std : : vector < DIJoyInfo > : : iterator it = m_diJoyInfos . begin ( ) ; it ! = m_diJoyInfos . end ( ) ; + + it )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
LPDIJOYSTATE2 pJoyState = & m_diJoyStates [ i + + ] ;
2011-04-24 01:14:00 +00:00
HRESULT hr ;
2011-06-22 13:06:52 +00:00
if ( it - > isXInput )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
// Use XInput to query joystick
XINPUT_STATE xState ;
2012-02-27 20:32:15 +00:00
memset ( & xState , 0 , sizeof ( xState ) ) ;
2011-06-05 20:53:39 +00:00
if ( FAILED ( hr = m_xiGetStatePtr ( it - > xInputNum , & xState ) ) )
{
memset ( pJoyState , 0 , sizeof ( DIJOYSTATE2 ) ) ;
2011-04-24 01:14:00 +00:00
continue ;
2011-06-05 20:53:39 +00:00
}
// Map XInput state onto joystick's DirectInput state object
XINPUT_GAMEPAD gamepad = xState . Gamepad ;
pJoyState - > lX = ( LONG ) gamepad . sThumbLX ,
pJoyState - > lY = ( LONG ) - gamepad . sThumbLY ;
pJoyState - > lZ = ( LONG ) CInputSource : : Scale ( gamepad . bLeftTrigger , 0 , 255 , 0 , 32767 ) ;
pJoyState - > lRx = ( LONG ) gamepad . sThumbRX ;
pJoyState - > lRy = ( LONG ) - gamepad . sThumbRY ;
pJoyState - > lRz = ( LONG ) CInputSource : : Scale ( gamepad . bRightTrigger , 0 , 255 , 0 , 32767 ) ;
WORD buttons = gamepad . wButtons ;
2011-09-08 06:34:18 +00:00
int dUp = ( buttons & XINPUT_GAMEPAD_DPAD_UP ) ;
int dDown = ( buttons & XINPUT_GAMEPAD_DPAD_DOWN ) ;
int dLeft = ( buttons & XINPUT_GAMEPAD_DPAD_LEFT ) ;
int dRight = ( buttons & XINPUT_GAMEPAD_DPAD_RIGHT ) ;
2011-06-05 20:53:39 +00:00
if ( dUp )
{
if ( dLeft ) pJoyState - > rgdwPOV [ 0 ] = 31500 ;
else if ( dRight ) pJoyState - > rgdwPOV [ 0 ] = 4500 ;
else pJoyState - > rgdwPOV [ 0 ] = 0 ;
}
else if ( dDown )
{
if ( dLeft ) pJoyState - > rgdwPOV [ 0 ] = 22500 ;
else if ( dRight ) pJoyState - > rgdwPOV [ 0 ] = 13500 ;
else pJoyState - > rgdwPOV [ 0 ] = 18000 ;
}
else if ( dLeft ) pJoyState - > rgdwPOV [ 0 ] = 27000 ;
else if ( dRight ) pJoyState - > rgdwPOV [ 0 ] = 9000 ;
else pJoyState - > rgdwPOV [ 0 ] = - 1 ;
pJoyState - > rgbButtons [ 0 ] = ! ! ( buttons & XINPUT_GAMEPAD_A ) ;
pJoyState - > rgbButtons [ 1 ] = ! ! ( buttons & XINPUT_GAMEPAD_B ) ;
pJoyState - > rgbButtons [ 2 ] = ! ! ( buttons & XINPUT_GAMEPAD_X ) ;
pJoyState - > rgbButtons [ 3 ] = ! ! ( buttons & XINPUT_GAMEPAD_Y ) ;
pJoyState - > rgbButtons [ 4 ] = ! ! ( buttons & XINPUT_GAMEPAD_LEFT_SHOULDER ) ;
pJoyState - > rgbButtons [ 5 ] = ! ! ( buttons & XINPUT_GAMEPAD_RIGHT_SHOULDER ) ;
pJoyState - > rgbButtons [ 6 ] = ! ! ( buttons & XINPUT_GAMEPAD_BACK ) ;
pJoyState - > rgbButtons [ 7 ] = ! ! ( buttons & XINPUT_GAMEPAD_START ) ;
pJoyState - > rgbButtons [ 8 ] = ! ! ( buttons & XINPUT_GAMEPAD_LEFT_THUMB ) ;
pJoyState - > rgbButtons [ 9 ] = ! ! ( buttons & XINPUT_GAMEPAD_RIGHT_THUMB ) ;
2011-04-24 01:14:00 +00:00
}
2011-06-05 20:53:39 +00:00
else
{
// Use DirectInput to query joystick
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks [ it - > dInputNum ] ;
if ( FAILED ( hr = joystick - > Poll ( ) ) )
{
hr = joystick - > Acquire ( ) ;
while ( hr = = DIERR_INPUTLOST )
hr = joystick - > Acquire ( ) ;
2011-04-24 01:14:00 +00:00
2011-06-05 20:53:39 +00:00
if ( hr = = DIERR_OTHERAPPHASPRIO | | hr = = DIERR_INVALIDPARAM | | hr = = DIERR_NOTINITIALIZED )
{
memset ( pJoyState , 0 , sizeof ( DIJOYSTATE2 ) ) ;
continue ;
}
}
// Update joystick's DirectInput state
joystick - > GetDeviceState ( sizeof ( DIJOYSTATE2 ) , pJoyState ) ;
}
2011-04-24 01:14:00 +00:00
}
}
void CDirectInputSystem : : CloseJoysticks ( )
{
2011-06-22 13:06:52 +00:00
// Release any DirectInput force feedback effects that were created
2022-07-11 15:43:59 +00:00
for ( std : : vector < DIJoyInfo > : : iterator it = m_diJoyInfos . begin ( ) ; it ! = m_diJoyInfos . end ( ) ; + + it )
2011-06-22 13:06:52 +00:00
{
for ( unsigned axisNum = 0 ; axisNum < NUM_JOY_AXES ; axisNum + + )
{
for ( unsigned effNum = 0 ; effNum < NUM_FF_EFFECTS ; effNum + + )
{
if ( it - > dInputEffects [ axisNum ] [ effNum ] ! = NULL )
{
it - > dInputEffects [ axisNum ] [ effNum ] - > Release ( ) ;
it - > dInputEffects [ axisNum ] [ effNum ] = NULL ;
}
}
}
}
2011-04-24 01:14:00 +00:00
// Release each DirectInput joystick
2022-07-11 15:43:59 +00:00
for ( std : : vector < LPDIRECTINPUTDEVICE8 > : : iterator it = m_di8Joysticks . begin ( ) ; it ! = m_di8Joysticks . end ( ) ; + + it )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
( * it ) - > Unacquire ( ) ;
( * it ) - > Release ( ) ;
2011-04-24 01:14:00 +00:00
}
m_joyDetails . clear ( ) ;
2011-06-05 20:53:39 +00:00
m_diJoyInfos . clear ( ) ;
2011-04-24 01:14:00 +00:00
m_diJoyStates . clear ( ) ;
2011-06-05 20:53:39 +00:00
m_di8Joysticks . clear ( ) ;
}
2011-06-22 13:06:52 +00:00
HRESULT CDirectInputSystem : : CreateJoystickEffect ( LPDIRECTINPUTDEVICE8 joystick , int axisNum , ForceFeedbackCmd ffCmd , LPDIRECTINPUTEFFECT * pEffect )
2011-06-05 20:53:39 +00:00
{
2011-06-22 13:06:52 +00:00
// Map axis number to DI object offset
2011-06-05 20:53:39 +00:00
DWORD axisOfs ;
switch ( axisNum )
{
case AXIS_X : axisOfs = DIJOFS_X ; break ;
2011-06-22 13:06:52 +00:00
case AXIS_Y : axisOfs = DIJOFS_Y ; break ;
case AXIS_Z : axisOfs = DIJOFS_Z ; break ;
case AXIS_RX : axisOfs = DIJOFS_RX ; break ;
case AXIS_RY : axisOfs = DIJOFS_RY ; break ;
case AXIS_RZ : axisOfs = DIJOFS_RZ ; break ;
2011-11-01 23:24:37 +00:00
case AXIS_S1 : axisOfs = DIJOFS_SLIDER ( 0 ) ; break ;
case AXIS_S2 : axisOfs = DIJOFS_SLIDER ( 1 ) ; break ;
2011-06-05 20:53:39 +00:00
default : return E_FAIL ;
}
2011-09-06 19:05:22 +00:00
DWORD dwAxis = axisOfs ;
LONG lDirection = 0 ;
2011-06-22 13:06:52 +00:00
DICONSTANTFORCE dicf ;
DICONDITION dic ;
DIPERIODIC dip ;
DIENVELOPE die ;
GUID guid ;
// Set common effects parameters
2011-06-05 20:53:39 +00:00
DIEFFECT eff ;
memset ( & eff , 0 , sizeof ( eff ) ) ;
eff . dwSize = sizeof ( DIEFFECT ) ;
eff . dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS ;
eff . dwTriggerButton = DIEB_NOTRIGGER ;
eff . dwTriggerRepeatInterval = 0 ;
2011-09-21 22:28:13 +00:00
eff . dwGain = DI_FFNOMINALMAX ;
2011-06-05 20:53:39 +00:00
eff . cAxes = 1 ;
2011-09-06 19:05:22 +00:00
eff . rgdwAxes = & dwAxis ;
eff . rglDirection = & lDirection ;
2011-06-22 13:06:52 +00:00
eff . dwDuration = INFINITE ;
2011-06-05 20:53:39 +00:00
eff . dwStartDelay = 0 ;
2011-09-06 19:05:22 +00:00
eff . lpEnvelope = NULL ;
2011-06-22 13:06:52 +00:00
// Set specific effects parameters
switch ( ffCmd . id )
{
case FFStop :
return E_FAIL ;
2011-09-06 19:05:22 +00:00
case FFConstantForce :
guid = GUID_ConstantForce ;
dicf . lMagnitude = 0 ;
2011-06-22 13:06:52 +00:00
2011-09-06 19:05:22 +00:00
eff . cbTypeSpecificParams = sizeof ( DICONSTANTFORCE ) ;
eff . lpvTypeSpecificParams = & dicf ;
break ;
2011-06-22 13:06:52 +00:00
case FFSelfCenter :
guid = GUID_Spring ;
2011-09-06 19:05:22 +00:00
dic . lOffset = 0 ; // offset is +ve/-ve bias, 0 = evenly spread in both directions
2011-06-22 13:06:52 +00:00
dic . lPositiveCoefficient = 0 ;
dic . lNegativeCoefficient = 0 ;
dic . dwPositiveSaturation = DI_FFNOMINALMAX ;
dic . dwNegativeSaturation = DI_FFNOMINALMAX ;
2011-11-01 23:24:37 +00:00
dic . lDeadBand = ( LONG ) ( 0.05 * DI_FFNOMINALMAX ) ; // 5% deadband
2011-06-22 13:06:52 +00:00
eff . cbTypeSpecificParams = sizeof ( DICONDITION ) ;
eff . lpvTypeSpecificParams = & dic ;
break ;
2011-09-06 19:05:22 +00:00
case FFFriction :
guid = GUID_Friction ;
dic . lOffset = 0 ;
dic . lPositiveCoefficient = 0 ;
dic . lNegativeCoefficient = 0 ;
dic . dwPositiveSaturation = DI_FFNOMINALMAX ;
dic . dwNegativeSaturation = DI_FFNOMINALMAX ;
dic . lDeadBand = 0 ; // 0% deadband
2011-06-22 13:06:52 +00:00
2011-09-06 19:05:22 +00:00
eff . cbTypeSpecificParams = sizeof ( DICONDITION ) ;
eff . lpvTypeSpecificParams = & dic ;
2011-06-22 13:06:52 +00:00
break ;
case FFVibrate :
guid = GUID_Sine ;
dip . dwMagnitude = 0 ;
2011-09-06 19:05:22 +00:00
dip . lOffset = 0 ;
dip . dwPhase = 0 ;
dip . dwPeriod = ( DWORD ) ( 0.05 * DI_SECONDS ) ; // 1/20th second
2011-06-22 13:06:52 +00:00
eff . cbTypeSpecificParams = sizeof ( DIPERIODIC ) ;
eff . lpvTypeSpecificParams = & dip ;
break ;
}
joystick - > Acquire ( ) ;
2011-06-05 20:53:39 +00:00
HRESULT hr ;
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = joystick - > CreateEffect ( guid , & eff , pEffect , NULL ) ) )
2011-06-05 20:53:39 +00:00
return hr ;
2011-06-22 13:06:52 +00:00
if ( * pEffect = = NULL )
2011-06-05 20:53:39 +00:00
return E_FAIL ;
2011-06-22 13:06:52 +00:00
( * pEffect ) - > Start ( 1 , 0 ) ;
2011-06-05 20:53:39 +00:00
return S_OK ;
2011-04-24 01:14:00 +00:00
}
2016-06-02 00:13:42 +00:00
void CDirectInputSystem : : LoadXInputDLL ( )
{
// Try each of the XInput DLLs
HMODULE xInput = NULL ;
for ( auto filename : s_xinput_dlls )
{
xInput = LoadLibrary ( filename ) ;
if ( xInput ! = NULL )
break ;
}
if ( xInput ! = NULL )
{
m_xiGetCapabilitiesPtr = ( XInputGetCapabilitiesPtr ) GetProcAddress ( xInput , " XInputGetCapabilities " ) ;
m_xiGetStatePtr = ( XInputGetStatePtr ) GetProcAddress ( xInput , " XInputGetState " ) ;
m_xiSetStatePtr = ( XInputSetStatePtr ) GetProcAddress ( xInput , " XInputSetState " ) ;
m_useXInput = m_xiGetCapabilitiesPtr ! = NULL & & m_xiGetStatePtr ! = NULL & & m_xiSetStatePtr ! = NULL ;
}
else
m_useXInput = false ;
if ( ! m_useXInput )
{
ErrorLog ( " XInput not found. Tried: %s. " , Util : : Format ( " , " ) . Join ( s_xinput_dlls_a ) . str ( ) . c_str ( ) ) ;
ErrorLog ( " Falling back on DirectInput. " ) ;
}
}
2011-04-24 01:14:00 +00:00
bool CDirectInputSystem : : InitializeSystem ( )
{
if ( m_useRawInput )
{
// Dynamically load RawInput API
HMODULE user32 = LoadLibrary ( TEXT ( " user32.dll " ) ) ;
if ( user32 ! = NULL )
{
m_getRIDevListPtr = ( GetRawInputDeviceListPtr ) GetProcAddress ( user32 , " GetRawInputDeviceList " ) ;
m_getRIDevInfoPtr = ( GetRawInputDeviceInfoPtr ) GetProcAddress ( user32 , " GetRawInputDeviceInfoA " ) ;
m_regRIDevsPtr = ( RegisterRawInputDevicesPtr ) GetProcAddress ( user32 , " RegisterRawInputDevices " ) ;
m_getRIDataPtr = ( GetRawInputDataPtr ) GetProcAddress ( user32 , " GetRawInputData " ) ;
m_useRawInput = m_getRIDevListPtr ! = NULL & & m_getRIDevInfoPtr ! = NULL & & m_regRIDevsPtr ! = NULL & & m_getRIDataPtr ! = NULL ;
}
else
m_useRawInput = false ;
2011-06-05 20:53:39 +00:00
if ( m_useRawInput )
{
// Get screen resolution (needed for absolute mouse devices)
2018-09-14 20:31:34 +00:00
DEVMODEA settings = { 0 } ;
2011-06-05 20:53:39 +00:00
if ( ! EnumDisplaySettings ( NULL , ENUM_CURRENT_SETTINGS , & settings ) )
{
ErrorLog ( " Unable to read current display settings \n " ) ;
return false ;
}
m_screenW = settings . dmPelsWidth ;
m_screenH = settings . dmPelsHeight ;
}
else
2011-04-24 01:14:00 +00:00
ErrorLog ( " Unable to initialize RawInput API (library hooks are not available) - switching to DirectInput. \n " ) ;
}
2011-06-05 20:53:39 +00:00
if ( m_useXInput )
{
// Dynamically load XInput API
2016-06-02 00:13:42 +00:00
LoadXInputDLL ( ) ; //LoadLibrary(TEXT(XINPUT_DLL_A));
2011-06-05 20:53:39 +00:00
}
2011-04-24 01:14:00 +00:00
// Dynamically create DirectInput8 via COM, rather than statically linking to dinput8.dll
// TODO - if fails, try older versions of DirectInput
HRESULT hr ;
2011-06-27 23:06:08 +00:00
if ( SUCCEEDED ( hr = CoInitialize ( NULL ) ) )
m_initializedCOM = true ;
else
2011-04-24 01:14:00 +00:00
{
2011-06-27 23:06:08 +00:00
// CoInitialize fails if called from managed context (ie .NET debugger) so check for this and ignore this error
if ( hr ! = RPC_E_CHANGED_MODE )
{
ErrorLog ( " Unable to initialize COM (error %d). \n " , hr ) ;
2011-04-24 01:14:00 +00:00
2011-06-27 23:06:08 +00:00
return false ;
}
2011-04-24 01:14:00 +00:00
}
if ( FAILED ( hr = CoCreateInstance ( CLSID_DirectInput8 , NULL , CLSCTX_INPROC_SERVER , IID_IDirectInput8A , ( LPVOID * ) & m_di8 ) ) )
{
ErrorLog ( " Unable to initialize DirectInput API (error %d) - is DirectX 8 or later installed? \n " , hr ) ;
2011-06-27 23:06:08 +00:00
if ( m_initializedCOM )
CoUninitialize ( ) ;
2011-04-24 01:14:00 +00:00
return false ;
}
if ( FAILED ( hr = m_di8 - > Initialize ( GetModuleHandle ( NULL ) , DIRECTINPUT_VERSION ) ) )
{
ErrorLog ( " Unable to initialize DirectInput API (error %d) - is DirectX 8 or later installed? \n " , hr ) ;
m_di8 - > Release ( ) ;
2011-06-05 20:53:39 +00:00
m_di8 = NULL ;
2011-06-27 23:06:08 +00:00
if ( m_initializedCOM )
CoUninitialize ( ) ;
2011-04-24 01:14:00 +00:00
return false ;
}
// Open all devices
OpenKeyboardsAndMice ( ) ;
OpenJoysticks ( ) ;
2011-06-05 20:53:39 +00:00
return true ;
2011-04-24 01:14:00 +00:00
}
int CDirectInputSystem : : GetKeyIndex ( const char * keyName )
{
for ( int i = 0 ; i < NUM_DI_KEYS ; i + + )
{
if ( stricmp ( keyName , s_keyMap [ i ] . keyName ) = = 0 )
return i ;
}
return - 1 ;
}
const char * CDirectInputSystem : : GetKeyName ( int keyIndex )
{
if ( keyIndex < 0 | | keyIndex > = NUM_DI_KEYS )
return NULL ;
return s_keyMap [ keyIndex ] . keyName ;
}
bool CDirectInputSystem : : IsKeyPressed ( int kbdNum , int keyIndex )
{
// Get DI key code (scancode) for given key index
int diKey = s_keyMap [ keyIndex ] . diKey ;
if ( m_useRawInput )
{
// For RawInput, check if key is currently pressed for given keyboard number
2011-09-08 06:34:18 +00:00
bool * keyState = m_rawKeyStates [ kbdNum ] ;
2011-04-24 01:14:00 +00:00
return ! ! keyState [ diKey ] ;
}
// For DirectInput, just check common keyboard state
return ! ! ( m_diKeyState [ diKey ] & 0x80 ) ;
}
int CDirectInputSystem : : GetMouseAxisValue ( int mseNum , int axisNum )
{
if ( m_useRawInput )
{
// For RawInput, get combined or individual mouse state and return value for given axis
// The cursor is always hidden when using RawInput, so it does not matter if these values don't match with the cursor (with multiple
// mice the cursor is irrelevant anyway)
2011-06-05 20:53:39 +00:00
RawMseState * pMseState = ( mseNum = = ANY_MOUSE ? & m_combRawMseState : & m_rawMseStates [ mseNum ] ) ;
2011-04-24 01:14:00 +00:00
switch ( axisNum )
{
2011-06-05 20:53:39 +00:00
case AXIS_X : return pMseState - > x ;
case AXIS_Y : return pMseState - > y ;
case AXIS_Z : return pMseState - > z ;
2011-04-24 01:14:00 +00:00
default : return 0 ;
}
}
// For DirectInput, for X- and Y-axes just use cursor position within window if available (so that mouse movements sync with the cursor)
if ( axisNum = = AXIS_X | | axisNum = = AXIS_Y )
{
POINT p ;
if ( GetCursorPos ( & p ) & & ScreenToClient ( m_hwnd , & p ) )
return ( axisNum = = AXIS_X ? p . x : p . y ) ;
}
// Otherwise, return the raw DirectInput axis values
switch ( axisNum )
{
case AXIS_X : return m_diMseState . x ;
case AXIS_Y : return m_diMseState . y ;
case AXIS_Z : return m_diMseState . z ;
default : return 0 ;
}
}
int CDirectInputSystem : : GetMouseWheelDir ( int mseNum )
{
if ( m_useRawInput )
{
2011-06-05 20:53:39 +00:00
// For RawInput, return the wheel value for combined or individual mouse state
return ( mseNum = = ANY_MOUSE ? m_combRawMseState . wheelDir : m_rawMseStates [ mseNum ] . wheelDir ) ;
2011-04-24 01:14:00 +00:00
}
// For DirectInput just return the common wheel value
return m_diMseState . wheelDir ;
}
bool CDirectInputSystem : : IsMouseButPressed ( int mseNum , int butNum )
{
if ( m_useRawInput )
{
2011-06-05 20:53:39 +00:00
// For RawInput, return the button state for combined or individual mouse state
return ! ! ( ( mseNum = = ANY_MOUSE ? m_combRawMseState . buttons : m_rawMseStates [ mseNum ] . buttons ) & ( 1 < < butNum ) ) ;
2011-04-24 01:14:00 +00:00
}
// For DirectInput just return the common button state (taking care with the middle and right mouse buttons
// which DirectInput numbers 2 and 1 respectively, rather than 1 and 2)
if ( butNum = = 1 ) butNum = 2 ;
else if ( butNum = = 2 ) butNum = 1 ;
return ( butNum < 5 ? ! ! ( m_diMseState . buttons [ butNum ] & 0x80 ) : false ) ;
}
int CDirectInputSystem : : GetJoyAxisValue ( int joyNum , int axisNum )
{
2011-06-05 20:53:39 +00:00
// Return raw value for given joystick number and axis (values range from -32768 to 32767)
2011-04-24 01:14:00 +00:00
switch ( axisNum )
{
2011-06-05 20:53:39 +00:00
case AXIS_X : return ( int ) m_diJoyStates [ joyNum ] . lX ;
case AXIS_Y : return ( int ) m_diJoyStates [ joyNum ] . lY ;
case AXIS_Z : return ( int ) m_diJoyStates [ joyNum ] . lZ ;
case AXIS_RX : return ( int ) m_diJoyStates [ joyNum ] . lRx ;
case AXIS_RY : return ( int ) m_diJoyStates [ joyNum ] . lRy ;
case AXIS_RZ : return ( int ) m_diJoyStates [ joyNum ] . lRz ;
2011-11-01 23:24:37 +00:00
case AXIS_S1 : return ( int ) m_diJoyStates [ joyNum ] . rglSlider [ 0 ] ;
case AXIS_S2 : return ( int ) m_diJoyStates [ joyNum ] . rglSlider [ 1 ] ;
2011-04-24 01:14:00 +00:00
default : return 0 ;
}
}
bool CDirectInputSystem : : IsJoyPOVInDir ( int joyNum , int povNum , int povDir )
{
2011-06-05 20:53:39 +00:00
// Check if POV-hat value for given joystick number and POV is pointing in required direction
int povVal = m_diJoyStates [ joyNum ] . rgdwPOV [ povNum ] / 100 ; // DirectInput value is angle of POV-hat in 100ths of a degree
2011-04-24 01:14:00 +00:00
switch ( povDir )
{
case POV_UP : return povVal = = 315 | | povVal = = 0 | | povVal = = 45 ;
case POV_DOWN : return povVal = = 135 | | povVal = = 180 | | povVal = = 225 ;
case POV_RIGHT : return povVal = = 45 | | povVal = = 90 | | povVal = = 135 ;
case POV_LEFT : return povVal = = 225 | | povVal = = 270 | | povVal = = 315 ;
default : return false ;
}
}
bool CDirectInputSystem : : IsJoyButPressed ( int joyNum , int butNum )
{
// Get joystick state for given joystick and return current button value for given button number
2011-06-05 20:53:39 +00:00
return ! ! m_diJoyStates [ joyNum ] . rgbButtons [ butNum ] ;
}
2011-06-22 13:06:52 +00:00
bool CDirectInputSystem : : ProcessForceFeedbackCmd ( int joyNum , int axisNum , ForceFeedbackCmd ffCmd )
2011-06-05 20:53:39 +00:00
{
DIJoyInfo * pInfo = & m_diJoyInfos [ joyNum ] ;
HRESULT hr ;
2011-06-22 13:06:52 +00:00
if ( pInfo - > isXInput )
2011-06-05 20:53:39 +00:00
{
2011-09-06 19:05:22 +00:00
if ( axisNum ! = AXIS_X & & axisNum ! = AXIS_Y & & axisNum ! = AXIS_RX & & axisNum ! = AXIS_RY )
return false ;
2011-06-05 20:53:39 +00:00
XINPUT_VIBRATION vibration ;
2011-09-06 19:05:22 +00:00
bool negForce ;
float absForce ;
2011-09-12 20:05:58 +00:00
float threshold ;
2011-06-22 13:06:52 +00:00
switch ( ffCmd . id )
2011-06-05 20:53:39 +00:00
{
case FFStop :
2011-09-06 19:05:22 +00:00
// Stop command halts all vibration
2011-09-13 22:50:50 +00:00
pInfo - > xiConstForceLeft = 0 ;
pInfo - > xiConstForceRight = 0 ;
pInfo - > xiVibrateBoth = 0 ;
break ;
2011-06-05 20:53:39 +00:00
case FFConstantForce :
2021-01-30 09:54:03 +00:00
{
bool bothMotorVib = m_config [ " XInputStereoVibration " ] . ValueAs < bool > ( ) ;
2011-09-13 22:50:50 +00:00
// Check if constant force effect is disabled
2017-03-27 03:19:15 +00:00
unsigned xInputConstForceMax = m_config [ " XInputConstForceMax " ] . ValueAs < unsigned > ( ) ;
if ( xInputConstForceMax = = 0 )
2011-09-12 20:05:58 +00:00
return false ;
2011-09-13 22:50:50 +00:00
// Constant force effect is mapped to either left or right vibration motor depending on its direction
2011-09-06 19:05:22 +00:00
negForce = ffCmd . force < 0.0f ;
absForce = ( negForce ? - ffCmd . force : ffCmd . force ) ;
2017-03-27 03:19:15 +00:00
threshold = ( float ) m_config [ " XInputConstForceThreshold " ] . ValueAs < unsigned > ( ) / 100.0f ;
2011-09-13 22:50:50 +00:00
// Check if constant force effect is being stopped or is below threshold
if ( absForce = = 0.0f | | absForce < threshold )
{
// If so, stop vibration due to force effect
pInfo - > xiConstForceLeft = 0 ;
pInfo - > xiConstForceRight = 0 ;
2021-01-30 09:54:03 +00:00
pInfo - > xiVibrateBoth = 0 ;
}
else if ( bothMotorVib )
{
pInfo - > xiVibrateBoth = ( WORD ) ( absForce * ( float ) ( xInputConstForceMax * XI_VIBRATE_SCALE ) ) ;
2011-09-13 22:50:50 +00:00
}
else if ( negForce )
{
// If force is negative (to left), set left motor vibrating
2017-03-27 03:19:15 +00:00
pInfo - > xiConstForceLeft = ( WORD ) ( absForce * ( float ) ( xInputConstForceMax * XI_VIBRATE_SCALE ) ) ;
2011-09-13 22:50:50 +00:00
pInfo - > xiConstForceRight = 0 ;
}
2011-09-06 19:05:22 +00:00
else
2011-09-13 22:50:50 +00:00
{
// If force positive (to right), set right motor vibrating
pInfo - > xiConstForceLeft = 0 ;
2017-03-27 03:19:15 +00:00
pInfo - > xiConstForceRight = ( WORD ) ( absForce * ( float ) ( xInputConstForceMax * XI_VIBRATE_SCALE ) ) ;
2011-09-13 22:50:50 +00:00
}
break ;
2017-03-27 03:19:15 +00:00
}
2011-09-06 19:05:22 +00:00
case FFSelfCenter :
case FFFriction :
// Self center and friction effects are not mapped
return false ;
case FFVibrate :
2017-03-27 03:19:15 +00:00
{
// Check if vibration effect is disabled
unsigned xInputVibrateMax = m_config [ " XInputVibrateMax " ] . ValueAs < unsigned > ( ) ;
if ( xInputVibrateMax = = 0 )
2011-09-12 20:05:58 +00:00
return false ;
2011-09-13 22:50:50 +00:00
// Check if vibration effect is being stopped
if ( ffCmd . force = = 0.0f )
{
// If so, stop vibration due to vibration effect
pInfo - > xiVibrateBoth = 0 ;
}
else
{
// Otherwise, set both motors vibrating
2017-03-27 03:19:15 +00:00
pInfo - > xiVibrateBoth = ( WORD ) ( ffCmd . force * ( float ) ( xInputVibrateMax * XI_VIBRATE_SCALE ) ) ;
2011-09-13 22:50:50 +00:00
}
break ;
2017-03-27 03:19:15 +00:00
}
2011-06-22 13:06:52 +00:00
default :
2011-09-06 19:05:22 +00:00
// Unknown feedback command
2011-06-22 13:06:52 +00:00
return false ;
2011-06-05 20:53:39 +00:00
}
2011-09-13 22:50:50 +00:00
// Combine vibration speeds from both constant force effect and vibration effect and set motors in action
2016-03-21 04:07:32 +00:00
vibration . wLeftMotorSpeed = std : : min < WORD > ( pInfo - > xiConstForceLeft + pInfo - > xiVibrateBoth , XI_VIBRATE_MAX ) ;
vibration . wRightMotorSpeed = std : : min < WORD > ( pInfo - > xiConstForceRight + pInfo - > xiVibrateBoth , XI_VIBRATE_MAX ) ;
2011-09-13 22:50:50 +00:00
return SUCCEEDED ( hr = m_xiSetStatePtr ( pInfo - > xInputNum , & vibration ) ) ;
2011-06-05 20:53:39 +00:00
}
else
{
2011-06-22 13:06:52 +00:00
LPDIRECTINPUTDEVICE8 joystick = m_di8Joysticks [ pInfo - > dInputNum ] ;
2011-09-06 19:05:22 +00:00
// See if command is to stop all force feedback, if so send appropriate command
if ( ffCmd . id = = FFStop )
2011-06-22 13:06:52 +00:00
return SUCCEEDED ( hr = joystick - > SendForceFeedbackCommand ( DISFFC_STOPALL ) ) ;
2011-06-27 23:06:08 +00:00
2011-06-22 13:06:52 +00:00
// Create effect for given axis if has not already been created
int effNum = ( int ) ffCmd . id ;
LPDIRECTINPUTEFFECT * pEffect = & pInfo - > dInputEffects [ axisNum ] [ effNum ] ;
if ( ( * pEffect ) = = NULL )
2011-06-05 20:53:39 +00:00
{
2011-06-22 13:06:52 +00:00
if ( FAILED ( hr = CreateJoystickEffect ( joystick , axisNum , ffCmd , pEffect ) ) )
return false ;
}
2011-09-06 19:05:22 +00:00
LONG lDirection = 0 ;
2011-06-22 13:06:52 +00:00
DICONSTANTFORCE dicf ;
DICONDITION dic ;
DIPERIODIC dip ;
DIENVELOPE die ;
// Set common parameters
DIEFFECT eff ;
memset ( & eff , 0 , sizeof ( eff ) ) ;
eff . dwSize = sizeof ( DIEFFECT ) ;
eff . dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS ;
eff . cAxes = 1 ;
2011-09-06 19:05:22 +00:00
eff . rglDirection = & lDirection ;
2011-06-22 13:06:52 +00:00
eff . dwStartDelay = 0 ;
2011-09-06 19:05:22 +00:00
eff . lpEnvelope = NULL ;
2011-06-22 13:06:52 +00:00
2011-09-06 19:05:22 +00:00
// Set command specific parameters
2011-09-25 22:46:58 +00:00
LONG lFFMag ;
DWORD dFFMag ;
2011-06-22 13:06:52 +00:00
switch ( ffCmd . id )
{
2011-09-06 19:05:22 +00:00
case FFConstantForce :
//printf("FFConstantForce %0.2f\n", 100.0f * ffCmd.force);
2011-10-02 20:53:12 +00:00
if ( ffCmd . force > = 0.0f )
{
2017-03-27 03:19:15 +00:00
unsigned dInputConstForceRightMax = m_config [ " DirectInputConstForceRightMax " ] . ValueAs < unsigned > ( ) ;
if ( dInputConstForceRightMax = = 0 )
2011-10-02 20:53:12 +00:00
return false ;
2017-03-27 03:19:15 +00:00
lFFMag = ( LONG ) ( - ffCmd . force * ( float ) ( dInputConstForceRightMax * DI_EFFECTS_SCALE ) ) ; // Invert sign for DirectInput effect
2016-03-21 04:07:32 +00:00
dicf . lMagnitude = std : : max < LONG > ( lFFMag , - DI_EFFECTS_MAX ) ;
2011-10-02 20:53:12 +00:00
}
else
{
2017-03-27 03:19:15 +00:00
unsigned dInputConstForceLeftMax = m_config [ " DirectInputConstForceLeftMax " ] . ValueAs < unsigned > ( ) ;
if ( dInputConstForceLeftMax = = 0 )
2011-10-02 20:53:12 +00:00
return false ;
2017-03-27 03:19:15 +00:00
lFFMag = ( LONG ) ( - ffCmd . force * ( float ) ( dInputConstForceLeftMax * DI_EFFECTS_SCALE ) ) ; // Invert sign for DirectInput effect
2016-03-21 04:07:32 +00:00
dicf . lMagnitude = std : : min < LONG > ( lFFMag , DI_EFFECTS_MAX ) ;
2011-10-02 20:53:12 +00:00
}
2011-11-01 23:24:37 +00:00
2011-09-06 19:05:22 +00:00
eff . cbTypeSpecificParams = sizeof ( DICONSTANTFORCE ) ;
eff . lpvTypeSpecificParams = & dicf ;
break ;
2011-06-22 13:06:52 +00:00
case FFSelfCenter :
2017-03-27 03:19:15 +00:00
{
unsigned dInputSelfCenterMax = m_config [ " DirectInputSelfCenterMax " ] . ValueAs < unsigned > ( ) ;
2011-09-06 19:05:22 +00:00
//printf("FFSelfCenter %0.2f\n", 100.0f * ffCmd.force);
2017-03-27 03:19:15 +00:00
if ( dInputSelfCenterMax = = 0 )
2011-09-12 20:05:58 +00:00
return false ;
2017-03-27 03:19:15 +00:00
lFFMag = ( LONG ) ( ffCmd . force * ( float ) ( dInputSelfCenterMax * DI_EFFECTS_SCALE ) ) ;
2011-09-06 19:05:22 +00:00
dic . lOffset = 0 ;
2016-03-21 04:07:32 +00:00
dic . lPositiveCoefficient = std : : max < LONG > ( 0 , std : : min < LONG > ( lFFMag , DI_EFFECTS_MAX ) ) ;
dic . lNegativeCoefficient = std : : max < LONG > ( 0 , std : : min < LONG > ( lFFMag , DI_EFFECTS_MAX ) ) ;
2011-06-22 13:06:52 +00:00
dic . dwPositiveSaturation = DI_FFNOMINALMAX ;
dic . dwNegativeSaturation = DI_FFNOMINALMAX ;
2011-09-06 19:05:22 +00:00
dic . lDeadBand = ( LONG ) ( 0.05 * DI_FFNOMINALMAX ) ;
2011-06-22 13:06:52 +00:00
eff . cbTypeSpecificParams = sizeof ( DICONDITION ) ;
eff . lpvTypeSpecificParams = & dic ;
2011-06-05 20:53:39 +00:00
break ;
2017-03-27 03:19:15 +00:00
}
2011-09-06 19:05:22 +00:00
case FFFriction :
2017-03-27 03:19:15 +00:00
{
unsigned dInputFrictionMax = m_config [ " DirectInputFrictionMax " ] . ValueAs < unsigned > ( ) ;
2011-09-06 19:05:22 +00:00
//printf("FFFriction %0.2f\n", 100.0f * ffCmd.force);
2017-03-27 03:19:15 +00:00
if ( dInputFrictionMax = = 0 )
2011-09-12 20:05:58 +00:00
return false ;
2017-03-27 03:19:15 +00:00
lFFMag = ( LONG ) ( ffCmd . force * ( float ) ( dInputFrictionMax * DI_EFFECTS_SCALE ) ) ;
2011-09-06 19:05:22 +00:00
dic . lOffset = 0 ;
2016-03-21 04:07:32 +00:00
dic . lPositiveCoefficient = std : : max < LONG > ( 0 , std : : min < LONG > ( lFFMag , DI_EFFECTS_MAX ) ) ;
dic . lNegativeCoefficient = std : : max < LONG > ( 0 , std : : min < LONG > ( lFFMag , DI_EFFECTS_MAX ) ) ;
2011-09-06 19:05:22 +00:00
dic . dwPositiveSaturation = DI_FFNOMINALMAX ;
dic . dwNegativeSaturation = DI_FFNOMINALMAX ;
dic . lDeadBand = 0 ;
eff . cbTypeSpecificParams = sizeof ( DICONDITION ) ;
eff . lpvTypeSpecificParams = & dic ;
2011-06-22 13:06:52 +00:00
break ;
2017-03-27 03:19:15 +00:00
}
2011-06-22 13:06:52 +00:00
case FFVibrate :
2017-03-27 03:19:15 +00:00
{
unsigned dInputVibrateMax = m_config [ " DirectInputVibrateMax " ] . ValueAs < unsigned > ( ) ;
2011-09-06 19:05:22 +00:00
//printf("FFVibrate %0.2f\n", 100.0f * ffCmd.force);
2017-03-27 03:19:15 +00:00
if ( dInputVibrateMax = = 0 )
2011-09-12 20:05:58 +00:00
return false ;
2017-03-27 03:19:15 +00:00
dFFMag = ( DWORD ) ( ffCmd . force * ( float ) ( dInputVibrateMax * DI_EFFECTS_SCALE ) ) ;
2016-03-21 04:07:32 +00:00
dip . dwMagnitude = std : : max < DWORD > ( 0 , std : : min < DWORD > ( dFFMag , DI_EFFECTS_MAX ) ) ;
2011-06-22 13:06:52 +00:00
dip . lOffset = 0 ;
dip . dwPhase = 0 ;
2011-09-06 19:05:22 +00:00
dip . dwPeriod = ( DWORD ) ( 0.05 * DI_SECONDS ) ; // 1/20th second
2011-06-22 13:06:52 +00:00
eff . cbTypeSpecificParams = sizeof ( DIPERIODIC ) ;
eff . lpvTypeSpecificParams = & dip ;
2011-06-05 20:53:39 +00:00
break ;
2017-03-27 03:19:15 +00:00
}
2011-06-22 13:06:52 +00:00
default :
2011-09-06 19:05:22 +00:00
// Unknown feedback command
2011-06-22 13:06:52 +00:00
return false ;
2011-06-05 20:53:39 +00:00
}
2011-09-06 19:05:22 +00:00
// Set the new parameters and start effect immediately
2011-06-22 13:06:52 +00:00
return SUCCEEDED ( hr = ( * pEffect ) - > SetParameters ( & eff , DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START ) ) ;
}
2011-04-24 01:14:00 +00:00
}
bool CDirectInputSystem : : ConfigMouseCentered ( )
{
// When checking if mouse centered, use system cursor rather than raw values (otherwise user's mouse movements won't match up
// with onscreen cursor during configuration)
POINT p ;
if ( ! GetCursorPos ( & p ) | | ! ScreenToClient ( m_hwnd , & p ) )
return false ;
// See if mouse in center of display
unsigned lx = m_dispX + m_dispW / 4 ;
unsigned ly = m_dispY + m_dispH / 4 ;
2011-06-05 20:53:39 +00:00
if ( p . x < ( LONG ) lx | | p . x > ( LONG ) ( lx + m_dispW / 2 ) | | p . y < ( LONG ) ly | | p . y > ( LONG ) ( ly + m_dispH / 2 ) )
2011-04-24 01:14:00 +00:00
return false ;
// Once mouse has been centered, sync up mice raw values with current cursor position so that movements are detected correctly
ResetMice ( ) ;
return true ;
}
CInputSource * CDirectInputSystem : : CreateAnyMouseSource ( EMousePart msePart )
{
// If using RawInput, create a mouse source that uses the combined mouse state m_combRawState, rather than combining all the individual mouse
// sources in the default manner
if ( m_useRawInput )
return CreateMouseSource ( ANY_MOUSE , msePart ) ;
return CInputSystem : : CreateAnyMouseSource ( msePart ) ;
}
2011-06-05 20:53:39 +00:00
int CDirectInputSystem : : GetNumKeyboards ( )
{
// If RawInput enabled, then return number of keyboards found. Otherwise, return ANY_KEYBOARD as DirectInput cannot handle multiple keyboards
return ( m_useRawInput ? m_rawKeyboards . size ( ) : ANY_KEYBOARD ) ;
}
int CDirectInputSystem : : GetNumMice ( )
{
// If RawInput enabled, then return number of mice found. Otherwise, return ANY_MOUSE as DirectInput cannot handle multiple keyboards
return ( m_useRawInput ? m_rawMice . size ( ) : ANY_MOUSE ) ;
}
int CDirectInputSystem : : GetNumJoysticks ( )
{
// Return number of joysticks found
return m_diJoyInfos . size ( ) ;
}
const KeyDetails * CDirectInputSystem : : GetKeyDetails ( int kbdNum )
{
// If RawInput enabled, then return details for given keyboard. Otherwise, return NULL as DirectInput cannot handle multiple keyboards
return ( m_useRawInput ? & m_keyDetails [ kbdNum ] : NULL ) ;
}
const MouseDetails * CDirectInputSystem : : GetMouseDetails ( int mseNum )
{
// If RawInput enabled, then return details of given mouse. Otherwise, return NULL as DirectInput cannot handle multiple keyboards
return ( m_useRawInput ? & m_mseDetails [ mseNum ] : NULL ) ;
}
const JoyDetails * CDirectInputSystem : : GetJoyDetails ( int joyNum )
{
return & m_joyDetails [ joyNum ] ;
}
2011-04-24 01:14:00 +00:00
bool CDirectInputSystem : : Poll ( )
{
// See if keyboard, mice and joysticks have been activated yet
if ( ! m_activated )
{
// If not, then get Window handle of SDL window
SDL_SysWMinfo info ;
2020-04-19 08:34:58 +00:00
SDL_VERSION ( & info . version ) ;
if ( SDL_GetWindowWMInfo ( m_window , & info ) )
{
m_hwnd = info . info . win . window ;
}
2011-04-24 01:14:00 +00:00
// Tell SDL to pass on all Windows events
// Removed - see below
//SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
// Activate the devices now that a Window handle is available
ActivateKeyboardsAndMice ( ) ;
ActivateJoysticks ( ) ;
m_activated = true ;
}
// Wait or poll for event from SDL
// Removed - see below
/*
SDL_Event e ;
while ( SDL_PollEvent ( & e ) )
{
if ( e . type = = SDL_QUIT )
return false ;
else if ( e . type = = SDL_SYSWMEVENT )
{
SDL_SysWMmsg * wmMsg = e . syswm . msg ;
ProcessWMEvent ( wmMsg - > hwnd , wmMsg - > msg , wmMsg - > wParam , wmMsg - > lParam ) ;
}
} */
// Wait or poll for event on Windows message queue (done this way instead of using SDL_PollEvent as above because
// for some reason this causes RawInput HRAWINPUT handles to arrive stale. Not sure what SDL_PollEvent is doing to cause this
// but the following code can replace it without any problems as it is effectively what SDL_PollEvent does anyway)
MSG msg ;
while ( PeekMessage ( & msg , NULL , 0 , 0 , PM_NOREMOVE ) )
{
int ret = GetMessage ( & msg , NULL , 0 , 0 ) ;
if ( ret = = 0 )
return false ;
else if ( ret > 0 )
{
TranslateMessage ( & msg ) ;
// Handle RawInput messages here
if ( m_useRawInput & & msg . message = = WM_INPUT )
ProcessRawInput ( ( HRAWINPUT ) msg . lParam ) ;
// Propagate all messages to default (SDL) handlers
DispatchMessage ( & msg ) ;
}
}
2020-04-19 08:34:58 +00:00
// SDL2: I'm not sure how the SDL1.x code was detecting quit events but in
// SDL2, it seems that we want to explicitly run SDL_PollEvent() after we
// have peeked at the message queue ourselves (above).
// Wait or poll for event from SDL
SDL_Event e ;
while ( SDL_PollEvent ( & e ) )
{
if ( e . type = = SDL_QUIT )
return false ;
}
2011-04-24 01:14:00 +00:00
// Poll keyboards, mice and joysticks
PollKeyboardsAndMice ( ) ;
PollJoysticks ( ) ;
return true ;
}
void CDirectInputSystem : : SetMouseVisibility ( bool visible )
{
if ( m_useRawInput )
2011-09-12 20:05:58 +00:00
ShowCursor ( ! m_grabMouse & & visible ? TRUE : FALSE ) ;
2011-04-24 01:14:00 +00:00
else
2011-09-12 20:05:58 +00:00
ShowCursor ( visible ? TRUE : FALSE ) ;
2011-04-24 01:14:00 +00:00
}
2011-06-05 20:53:39 +00:00
void CDirectInputSystem : : GrabMouse ( )
{
CInputSystem : : GrabMouse ( ) ;
2011-09-18 21:44:40 +00:00
if ( m_useRawInput )
SetMouseVisibility ( false ) ;
2011-06-05 20:53:39 +00:00
// When grabbing mouse, make sure devices get re-activated
2011-09-18 21:44:40 +00:00
if ( m_activated )
{
ActivateKeyboardsAndMice ( ) ;
ActivateJoysticks ( ) ;
}
2011-06-05 20:53:39 +00:00
}
void CDirectInputSystem : : UngrabMouse ( )
2011-04-24 01:14:00 +00:00
{
2011-06-05 20:53:39 +00:00
CInputSystem : : UngrabMouse ( ) ;
2011-04-24 01:14:00 +00:00
2011-09-18 21:44:40 +00:00
// When ungrabbing mouse, make sure devices get re-activated
if ( m_activated )
{
ActivateKeyboardsAndMice ( ) ;
ActivateJoysticks ( ) ;
}
2011-04-24 01:14:00 +00:00
}