2501 lines
72 KiB
C++
2501 lines
72 KiB
C++
//===== Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "inputsystem.h"
|
|
#include "key_translation.h"
|
|
#include "inputsystem/ButtonCode.h"
|
|
#include "inputsystem/AnalogCode.h"
|
|
#include "tier0/etwprof.h"
|
|
#include "tier1/convar.h"
|
|
#include "filesystem.h"
|
|
#include "platforminputdevice.h"
|
|
|
|
#ifdef _PS3
|
|
#include <vjobs_interface.h>
|
|
#endif
|
|
|
|
#ifdef PLATFORM_OSX
|
|
#include <Carbon/Carbon.h>
|
|
#include "materialsystem/imaterialsystem.h"
|
|
#endif
|
|
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
#include "scaleformui/scaleformui.h"
|
|
#endif
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
IScaleformUI* g_pScaleformUI = NULL;
|
|
#endif
|
|
|
|
#if defined( USE_SDL )
|
|
#include "SDL.h"
|
|
static void initKeymap(void);
|
|
#endif
|
|
|
|
ConVar joy_xcontroller_found( "joy_xcontroller_found", "1", FCVAR_NONE, "Automatically set to 1 if an xcontroller has been detected." );
|
|
ConVar joy_deadzone_mode( "joy_deadzone_mode", "0", FCVAR_NONE, "0 => Cross-shaped deadzone (default), 1 => Square deadzone." );
|
|
ConVar pc_fake_controller( "pc_fake_controller", "0", FCVAR_DEVELOPMENTONLY, "" );
|
|
ConVar dev_force_selected_device( "dev_force_selected_device", "0", FCVAR_DEVELOPMENTONLY, "" );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton instance
|
|
//-----------------------------------------------------------------------------
|
|
static CInputSystem g_InputSystem;
|
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CInputSystem, IInputSystem,
|
|
INPUTSYSTEM_INTERFACE_VERSION, g_InputSystem );
|
|
|
|
#ifdef _PS3
|
|
IVJobs * g_pVJobs = NULL;
|
|
#endif
|
|
|
|
|
|
|
|
#if defined( WIN32 ) && !defined( _X360 )
|
|
typedef BOOL (WINAPI *RegisterRawInputDevices_t)
|
|
(
|
|
PCRAWINPUTDEVICE pRawInputDevices,
|
|
UINT uiNumDevices,
|
|
UINT cbSize
|
|
);
|
|
|
|
typedef UINT (WINAPI *GetRawInputData_t)
|
|
(
|
|
HRAWINPUT hRawInput,
|
|
UINT uiCommand,
|
|
LPVOID pData,
|
|
PUINT pcbSize,
|
|
UINT cbSizeHeader
|
|
);
|
|
|
|
RegisterRawInputDevices_t pfnRegisterRawInputDevices;
|
|
GetRawInputData_t pfnGetRawInputData;
|
|
#endif
|
|
|
|
|
|
|
|
extern int countBits( uint32 iValue );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor, destructor
|
|
//-----------------------------------------------------------------------------
|
|
CInputSystem::CInputSystem()
|
|
{
|
|
m_nLastPollTick = m_nLastSampleTick = m_StartupTimeTick = 0;
|
|
m_ChainedWndProc = 0;
|
|
m_hAttachedHWnd = 0;
|
|
m_hEvent = NULL;
|
|
m_bEnabled = true;
|
|
m_bPumpEnabled = true;
|
|
m_bIsPolling = false;
|
|
m_bIsInGame = false;
|
|
m_JoysticksEnabled.ClearAllFlags();
|
|
m_nJoystickCount = 0;
|
|
m_nJoystickBaseline = 0;
|
|
m_nPollCount = 0;
|
|
m_uiMouseWheel = 0;
|
|
m_bXController = false;
|
|
m_bRawInputSupported = false;
|
|
m_bIMEComposing = false;
|
|
m_nUIEventClientCount = 0;
|
|
m_hLastIMEHWnd = NULL;
|
|
m_hCurrentCaptureWnd = PLAT_WINDOW_INVALID;
|
|
m_bCursorVisible = true;
|
|
m_hCursor = INPUT_CURSOR_HANDLE_INVALID;
|
|
m_bMotionControllerActive = false;
|
|
m_qMotionControllerOrientation.Init();
|
|
m_fMotionControllerPosX = 0.0f;
|
|
m_fMotionControllerPosY = 0.0f;
|
|
m_nMotionControllerStatus = INPUT_DEVICE_MC_STATE_CAMERA_NOT_CONNECTED;
|
|
m_nMotionControllerStatusFlags = 0;
|
|
|
|
// This is B.S., must be a compile-time assert with valid expression:
|
|
// Assert( (MAX_JOYSTICKS + 7) >> 3 << sizeof(unsigned short) );
|
|
|
|
#if !defined( _CERT ) && !defined(LINUX)
|
|
V_memset( m_press_x360_buttons, 0, sizeof( m_press_x360_buttons ) );
|
|
#endif
|
|
|
|
#ifdef _PS3
|
|
m_pPS3CellNoPadDataHook = NULL;
|
|
m_pPS3CellPadDataHook = NULL;
|
|
m_PS3KeyboardConnected = false;
|
|
m_PS3MouseConnected = false;
|
|
#endif
|
|
|
|
m_pXInputDLL = NULL;
|
|
m_pRawInputDLL = NULL;
|
|
|
|
for ( int i = 0; i < Q_ARRAYSIZE(m_nControllerType); i++)
|
|
{
|
|
m_nControllerType[i] = INPUT_TYPE_GENERIC_JOYSTICK;
|
|
}
|
|
|
|
InitPlatfromInputDeviceInfo();
|
|
}
|
|
|
|
#if defined( USE_SDL )
|
|
|
|
void CInputSystem::DisableHardwareCursor( )
|
|
{
|
|
m_pLauncherMgr->SetMouseVisible(false);
|
|
}
|
|
|
|
void CInputSystem::EnableHardwareCursor( )
|
|
{
|
|
m_pLauncherMgr->SetMouseVisible(true);
|
|
|
|
}
|
|
#endif
|
|
|
|
CInputSystem::~CInputSystem()
|
|
{
|
|
if ( m_pXInputDLL )
|
|
{
|
|
Sys_UnloadModule( m_pXInputDLL );
|
|
m_pXInputDLL = NULL;
|
|
}
|
|
|
|
if ( m_pRawInputDLL )
|
|
{
|
|
Sys_UnloadModule( m_pRawInputDLL );
|
|
m_pRawInputDLL = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initialization
|
|
//-----------------------------------------------------------------------------
|
|
InitReturnVal_t CInputSystem::Init()
|
|
{
|
|
InitReturnVal_t nRetVal = BaseClass::Init();
|
|
if ( nRetVal != INIT_OK )
|
|
return nRetVal;
|
|
|
|
m_StartupTimeTick = Plat_MSTime();
|
|
|
|
#if !defined( PLATFORM_POSIX )
|
|
if ( IsPC() )
|
|
{
|
|
m_uiMouseWheel = RegisterWindowMessage( "MSWHEEL_ROLLMSG" );
|
|
}
|
|
|
|
m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
|
|
if ( !m_hEvent )
|
|
return INIT_FAILED;
|
|
#endif
|
|
|
|
|
|
ButtonCode_InitKeyTranslationTable();
|
|
ButtonCode_UpdateScanCodeLayout();
|
|
|
|
joy_xcontroller_found.SetValue( 0 );
|
|
|
|
#if !defined( _GAMECONSOLE )
|
|
if ( IsPC() )
|
|
{
|
|
#if !defined( PLATFORM_POSIX )
|
|
m_pXInputDLL = Sys_LoadModule( "XInput1_3.dll" );
|
|
if ( m_pXInputDLL )
|
|
{
|
|
InitializeXDevices();
|
|
}
|
|
#endif
|
|
if ( !m_nJoystickCount )
|
|
{
|
|
// Didn't find any XControllers. See if we can find other joysticks.
|
|
InitializeJoysticks();
|
|
}
|
|
else
|
|
{
|
|
m_bXController = true;
|
|
}
|
|
|
|
if ( m_bXController )
|
|
joy_xcontroller_found.SetValue( 1 );
|
|
|
|
|
|
}
|
|
#elif defined( _GAMECONSOLE )
|
|
if ( IsGameConsole() )
|
|
{
|
|
InitializeXDevices();
|
|
m_bXController = true;
|
|
joy_xcontroller_found.SetValue( 1 );
|
|
}
|
|
#endif
|
|
|
|
InitCursors();
|
|
|
|
m_bRawInputSupported = false;
|
|
|
|
#if defined( LINUX )
|
|
|
|
m_bRawInputSupported = true;
|
|
|
|
#elif defined( WIN32 ) && !defined( _X360 )
|
|
// Check if this version of windows supports raw mouse input (later than win2k)
|
|
|
|
CSysModule *m_pRawInputDLL = Sys_LoadModule( "USER32.dll" );
|
|
if ( m_pRawInputDLL )
|
|
{
|
|
pfnRegisterRawInputDevices = (RegisterRawInputDevices_t)GetProcAddress( (HMODULE)m_pRawInputDLL, "RegisterRawInputDevices" );
|
|
pfnGetRawInputData = (GetRawInputData_t)GetProcAddress( (HMODULE)m_pRawInputDLL, "GetRawInputData" );
|
|
if ( pfnRegisterRawInputDevices && pfnGetRawInputData )
|
|
m_bRawInputSupported = true;
|
|
}
|
|
#endif
|
|
|
|
#if defined( USE_SDL )
|
|
initKeymap();
|
|
#endif
|
|
|
|
m_unNumSteamControllerConnected = 0;
|
|
m_bSteamController = InitializeSteamControllers();
|
|
|
|
return INIT_OK;
|
|
}
|
|
|
|
bool CInputSystem::Connect( CreateInterfaceFn factory )
|
|
{
|
|
if ( !BaseClass::Connect( factory ) )
|
|
return false;
|
|
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
g_pScaleformUI = (IScaleformUI*)factory( SCALEFORMUI_INTERFACE_VERSION, NULL );
|
|
#endif
|
|
#ifdef _PS3
|
|
g_pVJobs = ( IVJobs* )factory( VJOBS_INTERFACE_VERSION, NULL );
|
|
#endif
|
|
|
|
#if defined( USE_SDL )
|
|
m_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL );
|
|
#elif defined( OSX )
|
|
m_pLauncherMgr = (ILauncherMgr *)factory( COCOAMGR_INTERFACE_VERSION, NULL );
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef _PS3
|
|
extern void PS3_XInputShutdown();
|
|
#endif
|
|
|
|
#ifdef _PS3
|
|
void CInputSystem::SetPS3CellPadDataHook( BCellPadDataHook_t hookFunc )
|
|
{
|
|
m_pPS3CellPadDataHook = hookFunc;
|
|
}
|
|
void CInputSystem::SetPS3CellPadNoDataHook( BCellPadNoDataHook_t hookFunc )
|
|
{
|
|
m_pPS3CellNoPadDataHook = hookFunc;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Shutdown
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::Shutdown()
|
|
{
|
|
#if !defined( PLATFORM_POSIX )
|
|
if ( m_hEvent != NULL )
|
|
{
|
|
CloseHandle( m_hEvent );
|
|
m_hEvent = NULL;
|
|
}
|
|
#endif
|
|
|
|
ShutdownCursors();
|
|
|
|
BaseClass::Shutdown();
|
|
|
|
#ifdef _PS3
|
|
PS3_XInputShutdown();
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Sleep until input
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::SleepUntilInput( int nMaxSleepTimeMS )
|
|
{
|
|
#if defined( USE_SDL ) || defined( OSX )
|
|
m_pLauncherMgr->WaitUntilUserInput( nMaxSleepTimeMS );
|
|
#elif defined( _WIN32 )
|
|
if ( nMaxSleepTimeMS < 0 )
|
|
{
|
|
nMaxSleepTimeMS = INFINITE;
|
|
}
|
|
|
|
MsgWaitForMultipleObjects( 1, &m_hEvent, FALSE, nMaxSleepTimeMS, QS_ALLEVENTS );
|
|
#elif defined( _PS3 )
|
|
// no-op
|
|
#else
|
|
#warning "need a SleepUntilInput impl"
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tells the input system to generate UI-related events, defined
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::AddUIEventListener()
|
|
{
|
|
++m_nUIEventClientCount;
|
|
}
|
|
|
|
void CInputSystem::RemoveUIEventListener()
|
|
{
|
|
--m_nUIEventClientCount;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the currently attached window
|
|
//-----------------------------------------------------------------------------
|
|
PlatWindow_t CInputSystem::GetAttachedWindow() const
|
|
{
|
|
return (PlatWindow_t)m_hAttachedHWnd;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Callback to call into our class
|
|
//-----------------------------------------------------------------------------
|
|
#if !defined( PLATFORM_POSIX )
|
|
static LRESULT CALLBACK InputSystemWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
return g_InputSystem.WindowProc( hwnd, uMsg, wParam, lParam );
|
|
}
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Hooks input listening up to a window
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::AttachToWindow( void* hWnd )
|
|
{
|
|
Assert( m_hAttachedHWnd == 0 );
|
|
if ( m_hAttachedHWnd )
|
|
{
|
|
Warning( "CInputSystem::AttachToWindow: Cannot attach to two windows at once!\n" );
|
|
return;
|
|
}
|
|
|
|
#if defined ( USE_SDL )
|
|
#elif defined( PLATFORM_OSX )
|
|
#elif defined( PLATFORM_WINDOWS )
|
|
#if defined( PLATFORM_X360 ) //GetWindowLongPtrW/SetWindowLongPtrW don't exist on the 360
|
|
m_ChainedWndProc = (WNDPROC)GetWindowLongPtr( (HWND)hWnd, GWLP_WNDPROC );
|
|
SetWindowLongPtr( (HWND)hWnd, GWLP_WNDPROC, (LONG_PTR)InputSystemWindowProc );
|
|
#else
|
|
|
|
m_ChainedWndProc = (WNDPROC)GetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC );
|
|
SetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC, (LONG_PTR)InputSystemWindowProc );
|
|
|
|
// register to read raw mouse input
|
|
#if !defined(HID_USAGE_PAGE_GENERIC)
|
|
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
|
|
#endif
|
|
#if !defined(HID_USAGE_GENERIC_MOUSE)
|
|
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
|
|
#endif
|
|
|
|
RAWINPUTDEVICE Rid[1];
|
|
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
|
|
Rid[0].dwFlags = RIDEV_INPUTSINK;
|
|
Rid[0].hwndTarget = (HWND)hWnd; // g_InputSystem.m_hAttachedHWnd; // GetHhWnd;
|
|
::RegisterRawInputDevices(Rid, ARRAYSIZE(Rid), sizeof(Rid[0]));
|
|
|
|
#endif
|
|
#elif defined( _PS3 )
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
m_hAttachedHWnd = (HWND)hWnd;
|
|
|
|
#if defined( WIN32 ) && !defined( _X360 )
|
|
// register to read raw mouse input
|
|
|
|
#if !defined(HID_USAGE_PAGE_GENERIC)
|
|
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
|
|
#endif
|
|
#if !defined(HID_USAGE_GENERIC_MOUSE)
|
|
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
|
|
#endif
|
|
|
|
if ( m_bRawInputSupported )
|
|
{
|
|
RAWINPUTDEVICE Rid[1];
|
|
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
|
|
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
|
|
Rid[0].dwFlags = RIDEV_INPUTSINK;
|
|
Rid[0].hwndTarget = g_InputSystem.m_hAttachedHWnd; // GetHhWnd;
|
|
pfnRegisterRawInputDevices(Rid, ARRAYSIZE(Rid), sizeof(Rid[0]));
|
|
}
|
|
#endif
|
|
|
|
// New window, clear input state
|
|
ClearInputState( true );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Unhooks input listening from a window
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::DetachFromWindow( )
|
|
{
|
|
if ( !m_hAttachedHWnd )
|
|
return;
|
|
|
|
ResetInputState();
|
|
|
|
#if !defined( PLATFORM_POSIX )
|
|
if ( m_ChainedWndProc )
|
|
{
|
|
SetWindowLongPtrW( m_hAttachedHWnd, GWLP_WNDPROC, (LONG_PTR)m_ChainedWndProc );
|
|
m_ChainedWndProc = 0;
|
|
}
|
|
#endif
|
|
|
|
m_hAttachedHWnd = 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Enables/disables input
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::EnableInput( bool bEnable )
|
|
{
|
|
m_bEnabled = bEnable;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Enables/disables the inputsystem windows message pump
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::EnableMessagePump( bool bEnable )
|
|
{
|
|
m_bPumpEnabled = bEnable;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Clears the input state, doesn't generate key-up messages
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::ClearInputState( bool bPurgeState )
|
|
{
|
|
for ( int i = 0; i < INPUT_STATE_COUNT; ++i )
|
|
{
|
|
InputState_t& state = m_InputState[i];
|
|
state.m_ButtonState.ClearAll();
|
|
memset( state.m_pAnalogDelta, 0, ANALOG_CODE_LAST * sizeof(int) );
|
|
memset( state.m_pAnalogValue, 0, ANALOG_CODE_LAST * sizeof(int) );
|
|
memset( state.m_ButtonPressedTick, 0, BUTTON_CODE_LAST * sizeof(int) );
|
|
memset( state.m_ButtonReleasedTick, 0, BUTTON_CODE_LAST * sizeof(int) );
|
|
if ( bPurgeState )
|
|
{
|
|
state.m_Events.Purge();
|
|
state.m_bDirty = false;
|
|
}
|
|
}
|
|
memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) );
|
|
m_mouseRawAccumX = m_mouseRawAccumY = 0;
|
|
m_flLastControllerPollTime = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Resets the input state
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::ResetInputState()
|
|
{
|
|
ReleaseAllButtons();
|
|
ZeroAnalogState( 0, ANALOG_CODE_LAST - 1 );
|
|
ClearInputState( false );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Convert back + forth between ButtonCode/AnalogCode + strings
|
|
//-----------------------------------------------------------------------------
|
|
const char *CInputSystem::ButtonCodeToString( ButtonCode_t code ) const
|
|
{
|
|
return ButtonCode_ButtonCodeToString( code, m_bXController );
|
|
}
|
|
|
|
const char *CInputSystem::AnalogCodeToString( AnalogCode_t code ) const
|
|
{
|
|
return AnalogCode_AnalogCodeToString( code );
|
|
}
|
|
|
|
ButtonCode_t CInputSystem::StringToButtonCode( const char *pString ) const
|
|
{
|
|
return ButtonCode_StringToButtonCode( pString, true );
|
|
}
|
|
|
|
AnalogCode_t CInputSystem::StringToAnalogCode( const char *pString ) const
|
|
{
|
|
return AnalogCode_StringToAnalogCode( pString );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Convert back + forth between virtual codes + button codes
|
|
// FIXME: This is a temporary piece of code
|
|
//-----------------------------------------------------------------------------
|
|
ButtonCode_t CInputSystem::VirtualKeyToButtonCode( int nVirtualKey ) const
|
|
{
|
|
return ButtonCode_VirtualKeyToButtonCode( nVirtualKey );
|
|
}
|
|
|
|
int CInputSystem::ButtonCodeToVirtualKey( ButtonCode_t code ) const
|
|
{
|
|
return ButtonCode_ButtonCodeToVirtualKey( code );
|
|
}
|
|
|
|
ButtonCode_t CInputSystem::XKeyToButtonCode( int nPort, int nXKey ) const
|
|
{
|
|
if ( m_bXController )
|
|
return ButtonCode_XKeyToButtonCode( nPort, nXKey );
|
|
return KEY_NONE;
|
|
}
|
|
|
|
ButtonCode_t CInputSystem::ScanCodeToButtonCode( int lParam ) const
|
|
{
|
|
return ButtonCode_ScanCodeToButtonCode( lParam );
|
|
}
|
|
|
|
ButtonCode_t CInputSystem::SKeyToButtonCode( int nPort, int nXKey ) const
|
|
{
|
|
return ButtonCode_SKeyToButtonCode( nPort, nXKey );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Post an event to the queue
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::PostEvent( int nType, int nTick, int nData, int nData2, int nData3 )
|
|
{
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
int i = state.m_Events.AddToTail();
|
|
InputEvent_t &event = state.m_Events[i];
|
|
event.m_nType = nType;
|
|
event.m_nTick = nTick;
|
|
event.m_nData = nData;
|
|
event.m_nData2 = nData2;
|
|
event.m_nData3 = nData3;
|
|
state.m_bDirty = true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Post an button press event to the queue
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::PostButtonPressedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode )
|
|
{
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
if ( !state.m_ButtonState.IsBitSet( scanCode ) )
|
|
{
|
|
// Update button state
|
|
state.m_ButtonState.Set( scanCode );
|
|
state.m_ButtonPressedTick[ scanCode ] = nTick;
|
|
|
|
// Add this event to the app-visible event queue
|
|
PostEvent( nType, nTick, scanCode, virtualCode );
|
|
|
|
if ( IsGameConsole() && ShouldGenerateUIEvents() && IsJoystickCode( scanCode ) )
|
|
{
|
|
// xboxissue - as yet input hasn't been made aware of analog inputs or ports
|
|
// so just digital produce a key typed message
|
|
PostEvent( IE_KeyCodeTyped, nTick, scanCode );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Post an button release event to the queue
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::PostButtonReleasedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode )
|
|
{
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
if ( state.m_ButtonState.IsBitSet( scanCode ) )
|
|
{
|
|
// Update button state
|
|
state.m_ButtonState.Clear( scanCode );
|
|
state.m_ButtonReleasedTick[ scanCode ] = nTick;
|
|
|
|
// Add this event to the app-visible event queue
|
|
PostEvent( nType, nTick, scanCode, virtualCode );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Pass Joystick button events through the engine's window procs
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::ProcessEvent( UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
#if !defined( PLATFORM_POSIX )
|
|
// To prevent subtle input timing bugs, all button events must be fed
|
|
// through the window proc once per frame, same as the keyboard and mouse.
|
|
HWND hWnd = GetFocus();
|
|
WNDPROC windowProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_WNDPROC );
|
|
if ( windowProc )
|
|
{
|
|
windowProc( hWnd, uMsg, wParam, lParam );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Copies the input state record over
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::CopyInputState( InputState_t *pDest, const InputState_t &src, bool bCopyEvents )
|
|
{
|
|
pDest->m_Events.RemoveAll();
|
|
pDest->m_bDirty = false;
|
|
if ( src.m_bDirty )
|
|
{
|
|
pDest->m_ButtonState = src.m_ButtonState;
|
|
memcpy( &pDest->m_ButtonPressedTick, &src.m_ButtonPressedTick, sizeof( pDest->m_ButtonPressedTick ) );
|
|
memcpy( &pDest->m_ButtonReleasedTick, &src.m_ButtonReleasedTick, sizeof( pDest->m_ButtonReleasedTick ) );
|
|
memcpy( &pDest->m_pAnalogDelta, &src.m_pAnalogDelta, sizeof( pDest->m_pAnalogDelta ) );
|
|
memcpy( &pDest->m_pAnalogValue, &src.m_pAnalogValue, sizeof( pDest->m_pAnalogValue ) );
|
|
if ( bCopyEvents )
|
|
{
|
|
if ( src.m_Events.Count() > 0 )
|
|
{
|
|
pDest->m_Events.EnsureCount( src.m_Events.Count() );
|
|
memcpy( pDest->m_Events.Base(), src.m_Events.Base(), src.m_Events.Count() * sizeof(InputEvent_t) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if defined( WIN32 ) && !defined( USE_SDL )
|
|
void CInputSystem::PollInputState_Windows()
|
|
{
|
|
if ( IsPC() && m_bPumpEnabled )
|
|
{
|
|
// Poll mouse + keyboard
|
|
MSG msg;
|
|
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
|
|
{
|
|
if ( msg.message == WM_QUIT )
|
|
{
|
|
PostEvent( IE_Quit, m_nLastSampleTick );
|
|
break;
|
|
}
|
|
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI )
|
|
{
|
|
// Scaleform IME requirement. Pass these messages to GFxIME BEFORE any TranlsateMessage/DispatchMessage.
|
|
if ( (msg.message == WM_KEYDOWN) || (msg.message == WM_KEYUP) || ImmIsUIMessage( NULL, msg.message, msg.wParam, msg.lParam )
|
|
|| (msg.message == WM_LBUTTONDOWN) || (msg.message == WM_LBUTTONUP) )
|
|
{
|
|
g_pScaleformUI->PreProcessKeyboardEvent( (size_t)msg.hwnd, msg.message, msg.wParam, msg.lParam );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
|
|
// NOTE: Under some implementations of Win9x,
|
|
// dispatching messages can cause the FPU control word to change
|
|
SetupFPUControlWord();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(OSX) || defined( USE_SDL )
|
|
|
|
#if defined( USE_SDL )
|
|
static BYTE scantokey[SDL_NUM_SCANCODES];
|
|
|
|
static void initKeymap(void)
|
|
{
|
|
memset(scantokey, '\0', sizeof (scantokey));
|
|
|
|
for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_Z; i++)
|
|
scantokey[i] = KEY_A + (i - SDL_SCANCODE_A);
|
|
for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_9; i++)
|
|
scantokey[i] = KEY_1 + (i - SDL_SCANCODE_1);
|
|
for (int i = SDL_SCANCODE_F1; i <= SDL_SCANCODE_F12; i++)
|
|
scantokey[i] = KEY_F1 + (i - SDL_SCANCODE_F1);
|
|
for (int i = SDL_SCANCODE_KP_1; i <= SDL_SCANCODE_KP_9; i++)
|
|
scantokey[i] = KEY_PAD_1 + (i - SDL_SCANCODE_KP_1);
|
|
|
|
scantokey[SDL_SCANCODE_0] = KEY_0;
|
|
scantokey[SDL_SCANCODE_KP_0] = KEY_PAD_0;
|
|
scantokey[SDL_SCANCODE_RETURN] = KEY_ENTER;
|
|
scantokey[SDL_SCANCODE_ESCAPE] = KEY_ESCAPE;
|
|
scantokey[SDL_SCANCODE_BACKSPACE] = KEY_BACKSPACE;
|
|
scantokey[SDL_SCANCODE_TAB] = KEY_TAB;
|
|
scantokey[SDL_SCANCODE_SPACE] = KEY_SPACE;
|
|
scantokey[SDL_SCANCODE_MINUS] = KEY_MINUS;
|
|
scantokey[SDL_SCANCODE_EQUALS] = KEY_EQUAL;
|
|
scantokey[SDL_SCANCODE_LEFTBRACKET] = KEY_LBRACKET;
|
|
scantokey[SDL_SCANCODE_RIGHTBRACKET] = KEY_RBRACKET;
|
|
scantokey[SDL_SCANCODE_BACKSLASH] = KEY_BACKSLASH;
|
|
scantokey[SDL_SCANCODE_SEMICOLON] = KEY_SEMICOLON;
|
|
scantokey[SDL_SCANCODE_APOSTROPHE] = KEY_APOSTROPHE;
|
|
scantokey[SDL_SCANCODE_GRAVE] = KEY_BACKQUOTE;
|
|
scantokey[SDL_SCANCODE_COMMA] = KEY_COMMA;
|
|
scantokey[SDL_SCANCODE_PERIOD] = KEY_PERIOD;
|
|
scantokey[SDL_SCANCODE_SLASH] = KEY_SLASH;
|
|
scantokey[SDL_SCANCODE_CAPSLOCK] = KEY_CAPSLOCK;
|
|
scantokey[SDL_SCANCODE_SCROLLLOCK] = KEY_SCROLLLOCK;
|
|
scantokey[SDL_SCANCODE_INSERT] = KEY_INSERT;
|
|
scantokey[SDL_SCANCODE_HOME] = KEY_HOME;
|
|
scantokey[SDL_SCANCODE_PAGEUP] = KEY_PAGEUP;
|
|
scantokey[SDL_SCANCODE_DELETE] = KEY_DELETE;
|
|
scantokey[SDL_SCANCODE_END] = KEY_END;
|
|
scantokey[SDL_SCANCODE_PAGEDOWN] = KEY_PAGEDOWN;
|
|
scantokey[SDL_SCANCODE_RIGHT] = KEY_RIGHT;
|
|
scantokey[SDL_SCANCODE_LEFT] = KEY_LEFT;
|
|
scantokey[SDL_SCANCODE_DOWN] = KEY_DOWN;
|
|
scantokey[SDL_SCANCODE_UP] = KEY_UP;
|
|
scantokey[SDL_SCANCODE_NUMLOCKCLEAR] = KEY_NUMLOCK;
|
|
scantokey[SDL_SCANCODE_KP_DIVIDE] = KEY_PAD_DIVIDE;
|
|
scantokey[SDL_SCANCODE_KP_MULTIPLY] = KEY_PAD_MULTIPLY;
|
|
scantokey[SDL_SCANCODE_KP_MINUS] = KEY_PAD_MINUS;
|
|
scantokey[SDL_SCANCODE_KP_PLUS] = KEY_PAD_PLUS;
|
|
// Map keybad enter to enter for vgui. This means vgui dialog won't ever see KEY_PAD_ENTER
|
|
scantokey[SDL_SCANCODE_KP_ENTER] = KEY_ENTER;
|
|
scantokey[SDL_SCANCODE_KP_PERIOD] = KEY_PAD_DECIMAL;
|
|
scantokey[SDL_SCANCODE_APPLICATION] = KEY_APP;
|
|
scantokey[SDL_SCANCODE_LCTRL] = KEY_LCONTROL;
|
|
scantokey[SDL_SCANCODE_LSHIFT] = KEY_LSHIFT;
|
|
scantokey[SDL_SCANCODE_LALT] = KEY_LALT;
|
|
scantokey[SDL_SCANCODE_LGUI] = KEY_LWIN;
|
|
scantokey[SDL_SCANCODE_RCTRL] = KEY_RCONTROL;
|
|
scantokey[SDL_SCANCODE_RSHIFT] = KEY_RSHIFT;
|
|
scantokey[SDL_SCANCODE_RALT] = KEY_RALT;
|
|
scantokey[SDL_SCANCODE_RGUI] = KEY_RWIN;
|
|
}
|
|
|
|
#elif defined(OSX)
|
|
static BYTE scantokey[128] =
|
|
{
|
|
KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
|
|
KEY_C, KEY_V, KEY_BACKQUOTE /*german backquote char*/ , KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R, //15
|
|
KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5, // 23
|
|
KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RBRACKET, KEY_O, //31
|
|
KEY_U, KEY_LBRACKET, KEY_I, KEY_P, KEY_ENTER , KEY_L, KEY_J, KEY_APOSTROPHE, //39
|
|
KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA,KEY_SLASH, KEY_N, KEY_M, KEY_PERIOD, // 47
|
|
KEY_TAB, KEY_SPACE, KEY_BACKQUOTE, KEY_BACKSPACE, 0, KEY_ESCAPE, KEY_RWIN, KEY_LWIN, //55
|
|
KEY_LSHIFT, KEY_CAPSLOCK, KEY_LALT, KEY_LCONTROL, KEY_LSHIFT, 0, KEY_RCONTROL, 0, //63
|
|
0, KEY_PAD_DECIMAL, 0 , KEY_PAD_MULTIPLY, 0 , KEY_PAD_PLUS, 0 , KEY_NUMLOCK , // 71
|
|
0, 0 , 0 , KEY_PAD_DIVIDE, KEY_PAD_ENTER, 0 , KEY_PAD_MINUS, 0 , // 79
|
|
0, KEY_PAD_DIVIDE, KEY_PAD_0, KEY_PAD_1, KEY_PAD_2, KEY_PAD_3, KEY_PAD_4, KEY_PAD_5, // 87
|
|
KEY_PAD_6, KEY_PAD_7, 0, KEY_PAD_8, KEY_PAD_9, 0, 0 , 0 , // 95
|
|
KEY_F5, KEY_F6, KEY_F7, KEY_F3, KEY_F8, KEY_F9, 0, KEY_F11, // 103
|
|
0, 0 , 0 , 0 , 0, KEY_F10, KEY_APP , KEY_F12, // 111
|
|
0 , 0, KEY_INSERT, KEY_HOME, KEY_PAGEUP, KEY_DELETE, KEY_F4, KEY_END, // 119
|
|
KEY_F2, KEY_PAGEDOWN, KEY_F1, KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0, // 127
|
|
};
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
|
|
bool MapCocoaVirtualKeyToButtonCode( int nCocoaVirtualKeyCode, ButtonCode_t *pOut )
|
|
{
|
|
if ( nCocoaVirtualKeyCode < 0 )
|
|
*pOut = (ButtonCode_t)(-1 * nCocoaVirtualKeyCode);
|
|
else
|
|
{
|
|
#ifdef OSX
|
|
int modified = nCocoaVirtualKeyCode & 255;
|
|
|
|
if ( modified > 127)
|
|
{
|
|
return false;
|
|
}
|
|
#else
|
|
nCocoaVirtualKeyCode &= 0x000000ff;
|
|
#endif
|
|
|
|
*pOut = (ButtonCode_t)scantokey[nCocoaVirtualKeyCode];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
#ifdef LINUX
|
|
void CInputSystem::PollInputState_Linux()
|
|
#elif defined( OSX )
|
|
void CInputSystem::PollInputState_OSX()
|
|
#elif defined( _WIN32 )
|
|
void CInputSystem::PollInputState_Windows()
|
|
#endif
|
|
{
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
|
|
if ( m_bPumpEnabled )
|
|
m_pLauncherMgr->PumpWindowsMessageLoop();
|
|
// These are Carbon virtual key codes. AFAIK they don't have a header that defines these, but they are supposed to map
|
|
// to the same letters across international keyboards, so our mapping here should work.
|
|
CCocoaEvent events[32];
|
|
while ( 1 )
|
|
{
|
|
int nEvents = m_pLauncherMgr->GetEvents( events, ARRAYSIZE( events ) );
|
|
if ( nEvents == 0 )
|
|
break;
|
|
|
|
for ( int iEvent=0; iEvent < nEvents; iEvent++ )
|
|
{
|
|
CCocoaEvent *pEvent = &events[iEvent];
|
|
|
|
switch( pEvent->m_EventType )
|
|
{
|
|
case CocoaEvent_Deleted:
|
|
break;
|
|
|
|
case CocoaEvent_KeyDown:
|
|
{
|
|
ButtonCode_t virtualCode;
|
|
if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) )
|
|
{
|
|
ButtonCode_t scanCode = virtualCode;
|
|
|
|
#ifdef LINUX
|
|
if( scanCode != BUTTON_CODE_NONE )
|
|
#endif
|
|
{
|
|
// For SDL, hitting spacebar causes a SDL_KEYDOWN event, then SDL_TEXTINPUT with
|
|
// event.text.text[0] = ' ', and then we get here and wind up sending two events
|
|
// to PostButtonPressedEvent. The first is virtualCode = ' ', the 2nd has virtualCode = 0.
|
|
// This will confuse Button::OnKeyCodePressed(), which is checking for space keydown
|
|
// followed by space keyup. So we ignore all BUTTON_CODE_NONE events here.
|
|
PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, scanCode, virtualCode );
|
|
}
|
|
|
|
InputEvent_t event;
|
|
memset( &event, 0, sizeof(event) );
|
|
event.m_nTick = GetPollTick();
|
|
event.m_nType = IE_KeyCodeTyped;
|
|
event.m_nData = scanCode;
|
|
g_pInputSystem->PostUserEvent( event );
|
|
|
|
#if defined( LINUX ) || (defined( OSX ) && defined( USE_SDL ) )
|
|
if ( scanCode == KEY_BACKSPACE )
|
|
{
|
|
// On Linux (and OS X, when using SDL), we need to fire this event to have backspace keypresses picked up by scaleform.
|
|
PostEvent( IE_KeyTyped, GetPollTick(), (wchar_t)8 );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ( !(pEvent->m_ModifierKeyMask & (1<<eCommandKey) ) && pEvent->m_VirtualKeyCode >= 0 && pEvent->m_UnicodeKey > 0 )
|
|
{
|
|
InputEvent_t event;
|
|
memset( &event, 0, sizeof(event) );
|
|
event.m_nTick = GetPollTick();
|
|
event.m_nType = IE_KeyTyped;
|
|
event.m_nData = (int)pEvent->m_UnicodeKey;
|
|
g_pInputSystem->PostUserEvent( event );
|
|
}
|
|
|
|
#if defined ( CSTRIKE15 )
|
|
// [will] - HACK: Allow cmd+a, cmd+c, cmd+v, cmd+x to go through, and treat them as the ctrl modified versions.
|
|
// This allows these to work in the Scaleform chat window.
|
|
if ( pEvent->m_ModifierKeyMask & (1<<eCommandKey)
|
|
&& ( pEvent->m_UnicodeKey == 'a'
|
|
|| pEvent->m_UnicodeKey == 'c'
|
|
|| pEvent->m_UnicodeKey == 'v'
|
|
|| pEvent->m_UnicodeKey == 'x' ) )
|
|
{
|
|
InputEvent_t event;
|
|
memset( &event, 0, sizeof(event) );
|
|
event.m_nTick = GetPollTick();
|
|
event.m_nType = IE_KeyTyped;
|
|
event.m_nData = (int)pEvent->m_UnicodeKey - 96; // Subtract 96 to give the ctrl version of this character.
|
|
g_pInputSystem->PostUserEvent( event );
|
|
}
|
|
#endif
|
|
|
|
}
|
|
break;
|
|
|
|
case CocoaEvent_KeyUp:
|
|
{
|
|
ButtonCode_t virtualCode;
|
|
if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) )
|
|
{
|
|
ButtonCode_t scanCode = virtualCode;
|
|
PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, scanCode, virtualCode );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CocoaEvent_MouseButtonDown:
|
|
{
|
|
int nButtonMask = pEvent->m_MouseButtonFlags;
|
|
ButtonCode_t dblClickCode = BUTTON_CODE_INVALID;
|
|
if ( pEvent->m_nMouseClickCount > 1 )
|
|
{
|
|
switch( pEvent->m_MouseButton )
|
|
{
|
|
default:
|
|
case COCOABUTTON_LEFT:
|
|
dblClickCode = MOUSE_LEFT;
|
|
break;
|
|
case COCOABUTTON_RIGHT:
|
|
dblClickCode = MOUSE_RIGHT;
|
|
break;
|
|
case COCOABUTTON_MIDDLE:
|
|
dblClickCode = MOUSE_MIDDLE;
|
|
break;
|
|
case COCOABUTTON_4:
|
|
dblClickCode = MOUSE_4;
|
|
break;
|
|
case COCOABUTTON_5:
|
|
dblClickCode = MOUSE_5;
|
|
break;
|
|
}
|
|
}
|
|
UpdateMouseButtonState( nButtonMask, dblClickCode );
|
|
}
|
|
break;
|
|
|
|
case CocoaEvent_MouseButtonUp:
|
|
{
|
|
int nButtonMask = pEvent->m_MouseButtonFlags;
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case CocoaEvent_MouseMove:
|
|
{
|
|
UpdateMousePositionState( state, (short)pEvent->m_MousePos[0], (short)pEvent->m_MousePos[1] );
|
|
|
|
InputEvent_t event;
|
|
memset( &event, 0, sizeof(event) );
|
|
event.m_nTick = GetPollTick();
|
|
event.m_nType = IE_LocateMouseClick;
|
|
event.m_nData = (short)pEvent->m_MousePos[0];
|
|
event.m_nData2 = (short)pEvent->m_MousePos[1];
|
|
g_pInputSystem->PostUserEvent( event );
|
|
}
|
|
break;
|
|
|
|
case CocoaEvent_MouseScroll:
|
|
{
|
|
ButtonCode_t code = (short)pEvent->m_MousePos[1] > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
|
|
state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
|
|
PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
|
|
PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
|
|
|
|
#ifdef LINUX
|
|
state.m_pAnalogDelta[ MOUSE_WHEEL ] = pEvent->m_MousePos[1];
|
|
#else
|
|
state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( (short)pEvent->m_MousePos[1] ) / 10;
|
|
#endif
|
|
state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
|
|
}
|
|
break;
|
|
|
|
case CocoaEvent_AppActivate:
|
|
{
|
|
InputEvent_t event;
|
|
memset( &event, 0, sizeof(event) );
|
|
event.m_nType = IE_FirstAppEvent + 1;
|
|
event.m_nData = (bool)pEvent->m_ModifierKeyMask;
|
|
|
|
g_pInputSystem->PostUserEvent( event );
|
|
}
|
|
break;
|
|
case CocoaEvent_AppQuit:
|
|
{
|
|
PostEvent( IE_Quit, m_nLastSampleTick );
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif // PLATFORM_OSX
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Polls the current input state
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::PollInputState( bool bIsInGame )
|
|
{
|
|
#if !defined( _CERT ) && !defined(LINUX)
|
|
PollPressX360Button();
|
|
#endif
|
|
|
|
m_bIsPolling = true;
|
|
++m_nPollCount;
|
|
|
|
// set whether in a game or not
|
|
m_bIsInGame = bIsInGame;
|
|
|
|
// Deals with polled input events
|
|
InputState_t &queuedState = m_InputState[ INPUT_STATE_QUEUED ];
|
|
CopyInputState( &m_InputState[ INPUT_STATE_CURRENT ], queuedState, true );
|
|
|
|
// Sample the joystick
|
|
SampleDevices();
|
|
|
|
// NOTE: This happens after SampleDevices since that updates LastSampleTick
|
|
// Also, I believe it's correct to post the joystick events with
|
|
// the LastPollTick not updated (not 100% sure though)
|
|
m_nLastPollTick = m_nLastSampleTick;
|
|
|
|
#if defined( PLATFORM_OSX )
|
|
PollInputState_OSX();
|
|
#elif defined( LINUX )
|
|
PollInputState_Linux();
|
|
#elif defined( WIN32 )
|
|
PollInputState_Windows();
|
|
#elif defined( _PS3 )
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
// Leave the queued state up-to-date with the current
|
|
CopyInputState( &queuedState, m_InputState[ INPUT_STATE_CURRENT ], false );
|
|
|
|
m_bIsPolling = false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Computes the sample tick
|
|
//-----------------------------------------------------------------------------
|
|
int CInputSystem::ComputeSampleTick()
|
|
{
|
|
// This logic will only fail if the app has been running for 49.7 days
|
|
int nSampleTick;
|
|
|
|
DWORD nCurrentTick = Plat_MSTime();
|
|
if ( nCurrentTick >= m_StartupTimeTick )
|
|
{
|
|
nSampleTick = (int)( nCurrentTick - m_StartupTimeTick );
|
|
}
|
|
else
|
|
{
|
|
DWORD nDelta = (DWORD)0xFFFFFFFF - m_StartupTimeTick;
|
|
nSampleTick = (int)( nCurrentTick + nDelta ) + 1;
|
|
}
|
|
return nSampleTick;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// How many times has poll been called?
|
|
//-----------------------------------------------------------------------------
|
|
int CInputSystem::GetPollCount() const
|
|
{
|
|
return m_nPollCount;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Samples attached devices and appends events to the input queue
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::SampleDevices( void )
|
|
{
|
|
m_nLastSampleTick = ComputeSampleTick();
|
|
|
|
static ConVarRef joystick_force_disabled( "joystick_force_disabled" );
|
|
#if !defined( PLATFORM_POSIX ) || defined( _GAMECONSOLE )
|
|
if ( joystick_force_disabled.IsValid() && joystick_force_disabled.GetBool() == false )
|
|
{
|
|
PollXDevices();
|
|
}
|
|
|
|
#endif
|
|
if ( m_bXController == false && joystick_force_disabled.IsValid() && joystick_force_disabled.GetBool() == false )
|
|
{
|
|
PollJoystick();
|
|
}
|
|
|
|
m_bSteamController = PollSteamControllers();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Forwards rumble info to attached devices
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::SetRumble( float fLeftMotor, float fRightMotor, int userId )
|
|
{
|
|
#ifndef LINUX
|
|
// TODO: send force feedback to rumble-enabled joysticks
|
|
SetXDeviceRumble( fLeftMotor, fRightMotor, userId );
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Force an immediate stop, transmits immediately to all devices
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::StopRumble( int userId )
|
|
{
|
|
if ( IsPlatformWindowsPC() )
|
|
{
|
|
if ( userId == INVALID_USER_ID )
|
|
{
|
|
xdevice_t* pXDevice = &m_XDevices[0];
|
|
|
|
for ( int i = 0; i < XUSER_MAX_COUNT; ++i, ++pXDevice )
|
|
{
|
|
if ( pXDevice->active )
|
|
{
|
|
pXDevice->vibration.wLeftMotorSpeed = 0;
|
|
pXDevice->vibration.wRightMotorSpeed = 0;
|
|
pXDevice->pendingRumbleUpdate = true;
|
|
WriteToXDevice( pXDevice );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xdevice_t* pXDevice = &m_XDevices[userId];
|
|
|
|
if ( pXDevice->active )
|
|
{
|
|
pXDevice->vibration.wLeftMotorSpeed = 0;
|
|
pXDevice->vibration.wRightMotorSpeed = 0;
|
|
pXDevice->pendingRumbleUpdate = true;
|
|
WriteToXDevice( pXDevice );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifndef LINUX
|
|
SetXDeviceRumble( 0, 0, userId );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Joystick interface
|
|
//-----------------------------------------------------------------------------
|
|
int CInputSystem::GetJoystickCount() const
|
|
{
|
|
return m_nJoystickCount;
|
|
}
|
|
|
|
void CInputSystem::EnableJoystickInput( int nJoystick, bool bEnable )
|
|
{
|
|
m_JoysticksEnabled.SetFlag( 1 << nJoystick, bEnable );
|
|
}
|
|
|
|
void CInputSystem::EnableJoystickDiagonalPOV( int nJoystick, bool bEnable )
|
|
{
|
|
m_pJoystickInfo[ nJoystick ].m_bDiagonalPOVControlEnabled = bEnable;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Poll current state
|
|
//-----------------------------------------------------------------------------
|
|
int CInputSystem::GetPollTick() const
|
|
{
|
|
return m_nLastPollTick;
|
|
}
|
|
|
|
bool CInputSystem::IsButtonDown( ButtonCode_t code ) const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_ButtonState.IsBitSet( code );
|
|
}
|
|
|
|
int CInputSystem::GetAnalogValue( AnalogCode_t code ) const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_pAnalogValue[code];
|
|
}
|
|
|
|
int CInputSystem::GetAnalogDelta( AnalogCode_t code ) const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_pAnalogDelta[code];
|
|
}
|
|
|
|
int CInputSystem::GetButtonPressedTick( ButtonCode_t code ) const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_ButtonPressedTick[code];
|
|
}
|
|
|
|
int CInputSystem::GetButtonReleasedTick( ButtonCode_t code ) const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_ButtonReleasedTick[code];
|
|
}
|
|
|
|
bool CInputSystem::MotionControllerActive( ) const
|
|
{
|
|
bool isReadingMotionControllerInput = IsDeviceReadingInput( INPUT_DEVICE_HYDRA ) ||
|
|
IsDeviceReadingInput( INPUT_DEVICE_PLAYSTATION_MOVE ) ||
|
|
IsDeviceReadingInput( INPUT_DEVICE_SHARPSHOOTER );
|
|
|
|
return ( isReadingMotionControllerInput && m_bMotionControllerActive );
|
|
}
|
|
|
|
Quaternion CInputSystem::GetMotionControllerOrientation( ) const
|
|
{
|
|
return m_qMotionControllerOrientation;
|
|
}
|
|
|
|
|
|
float CInputSystem::GetMotionControllerPosX( ) const
|
|
{
|
|
return m_fMotionControllerPosX;
|
|
}
|
|
|
|
float CInputSystem::GetMotionControllerPosY( ) const
|
|
{
|
|
return m_fMotionControllerPosY;
|
|
}
|
|
|
|
|
|
int CInputSystem::GetMotionControllerDeviceStatus( ) const
|
|
{
|
|
return m_nMotionControllerStatus;
|
|
}
|
|
|
|
void CInputSystem::SetMotionControllerDeviceStatus( int nStatus )
|
|
{
|
|
m_nMotionControllerStatus = nStatus;
|
|
}
|
|
|
|
uint64 CInputSystem::GetMotionControllerDeviceStatusFlags( ) const
|
|
{
|
|
return m_nMotionControllerStatusFlags;
|
|
}
|
|
|
|
#if defined( _OSX ) || defined (LINUX)
|
|
// this is defined in xcontroller.cpp, but that file isn't included
|
|
// in posix builds
|
|
void CInputSystem::SetMotionControllerCalibrationInvalid( void )
|
|
{
|
|
}
|
|
|
|
void CInputSystem::StepMotionControllerCalibration( void )
|
|
{
|
|
|
|
}
|
|
|
|
void CInputSystem::ResetMotionControllerScreenCalibration( void )
|
|
{
|
|
|
|
}
|
|
|
|
#endif // _OSX
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the input events since the last poll
|
|
//-----------------------------------------------------------------------------
|
|
int CInputSystem::GetEventCount() const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_Events.Count();
|
|
}
|
|
|
|
const InputEvent_t* CInputSystem::GetEventData( ) const
|
|
{
|
|
return m_InputState[INPUT_STATE_CURRENT].m_Events.Base();
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Posts a user-defined event into the event queue; this is expected
|
|
// to be called in overridden wndprocs connected to the root panel.
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::PostUserEvent( const InputEvent_t &event )
|
|
{
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
state.m_Events.AddToTail( event );
|
|
state.m_bDirty = true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Chains the window message to the previous wndproc
|
|
//-----------------------------------------------------------------------------
|
|
inline LRESULT CInputSystem::ChainWindowMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
#if !defined( PLATFORM_POSIX )
|
|
if ( m_ChainedWndProc )
|
|
return CallWindowProc( m_ChainedWndProc, hwnd, uMsg, wParam, lParam );
|
|
#endif
|
|
// FIXME: This comment is lifted from vguimatsurface;
|
|
// may not apply in future when the system is completed.
|
|
|
|
// This means the application is driving the messages (calling our window procedure manually)
|
|
// rather than us hooking their window procedure. The engine needs to do this in order for VCR
|
|
// mode to play back properly.
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Release all buttons
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::ReleaseAllButtons( int nFirstButton, int nLastButton )
|
|
{
|
|
// Force button up messages for all down buttons
|
|
for ( int i = nFirstButton; i <= nLastButton; ++i )
|
|
{
|
|
PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, (ButtonCode_t)i, (ButtonCode_t)i );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Zero analog state
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::ZeroAnalogState( int nFirstState, int nLastState )
|
|
{
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
memset( &state.m_pAnalogDelta[nFirstState], 0, ( nLastState - nFirstState + 1 ) * sizeof(int) );
|
|
memset( &state.m_pAnalogValue[nFirstState], 0, ( nLastState - nFirstState + 1 ) * sizeof(int) );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Determines all mouse button presses
|
|
//-----------------------------------------------------------------------------
|
|
int CInputSystem::ButtonMaskFromMouseWParam( WPARAM wParam, ButtonCode_t code, bool bDown ) const
|
|
{
|
|
int nButtonMask = 0;
|
|
|
|
#if !defined( POSIX ) && !defined( USE_SDL)
|
|
if ( wParam & MK_LBUTTON )
|
|
{
|
|
nButtonMask |= 1;
|
|
}
|
|
|
|
if ( wParam & MK_RBUTTON )
|
|
{
|
|
nButtonMask |= 2;
|
|
}
|
|
|
|
if ( wParam & MK_MBUTTON )
|
|
{
|
|
nButtonMask |= 4;
|
|
}
|
|
|
|
if ( wParam & MS_MK_BUTTON4 )
|
|
{
|
|
nButtonMask |= 8;
|
|
}
|
|
|
|
if ( wParam & MS_MK_BUTTON5 )
|
|
{
|
|
nButtonMask |= 16;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
if ( code != BUTTON_CODE_INVALID )
|
|
{
|
|
int nMsgMask = 1 << ( code - MOUSE_FIRST );
|
|
int nTestMask = bDown ? nMsgMask : 0;
|
|
Assert( ( nButtonMask & nMsgMask ) == nTestMask );
|
|
}
|
|
#endif
|
|
|
|
return nButtonMask;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Updates the state of all mouse buttons
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::UpdateMouseButtonState( int nButtonMask, ButtonCode_t dblClickCode )
|
|
{
|
|
for ( int i = 0; i < 5; ++i )
|
|
{
|
|
ButtonCode_t code = (ButtonCode_t)( MOUSE_FIRST + i );
|
|
bool bDown = ( nButtonMask & ( 1 << i ) ) != 0;
|
|
if ( bDown )
|
|
{
|
|
InputEventType_t type = ( code != dblClickCode ) ? IE_ButtonPressed : IE_ButtonDoubleClicked;
|
|
PostButtonPressedEvent( type, m_nLastSampleTick, code, code );
|
|
}
|
|
else
|
|
{
|
|
PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handles input messages
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::SetCursorPosition( int x, int y )
|
|
{
|
|
if ( !m_hAttachedHWnd )
|
|
return;
|
|
|
|
#if defined( USE_SDL )
|
|
m_pLauncherMgr->SetCursorPosition( x, y );
|
|
#elif defined( OSX )
|
|
m_pLauncherMgr->SetCursorPosition( x, y );
|
|
#elif defined( WIN32 )
|
|
POINT pt;
|
|
pt.x = x; pt.y = y;
|
|
ClientToScreen( (HWND)m_hAttachedHWnd, &pt );
|
|
SetCursorPos( pt.x, pt.y );
|
|
#elif defined( PLATFORM_PS3 )
|
|
POINT pt;
|
|
pt.x = x; pt.y = y;
|
|
SetCursorPos( pt.x, pt.y );
|
|
#else
|
|
#error
|
|
#endif
|
|
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
bool bXChanged = ( state.m_pAnalogValue[ MOUSE_X ] != x );
|
|
bool bYChanged = ( state.m_pAnalogValue[ MOUSE_Y ] != y );
|
|
|
|
state.m_pAnalogValue[ MOUSE_X ] = x;
|
|
state.m_pAnalogValue[ MOUSE_Y ] = y;
|
|
state.m_pAnalogDelta[ MOUSE_X ] = 0;
|
|
state.m_pAnalogDelta[ MOUSE_Y ] = 0;
|
|
|
|
if ( bXChanged )
|
|
{
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogDelta[ MOUSE_X ] );
|
|
}
|
|
if ( bYChanged )
|
|
{
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, state.m_pAnalogValue[ MOUSE_Y ], state.m_pAnalogDelta[ MOUSE_Y ] );
|
|
}
|
|
if ( bXChanged || bYChanged )
|
|
{
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogValue[ MOUSE_Y ] );
|
|
}
|
|
}
|
|
|
|
void CInputSystem::GetCursorPosition( int *pX, int *pY )
|
|
{
|
|
if ( !m_hAttachedHWnd )
|
|
{
|
|
*pX = *pY = 0;
|
|
return;
|
|
}
|
|
|
|
#if defined( USE_SDL )
|
|
*pX = m_InputState[INPUT_STATE_CURRENT].m_pAnalogValue[MOUSE_X];
|
|
*pY = m_InputState[INPUT_STATE_CURRENT].m_pAnalogValue[MOUSE_Y];
|
|
#elif defined( PLATFORM_OSX )
|
|
if ( m_bCursorVisible )
|
|
{
|
|
CGEventRef event = CGEventCreate( NULL );
|
|
CGPoint pnt = CGEventGetLocation( event );
|
|
|
|
// [will] - QuickDraw functions removed in 10.7, so using using CocoaMgr for window info instead.
|
|
unsigned int displayWidth, displayHeight;
|
|
m_pLauncherMgr->DisplayedSize( displayWidth, displayHeight );
|
|
|
|
*pX = pnt.x;
|
|
*pY = pnt.y;
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
int rx, ry, width, height;
|
|
pRenderContext->GetViewport( rx, ry, width, height );
|
|
|
|
int windowHeight = (int)displayWidth;
|
|
int windowWidth = (int)displayHeight;
|
|
if ( width != windowWidth || abs( height - windowHeight ) > 22 )
|
|
{
|
|
// scale the x/y back into the co-ords of the back buffer, not the scaled up window
|
|
//DevMsg( "Mouse x:%d y:%d %d %d %d %d\n", x, y, width, windowWidth, height, abs( height - windowHeight ) );
|
|
*pX = *pX * (float)width/windowWidth;
|
|
*pY = *pY * (float)height/windowHeight;
|
|
}
|
|
|
|
CFRelease( event );
|
|
}
|
|
else
|
|
{
|
|
// cursor is invisible, just say the center of the screen
|
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
|
|
int rx, ry, width, height;
|
|
pRenderContext->GetViewport( rx, ry, width, height );
|
|
*pX = width/2;
|
|
*pY = height/2;
|
|
}
|
|
#elif !defined( PLATFORM_POSIX )
|
|
POINT pt;
|
|
::GetCursorPos( &pt );
|
|
ScreenToClient((HWND)m_hAttachedHWnd, &pt);
|
|
*pX = pt.x; *pY = pt.y;
|
|
#endif
|
|
}
|
|
|
|
void CInputSystem::SetMouseCursorVisible( bool bVisible )
|
|
{
|
|
m_bCursorVisible = bVisible;
|
|
}
|
|
|
|
|
|
void CInputSystem::UpdateMousePositionState( InputState_t &state, short x, short y )
|
|
{
|
|
int nOldX = state.m_pAnalogValue[ MOUSE_X ];
|
|
int nOldY = state.m_pAnalogValue[ MOUSE_Y ];
|
|
|
|
state.m_pAnalogValue[ MOUSE_X ] = x;
|
|
state.m_pAnalogValue[ MOUSE_Y ] = y;
|
|
state.m_pAnalogDelta[ MOUSE_X ] = state.m_pAnalogValue[ MOUSE_X ] - nOldX;
|
|
state.m_pAnalogDelta[ MOUSE_Y ] = state.m_pAnalogValue[ MOUSE_Y ] - nOldY;
|
|
|
|
if ( state.m_pAnalogDelta[ MOUSE_X ] != 0 )
|
|
{
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogDelta[ MOUSE_X ] );
|
|
}
|
|
if ( state.m_pAnalogDelta[ MOUSE_Y ] != 0 )
|
|
{
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, state.m_pAnalogValue[ MOUSE_Y ], state.m_pAnalogDelta[ MOUSE_Y ] );
|
|
}
|
|
if ( state.m_pAnalogDelta[ MOUSE_X ] != 0 || state.m_pAnalogDelta[ MOUSE_Y ] != 0 )
|
|
{
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogValue[ MOUSE_Y ] );
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
//-----------------------------------------------------------------------------
|
|
// Generates LocateMouseClick messages
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::LocateMouseClick( LPARAM lParam )
|
|
{
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
PostEvent( IE_LocateMouseClick, m_nLastSampleTick, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Handles input messages
|
|
//-----------------------------------------------------------------------------
|
|
LRESULT CInputSystem::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
#if !defined( POSIX ) && !defined( USE_SDL )
|
|
|
|
if ( !m_bEnabled )
|
|
return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
|
|
|
|
if ( ShouldGenerateUIEvents() && ( hwnd != m_hLastIMEHWnd ) )
|
|
{
|
|
m_hLastIMEHWnd = hwnd;
|
|
PostEvent( IE_IMESetWindow, m_nLastSampleTick, (intp)hwnd );
|
|
}
|
|
|
|
// Allow ActivateApp messages to get through so we know when to reset input state
|
|
if ( ( hwnd != m_hAttachedHWnd ) && ( uMsg != WM_ACTIVATEAPP ) )
|
|
return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
|
|
|
|
InputState_t &state = m_InputState[ m_bIsPolling ];
|
|
switch( uMsg )
|
|
{
|
|
case WM_ACTIVATEAPP:
|
|
if ( hwnd == m_hAttachedHWnd )
|
|
{
|
|
bool bActivated = ( wParam == 1 );
|
|
if ( !bActivated )
|
|
{
|
|
ResetInputState();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_CLOSE:
|
|
// Handle close messages
|
|
PostEvent( IE_Close, m_nLastSampleTick );
|
|
|
|
// don't Run default message pump, as that destroys the window
|
|
return 0;
|
|
|
|
case WM_SETCURSOR:
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
PostEvent( IE_SetCursor, m_nLastSampleTick );
|
|
}
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
{
|
|
int nWidth = LOWORD( lParam );
|
|
int nHeight = HIWORD( lParam );
|
|
bool bMinimized = ( wParam == SIZE_MINIMIZED ) || IsIconic( hwnd );
|
|
if ( bMinimized )
|
|
{
|
|
nWidth = nHeight = 0;
|
|
}
|
|
PostEvent( IE_WindowSizeChanged, m_nLastSampleTick, nWidth, nHeight, bMinimized );
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true );
|
|
ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, false );
|
|
ETWMouseUp( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case WM_RBUTTONDOWN:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true );
|
|
ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case WM_RBUTTONUP:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, false );
|
|
ETWMouseUp( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case WM_MBUTTONDOWN:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true );
|
|
ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case WM_MBUTTONUP:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, false );
|
|
ETWMouseUp( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
case MS_WM_XBUTTONDOWN:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
|
|
ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, true );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
|
|
// Windows docs say the XBUTTON messages we should return true from
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case MS_WM_XBUTTONUP:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
|
|
ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, false );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
|
|
// Windows docs say the XBUTTON messages we should return true from
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true );
|
|
ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask, MOUSE_LEFT );
|
|
}
|
|
break;
|
|
|
|
case WM_RBUTTONDBLCLK:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true );
|
|
ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask, MOUSE_RIGHT );
|
|
}
|
|
break;
|
|
|
|
case WM_MBUTTONDBLCLK:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true );
|
|
ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
UpdateMouseButtonState( nButtonMask, MOUSE_MIDDLE );
|
|
}
|
|
break;
|
|
|
|
case MS_WM_XBUTTONDBLCLK:
|
|
{
|
|
LocateMouseClick( lParam );
|
|
|
|
ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, true );
|
|
UpdateMouseButtonState( nButtonMask, code );
|
|
|
|
// Windows docs say the XBUTTON messages we should return true from
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_KEYDOWN:
|
|
case WM_SYSKEYDOWN:
|
|
{
|
|
// Suppress key repeats
|
|
if ( !( lParam & ( 1<<30 ) ) )
|
|
{
|
|
// NOTE: These two can be unequal! For example, keypad enter
|
|
// which returns KEY_ENTER from virtual keys, and KEY_PAD_ENTER from scan codes
|
|
// Since things like vgui care about virtual keys; we're going to
|
|
// put both scan codes in the input message
|
|
ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
|
|
ButtonCode_t scanCode = ButtonCode_ScanCodeToButtonCode( lParam );
|
|
PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, scanCode, virtualCode );
|
|
|
|
// Post ETW events describing key presses to help correlate input events to performance
|
|
// problems in the game.
|
|
ETWKeyDown( scanCode, virtualCode, ButtonCodeToString( virtualCode ) );
|
|
|
|
// Deal with toggles
|
|
if ( scanCode == KEY_CAPSLOCK || scanCode == KEY_SCROLLLOCK || scanCode == KEY_NUMLOCK )
|
|
{
|
|
int nVirtualKey;
|
|
ButtonCode_t toggleCode;
|
|
switch( scanCode )
|
|
{
|
|
default: case KEY_CAPSLOCK: nVirtualKey = VK_CAPITAL; toggleCode = KEY_CAPSLOCKTOGGLE; break;
|
|
case KEY_SCROLLLOCK: nVirtualKey = VK_SCROLL; toggleCode = KEY_SCROLLLOCKTOGGLE; break;
|
|
case KEY_NUMLOCK: nVirtualKey = VK_NUMLOCK; toggleCode = KEY_NUMLOCKTOGGLE; break;
|
|
};
|
|
|
|
SHORT wState = GetKeyState( nVirtualKey );
|
|
bool bToggleState = ( wState & 0x1 ) != 0;
|
|
PostButtonPressedEvent( bToggleState ? IE_ButtonPressed : IE_ButtonReleased, m_nLastSampleTick, toggleCode, toggleCode );
|
|
}
|
|
}
|
|
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
|
|
int nKeyRepeat = LOWORD( lParam );
|
|
for ( int i = 0; i < nKeyRepeat; ++i )
|
|
{
|
|
PostEvent( IE_KeyCodeTyped, m_nLastSampleTick, virtualCode );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
case WM_SYSKEYUP:
|
|
{
|
|
// Don't handle key ups if the key's already up. This can happen when we alt-tab back to the engine.
|
|
ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
|
|
ButtonCode_t scanCode = ButtonCode_ScanCodeToButtonCode( lParam );
|
|
PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, scanCode, virtualCode );
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEWHEEL:
|
|
{
|
|
ButtonCode_t code = (short)HIWORD( wParam ) > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
|
|
state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
|
|
PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
|
|
PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
|
|
|
|
state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( (short)HIWORD(wParam) ) / WHEEL_DELTA;
|
|
state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
{
|
|
UpdateMousePositionState( state, (short)LOWORD(lParam), (short)HIWORD(lParam) );
|
|
|
|
int nButtonMask = ButtonMaskFromMouseWParam( wParam );
|
|
UpdateMouseButtonState( nButtonMask );
|
|
}
|
|
break;
|
|
|
|
#if defined ( WIN32 ) && !defined ( _X360 )
|
|
case WM_INPUT:
|
|
{
|
|
if ( m_bRawInputSupported )
|
|
{
|
|
UINT dwSize = sizeof( RAWINPUT );
|
|
static BYTE lpb[ sizeof( RAWINPUT ) ];
|
|
|
|
pfnGetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
|
|
|
|
RAWINPUT* raw = (RAWINPUT*)lpb;
|
|
if (raw->header.dwType == RIM_TYPEMOUSE)
|
|
{
|
|
m_mouseRawAccumX += raw->data.mouse.lLastX;
|
|
m_mouseRawAccumY += raw->data.mouse.lLastY;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
case WM_SYSCHAR:
|
|
case WM_CHAR:
|
|
if ( ShouldGenerateUIEvents() && !m_bIMEComposing )
|
|
{
|
|
PostEvent( IE_KeyTyped, m_nLastSampleTick, (wchar_t)wParam );
|
|
}
|
|
break;
|
|
|
|
case WM_INPUTLANGCHANGE:
|
|
// Note that this is passed to IME managers even if the IME is currently
|
|
// disallowed so that IMEs are still aware of the current language
|
|
// in case they are allowed in the future.
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI )
|
|
{
|
|
g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam );
|
|
}
|
|
#endif
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
PostEvent( IE_InputLanguageChanged, m_nLastSampleTick );
|
|
}
|
|
break;
|
|
|
|
case WM_IME_KEYDOWN:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI && g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam ) )
|
|
return 0;
|
|
#endif
|
|
break;
|
|
|
|
case WM_IME_STARTCOMPOSITION:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI && g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam ) )
|
|
{
|
|
m_bIMEComposing = true;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
m_bIMEComposing = true;
|
|
PostEvent( IE_IMEStartComposition, m_nLastSampleTick );
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_COMPOSITION:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI && g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam ) )
|
|
return 0;
|
|
#endif
|
|
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
PostEvent( IE_IMEComposition, m_nLastSampleTick, (int)lParam );
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_ENDCOMPOSITION:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI && g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam ) )
|
|
{
|
|
m_bIMEComposing = false;
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
m_bIMEComposing = false;
|
|
PostEvent( IE_IMEEndComposition, m_nLastSampleTick );
|
|
return TRUE;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_NOTIFY:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI && g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam ) )
|
|
return 0;
|
|
#endif
|
|
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
switch (wParam)
|
|
{
|
|
default:
|
|
break;
|
|
|
|
case 14: // Chinese Traditional IMN_PRIVATE...
|
|
break;
|
|
|
|
case IMN_OPENCANDIDATE:
|
|
PostEvent( IE_IMEShowCandidates, m_nLastSampleTick );
|
|
return 1;
|
|
|
|
case IMN_CHANGECANDIDATE:
|
|
PostEvent( IE_IMEChangeCandidates, m_nLastSampleTick );
|
|
return 0;
|
|
|
|
case IMN_CLOSECANDIDATE:
|
|
PostEvent( IE_IMECloseCandidates, m_nLastSampleTick );
|
|
break;
|
|
|
|
// To detect the change of IME mode, or the toggling of Japanese IME
|
|
case IMN_SETCONVERSIONMODE:
|
|
case IMN_SETSENTENCEMODE:
|
|
case IMN_SETOPENSTATUS:
|
|
PostEvent( IE_IMERecomputeModes, m_nLastSampleTick );
|
|
if ( wParam == IMN_SETOPENSTATUS )
|
|
return 0;
|
|
break;
|
|
|
|
case IMN_CLOSESTATUSWINDOW:
|
|
case IMN_GUIDELINE:
|
|
case IMN_OPENSTATUSWINDOW:
|
|
case IMN_SETCANDIDATEPOS:
|
|
case IMN_SETCOMPOSITIONFONT:
|
|
case IMN_SETCOMPOSITIONWINDOW:
|
|
case IMN_SETSTATUSWINDOWPOS:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_IME_CHAR:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI && g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam ) )
|
|
return 0;
|
|
#endif
|
|
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
// We need to process this message so that the IME doesn't double
|
|
// convert the unicode IME characters into garbage characters and post
|
|
// them to our window... (get ? marks after text entry ).
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_IME_SETCONTEXT:
|
|
#if defined( INCLUDE_SCALEFORM )
|
|
if ( g_pScaleformUI )
|
|
{
|
|
g_pScaleformUI->HandleIMEEvent( (size_t)hwnd, uMsg, wParam, lParam );
|
|
lParam = 0;
|
|
}
|
|
else
|
|
#endif
|
|
if ( ShouldGenerateUIEvents() )
|
|
{
|
|
// We draw all IME windows ourselves
|
|
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
|
lParam &= ~ISC_SHOWUIGUIDELINE;
|
|
lParam &= ~ISC_SHOWUIALLCANDIDATEWINDOW;
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Can't put this in the case statement, it's not constant
|
|
if ( IsPC() && ( uMsg == m_uiMouseWheel ) )
|
|
{
|
|
ButtonCode_t code = ( ( int )wParam ) > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
|
|
state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
|
|
PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
|
|
PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
|
|
|
|
state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( ( int )wParam ) / WHEEL_DELTA;
|
|
state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
|
|
PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
|
|
}
|
|
return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Initializes, shuts down cursors
|
|
//-----------------------------------------------------------------------------
|
|
void CInputSystem::InitCursors()
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
// load up all default cursors
|
|
memset( m_pDefaultCursors, 0, sizeof(m_pDefaultCursors) );
|
|
m_pDefaultCursors[INPUT_CURSOR_NONE] = INPUT_CURSOR_HANDLE_INVALID;
|
|
m_pDefaultCursors[INPUT_CURSOR_ARROW] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_NORMAL);
|
|
m_pDefaultCursors[INPUT_CURSOR_IBEAM] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_IBEAM);
|
|
m_pDefaultCursors[INPUT_CURSOR_HOURGLASS] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_WAIT);
|
|
m_pDefaultCursors[INPUT_CURSOR_CROSSHAIR] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_CROSS);
|
|
m_pDefaultCursors[INPUT_CURSOR_WAITARROW] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)32650);
|
|
m_pDefaultCursors[INPUT_CURSOR_UP] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_UP);
|
|
m_pDefaultCursors[INPUT_CURSOR_SIZE_NW_SE] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_SIZENWSE);
|
|
m_pDefaultCursors[INPUT_CURSOR_SIZE_NE_SW] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_SIZENESW);
|
|
m_pDefaultCursors[INPUT_CURSOR_SIZE_W_E] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_SIZEWE);
|
|
m_pDefaultCursors[INPUT_CURSOR_SIZE_N_S] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_SIZENS);
|
|
m_pDefaultCursors[INPUT_CURSOR_SIZE_ALL] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_SIZEALL);
|
|
m_pDefaultCursors[INPUT_CURSOR_NO] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)OCR_NO);
|
|
m_pDefaultCursors[INPUT_CURSOR_HAND] = (InputCursorHandle_t)LoadCursor(NULL, (LPCTSTR)32649);
|
|
#endif
|
|
}
|
|
|
|
void CInputSystem::ShutdownCursors()
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
int nCount = m_UserCursors.GetNumStrings();
|
|
for ( int i = 0; i < nCount; ++i )
|
|
{
|
|
::DestroyCursor( (HCURSOR)m_UserCursors[ i ] );
|
|
}
|
|
m_UserCursors.Purge();
|
|
|
|
for ( int i = 0; i < ARRAYSIZE( m_pDefaultCursors ); ++i )
|
|
{
|
|
if ( m_pDefaultCursors[i] != INPUT_CURSOR_HANDLE_INVALID )
|
|
{
|
|
::DestroyCursor( (HCURSOR)m_pDefaultCursors[ i ] );
|
|
m_pDefaultCursors[ i ] = INPUT_CURSOR_HANDLE_INVALID;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the cursor
|
|
//-----------------------------------------------------------------------------
|
|
InputCursorHandle_t CInputSystem::GetStandardCursor( InputStandardCursor_t id )
|
|
{
|
|
return m_pDefaultCursors[id];
|
|
}
|
|
|
|
InputCursorHandle_t CInputSystem::LoadCursorFromFile( const char *pFileName, const char *pPathID )
|
|
{
|
|
if ( !g_pFullFileSystem )
|
|
return INPUT_CURSOR_HANDLE_INVALID;
|
|
|
|
char fn[ 512 ];
|
|
Q_strncpy( fn, pFileName, sizeof( fn ) );
|
|
Q_strlower( fn );
|
|
Q_FixSlashes( fn );
|
|
|
|
UtlSymId_t nCursorIndex = m_UserCursors.Find( fn );
|
|
if ( nCursorIndex != m_UserCursors.InvalidIndex() )
|
|
return m_UserCursors[ nCursorIndex ];
|
|
|
|
g_pFullFileSystem->GetLocalCopy( fn );
|
|
|
|
#ifdef PLATFORM_WINDOWS
|
|
char fullpath[ 512 ];
|
|
g_pFullFileSystem->RelativePathToFullPath( fn, pPathID, fullpath, sizeof( fullpath ) );
|
|
|
|
HCURSOR newCursor = (HCURSOR)::LoadCursorFromFile( fullpath );
|
|
m_UserCursors[ fn ] = (InputCursorHandle_t)newCursor;
|
|
return (InputCursorHandle_t)newCursor;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
void CInputSystem::SetCursorIcon( InputCursorHandle_t hCursor )
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
m_hCursor = hCursor;
|
|
HCURSOR hWindowsCursor = (HCURSOR)hCursor;
|
|
::SetCursor( hWindowsCursor );
|
|
#endif
|
|
}
|
|
|
|
void CInputSystem::ResetCursorIcon()
|
|
{
|
|
SetCursorIcon( m_hCursor );
|
|
}
|
|
|
|
void CInputSystem::EnableMouseCapture( PlatWindow_t hWnd )
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
if ( m_hCurrentCaptureWnd == hWnd )
|
|
return;
|
|
|
|
// Determine if we're the foreground window. If not, force release of the mouse. Otherwise, we can capture the mouse
|
|
// while we're in the background and then we never get WM_ACTIVATE messages when trying to click on the app. This
|
|
// causes the app to react like it has mouse focus (firing weapons, etc) but doesn't actually come to the foreground
|
|
// and doesn't accept keyboard input.
|
|
//
|
|
// We're using GetForegroundWindow here, but we really want to ask engine or game if they're the ActiveApp.
|
|
bool bActiveWindow = true;
|
|
|
|
#if !defined( _GAMECONSOLE )
|
|
HWND hInputWnd = reinterpret_cast< HWND >( hWnd );
|
|
bActiveWindow = ( hInputWnd == ::GetForegroundWindow() );
|
|
#else
|
|
HWND hInputWnd = reinterpret_cast< HWND >( m_hCurrentCaptureWnd );
|
|
#endif
|
|
|
|
if ( m_hCurrentCaptureWnd != PLAT_WINDOW_INVALID || !bActiveWindow )
|
|
{
|
|
::ReleaseCapture();
|
|
}
|
|
|
|
m_hCurrentCaptureWnd = hWnd;
|
|
if ( m_hCurrentCaptureWnd != PLAT_WINDOW_INVALID && bActiveWindow )
|
|
{
|
|
::SetCapture( hInputWnd );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CInputSystem::GetRawMouseAccumulators( int& accumX, int& accumY )
|
|
{
|
|
#if defined( USE_SDL )
|
|
|
|
if ( m_pLauncherMgr )
|
|
{
|
|
m_pLauncherMgr->GetMouseDelta( accumX, accumY, false );
|
|
}
|
|
|
|
#else
|
|
|
|
accumX = m_mouseRawAccumX;
|
|
accumY = m_mouseRawAccumY;
|
|
m_mouseRawAccumX = m_mouseRawAccumY = 0;
|
|
|
|
#endif
|
|
}
|
|
|
|
void CInputSystem::DisableMouseCapture()
|
|
{
|
|
#ifdef PLATFORM_WINDOWS
|
|
EnableMouseCapture( PLAT_WINDOW_INVALID );
|
|
#endif
|
|
}
|
|
|
|
|
|
// ===================================================================
|
|
// If we add another support for another input device, we need to
|
|
// update the platform assignments below to reflect it. From here,
|
|
// pretty much everything else that uses these interfaces will work
|
|
// unchanged (obviously UI and device code needs to be added)
|
|
// Also: Add name to GetInputDeviceNameUI/Internal() in
|
|
// PlatformInputDevice.cpp
|
|
// ===================================================================
|
|
void CInputSystem::InitPlatfromInputDeviceInfo( void )
|
|
{
|
|
PlatformInputDevice::InitPlatfromInputDeviceInfo();
|
|
|
|
// Set the platform for which this code/client is compiled on and
|
|
// the input devices that are assumed to be already installed (as
|
|
// opposed to being queried by the inputsystem)
|
|
|
|
#if defined( PLATFORM_WINDOWS_PC )
|
|
m_currentlyConnectedInputDevices = INPUT_DEVICE_KEYBOARD_MOUSE;
|
|
#elif defined( PLATFORM_OSX )
|
|
m_currentlyConnectedInputDevices = INPUT_DEVICE_KEYBOARD_MOUSE;
|
|
#elif defined( PLATFORM_LINUX )
|
|
m_currentlyConnectedInputDevices = INPUT_DEVICE_KEYBOARD_MOUSE;
|
|
#elif defined( PLATFORM_X360 )
|
|
m_currentlyConnectedInputDevices = INPUT_DEVICE_GAMEPAD;
|
|
#elif defined( PLATFORM_PS3 )
|
|
m_currentlyConnectedInputDevices = INPUT_DEVICE_NONE;
|
|
#else
|
|
m_currentlyConnectedInputDevices = INPUT_DEVICE_NONE;
|
|
#endif
|
|
|
|
ResetCurrentInputDevice();
|
|
|
|
m_setCurrentInputDeviceOnNextButtonPress = false;
|
|
}
|
|
|
|
|
|
void CInputSystem::ResetCurrentInputDevice( void )
|
|
{
|
|
if ( m_currentInputDevice == INPUT_DEVICE_STEAM_CONTROLLER )
|
|
{
|
|
// Disable resetting away from the steam controller if it's being used.
|
|
return;
|
|
}
|
|
|
|
#if defined( PLATFORM_WINDOWS_PC )
|
|
m_currentInputDevice = INPUT_DEVICE_KEYBOARD_MOUSE;
|
|
#elif defined( PLATFORM_OSX )
|
|
m_currentInputDevice = INPUT_DEVICE_KEYBOARD_MOUSE;
|
|
#elif defined( PLATFORM_LINUX )
|
|
m_currentInputDevice = INPUT_DEVICE_KEYBOARD_MOUSE;
|
|
#elif defined( PLATFORM_X360 )
|
|
m_currentInputDevice = INPUT_DEVICE_GAMEPAD;
|
|
#elif defined( PLATFORM_PS3 )
|
|
m_currentInputDevice = INPUT_DEVICE_NONE;
|
|
#else
|
|
m_currentInputDevice = INPUT_DEVICE_NONE;
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
InputDevice_t CInputSystem::GetConnectedInputDevices( void )
|
|
{
|
|
return m_currentlyConnectedInputDevices;
|
|
}
|
|
|
|
|
|
bool CInputSystem::IsInputDeviceConnected( InputDevice_t device )
|
|
{
|
|
if ( countBits( device ) != 1 || ( device & PlatformInputDevice::s_AllInputDevices ) != device )
|
|
{
|
|
AssertMsg( false, "invalid input device" );
|
|
return false;
|
|
}
|
|
|
|
return ( ( m_currentlyConnectedInputDevices & device ) == device );
|
|
}
|
|
|
|
|
|
void CInputSystem::SetInputDeviceConnected( InputDevice_t device, bool connected )
|
|
{
|
|
if ( ( countBits( device ) != 1 ) || ( device & PlatformInputDevice::s_validPlatformInputDevices[PlatformInputDevice::s_LocalInputPlatform] ) != device )
|
|
{
|
|
AssertMsg( false, "invalid input device" );
|
|
return;
|
|
}
|
|
|
|
if ( connected )
|
|
{
|
|
// Message if device already connected?
|
|
m_currentlyConnectedInputDevices = m_currentlyConnectedInputDevices | device;
|
|
}
|
|
else
|
|
{
|
|
// Message if device not currently connected?
|
|
m_currentlyConnectedInputDevices = m_currentlyConnectedInputDevices & (~device);
|
|
}
|
|
}
|
|
|
|
|
|
InputDevice_t CInputSystem::IsOnlySingleDeviceConnected( void )
|
|
{
|
|
int32 mask = 1;
|
|
|
|
// nav controller doesn't need to be considered a seperate device.
|
|
int32 connectedMask = m_currentlyConnectedInputDevices & (~INPUT_DEVICE_MOVE_NAV_CONTROLLER);
|
|
|
|
if ( IsInputDeviceConnected( INPUT_DEVICE_SHARPSHOOTER ) )
|
|
{
|
|
connectedMask = m_currentlyConnectedInputDevices & ( ~INPUT_DEVICE_PLAYSTATION_MOVE );
|
|
}
|
|
|
|
// [dkorus] loop through a mask that represents each possible device.
|
|
// if one matches our connected mask exactly, we have only that device connected
|
|
while( mask <= INPUT_DEVICE_MAX )
|
|
{
|
|
if ( connectedMask == mask )
|
|
return (InputDevice_t) connectedMask;
|
|
mask = mask << 1;
|
|
}
|
|
|
|
return INPUT_DEVICE_NONE;
|
|
}
|
|
|
|
|
|
bool CInputSystem::IsDeviceReadingInput( InputDevice_t device ) const
|
|
{
|
|
#ifndef _GAMECONSOLE
|
|
return true;
|
|
#endif
|
|
|
|
#if !defined( _CERT )
|
|
// [dkorus] test code for the device selection
|
|
int forceSelected = dev_force_selected_device.GetInt();
|
|
if ( forceSelected != 0)
|
|
{
|
|
if ( device == forceSelected )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( device == m_currentInputDevice ||
|
|
m_currentInputDevice == INPUT_DEVICE_NONE )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
InputDevice_t CInputSystem::GetCurrentInputDevice( void )
|
|
{
|
|
return m_currentInputDevice;
|
|
}
|
|
|
|
|
|
void CInputSystem::SetCurrentInputDevice( InputDevice_t device )
|
|
{
|
|
if ( ( device != INPUT_DEVICE_NONE ) &&
|
|
( ( countBits( device ) != 1 ) || ( device & PlatformInputDevice::s_validPlatformInputDevices[PlatformInputDevice::s_LocalInputPlatform] ) != device ) )
|
|
{
|
|
AssertMsg( false, "invalid input device" );
|
|
return;
|
|
}
|
|
|
|
m_currentInputDevice = device;
|
|
}
|
|
|
|
void CInputSystem::SampleInputToFindCurrentDevice( bool doSample )
|
|
{
|
|
m_setCurrentInputDeviceOnNextButtonPress = doSample;
|
|
}
|
|
|
|
bool CInputSystem::IsSamplingForCurrentDevice( void )
|
|
{
|
|
return m_setCurrentInputDeviceOnNextButtonPress;
|
|
}
|
|
|
|
|
|
#ifndef LINUX
|
|
|
|
#if !defined( _CERT )
|
|
// [mhansen] Add support for pressing Xbox 360 controller buttons (should work on PS3 too)
|
|
struct C_press_x360_button_code
|
|
{
|
|
char c1;
|
|
char c2;
|
|
xKey_t key;
|
|
};
|
|
|
|
static const C_press_x360_button_code press_x360_button_codes[] =
|
|
{
|
|
{ 'l', 't', XK_BUTTON_LTRIGGER },
|
|
{ 'r', 't', XK_BUTTON_RTRIGGER },
|
|
{ 's', 't', XK_BUTTON_START },
|
|
{ 'b', 'a', XK_BUTTON_BACK },
|
|
{ 'l', 'b', XK_BUTTON_LEFT_SHOULDER },
|
|
{ 'r', 'b', XK_BUTTON_RIGHT_SHOULDER },
|
|
{ 'l', 's', XK_BUTTON_LEFT_SHOULDER },
|
|
{ 'r', 's', XK_BUTTON_RIGHT_SHOULDER },
|
|
{ 'a', 0, XK_BUTTON_A },
|
|
{ 'b', 0, XK_BUTTON_B },
|
|
{ 'x', 0, XK_BUTTON_X },
|
|
{ 'y', 0, XK_BUTTON_Y },
|
|
{ 'l', 0, XK_BUTTON_LEFT },
|
|
{ 'r', 0, XK_BUTTON_RIGHT },
|
|
{ 'u', 0, XK_BUTTON_UP },
|
|
{ 'd', 0, XK_BUTTON_DOWN },
|
|
};
|
|
static const int cNum_press_x360_button_codes = ARRAYSIZE( press_x360_button_codes );
|
|
|
|
void CInputSystem::PressX360Button( const CCommand &args )
|
|
{
|
|
if ( pc_fake_controller.GetBool( ) && !m_bXController )
|
|
{
|
|
// [dkorus] we're simulating fake controller input and we don't have a controller enabled. Fake a controller so we can accept controller presses.
|
|
// this fixes the PC so it can use the same scripting engine as the other setups
|
|
// NOTE: This is wrapped in a !_CERT block. This shouldn't end up in the shipped game.
|
|
m_bXController = true;
|
|
}
|
|
|
|
if ( args.ArgC() < 2 )
|
|
{
|
|
Warning( "press_x360_button: requires a key to send (lt, rt, st[art], ba[ck], lb, rb, a, b, x, y, l[eft], r[right], u[p], d[own])" );
|
|
return;
|
|
}
|
|
|
|
const char* pKey = args[1];
|
|
|
|
// We're stashing this in a bitmask so make sure we don't overflow it
|
|
//COMPILE_TIME_ASSERT( cNum_press_x360_button_codes < sizeof( m_press_x360_buttons[ 0 ] ) );
|
|
|
|
xKey_t key = XK_BUTTON_A;
|
|
for ( uint32 i = 0; i < cNum_press_x360_button_codes; i++ )
|
|
{
|
|
if ( pKey[0] == press_x360_button_codes[i].c1 && ( pKey[1] == press_x360_button_codes[i].c2 || press_x360_button_codes[i].c2 == 0 ) )
|
|
{
|
|
key = press_x360_button_codes[i].key;
|
|
m_press_x360_buttons[ 0 ] = m_press_x360_buttons[ 0 ] | (1 << i );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CInputSystem::PollPressX360Button( void )
|
|
{
|
|
uint32 pressedButtons = m_press_x360_buttons[ 0 ];
|
|
uint32 releasedButtons = m_press_x360_buttons[ 1 ];
|
|
|
|
// Reset the buttons we pressed this frame
|
|
m_press_x360_buttons[ 0 ] = 0;
|
|
|
|
// Store the buttons we pressed this frame so we can clear them next frame
|
|
m_press_x360_buttons[ 1 ] = pressedButtons;
|
|
|
|
// Clear any old button presses and press any new ones
|
|
for ( uint32 i = 0; i < cNum_press_x360_button_codes; i++ )
|
|
{
|
|
uint32 mask = 1 << i;
|
|
if ( releasedButtons & mask )
|
|
{
|
|
PostXKeyEvent( 0, press_x360_button_codes[i].key, 0 );
|
|
}
|
|
|
|
if ( pressedButtons & mask )
|
|
{
|
|
PostXKeyEvent( 0, press_x360_button_codes[i].key, 32768/*XBX_MAX_BUTTONSAMPLE*/ );
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // !_CERT
|
|
|
|
#endif
|