From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- inputsystem/inputsystem.cpp | 1550 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1550 insertions(+) create mode 100644 inputsystem/inputsystem.cpp (limited to 'inputsystem/inputsystem.cpp') diff --git a/inputsystem/inputsystem.cpp b/inputsystem/inputsystem.cpp new file mode 100644 index 0000000..5f11ac7 --- /dev/null +++ b/inputsystem/inputsystem.cpp @@ -0,0 +1,1550 @@ +//========= Copyright 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 "tier0/icommandline.h" + +#if defined( USE_SDL ) +#undef M_PI +#include "SDL.h" +static void initKeymap(void); +#endif + +#ifdef _X360 +#include "xbox/xbox_win32stubs.h" +#endif +ConVar joy_xcontroller_found( "joy_xcontroller_found", "1", FCVAR_HIDDEN, "Automatically set to 1 if an xcontroller has been detected." ); + +//----------------------------------------------------------------------------- +// Singleton instance +//----------------------------------------------------------------------------- +static CInputSystem g_InputSystem; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CInputSystem, IInputSystem, + INPUTSYSTEM_INTERFACE_VERSION, g_InputSystem ); + + + +#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 + + + +//----------------------------------------------------------------------------- +// 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_JoysticksEnabled.ClearAllFlags(); + m_nJoystickCount = 0; + m_bJoystickInitialized = false; + m_nPollCount = 0; + m_PrimaryUserId = INVALID_USER_ID; + m_uiMouseWheel = 0; + m_bXController = false; + m_bRawInputSupported = false; + m_bSteamController = false; + m_bSteamControllerActionsInitialized = false; + m_bSteamControllerActive = false; + + Assert( (MAX_JOYSTICKS + 7) >> 3 << sizeof(unsigned short) ); + + m_pXInputDLL = NULL; + m_pRawInputDLL = NULL; + +#if defined ( _WIN32 ) && !defined ( _X360 ) + // NVNT DLL + m_pNovintDLL = NULL; +#endif + + m_bConsoleTextMode = false; + m_bSkipControllerInitialization = false; + + if ( CommandLine()->CheckParm( "-nosteamcontroller" ) ) + { + m_bSkipControllerInitialization = true; + } +} + +CInputSystem::~CInputSystem() +{ + if ( m_pXInputDLL ) + { + Sys_UnloadModule( m_pXInputDLL ); + m_pXInputDLL = NULL; + } + + if ( m_pRawInputDLL ) + { + Sys_UnloadModule( m_pRawInputDLL ); + m_pRawInputDLL = NULL; + } + +#if defined ( _WIN32 ) && !defined ( _X360 ) + // NVNT DLL unload + if ( m_pNovintDLL ) + { + Sys_UnloadModule( m_pNovintDLL ); + m_pNovintDLL = NULL; + } +#endif +} + + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +InitReturnVal_t CInputSystem::Init() +{ + InitReturnVal_t nRetVal = BaseClass::Init(); + if ( nRetVal != INIT_OK ) + return nRetVal; + + m_StartupTimeTick = Plat_MSTime(); + + +#if !defined( POSIX ) + if ( IsPC() ) + { + m_uiMouseWheel = RegisterWindowMessage( "MSWHEEL_ROLLMSG" ); + } + + m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + if ( !m_hEvent ) + return INIT_FAILED; +#endif + + // Initialize the input system copy of the steam API context, for use by controller stuff (don't do this if we're a dedicated server). + if ( !m_bSkipControllerInitialization && SteamAPI_InitSafe() ) + { + m_SteamAPIContext.Init(); + if ( m_SteamAPIContext.SteamController() ) + { + m_SteamAPIContext.SteamController()->Init(); + m_bSteamController = InitializeSteamControllers(); + m_bSteamControllerActionsInitialized = m_bSteamController && InitializeSteamControllerActionSets(); + if ( m_bSteamControllerActionsInitialized ) + { + ActivateSteamControllerActionSet( GAME_ACTION_SET_MENUCONTROLS ); + } + } + } + + ButtonCode_InitKeyTranslationTable(); + ButtonCode_UpdateScanCodeLayout(); + + joy_xcontroller_found.SetValue( 0 ); + if ( IsPC() && !m_bConsoleTextMode ) + { + InitializeJoysticks(); + if ( m_bXController ) + joy_xcontroller_found.SetValue( 1 ); + + +#if defined( PLATFORM_WINDOWS_PC ) + // NVNT try and load and initialize through the haptic dll, but only if the drivers are installed + HMODULE hdl = LoadLibraryEx( "hdl.dll", NULL, LOAD_LIBRARY_AS_DATAFILE ); + + if ( hdl ) + { + m_pNovintDLL = Sys_LoadModule( "haptics.dll" ); + if ( m_pNovintDLL ) + { + InitializeNovintDevices(); + } + FreeLibrary( hdl ); + } +#endif + } + +#if defined( _X360 ) + SetPrimaryUserId( XBX_GetPrimaryUserId() ); + InitializeXDevices(); + m_bXController = true; +#endif + +#if defined( USE_SDL ) + + m_bRawInputSupported = true; + initKeymap(); + +#elif defined( WIN32 ) && !defined( _X360 ) + + // Check if this version of windows supports raw mouse input (later than win2k) + m_bRawInputSupported = false; + + 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 + + return INIT_OK; +} + +bool CInputSystem::Connect( CreateInterfaceFn factory ) +{ + if ( !BaseClass::Connect( factory ) ) + return false; + +#if defined( USE_SDL ) + m_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL ); +#endif + + return true; +} + + +//----------------------------------------------------------------------------- +// Shutdown +//----------------------------------------------------------------------------- +void CInputSystem::Shutdown() +{ +#if !defined( POSIX ) + if ( m_hEvent != NULL ) + { + CloseHandle( m_hEvent ); + m_hEvent = NULL; + } +#endif + + if ( IsPC() ) + { + ShutdownJoysticks(); + } + + BaseClass::Shutdown(); +} + + +//----------------------------------------------------------------------------- +// Sleep until input +//----------------------------------------------------------------------------- +void CInputSystem::SleepUntilInput( int nMaxSleepTimeMS ) +{ +#if defined( _WIN32 ) && !defined( USE_SDL ) + if ( nMaxSleepTimeMS < 0 ) + { + nMaxSleepTimeMS = INFINITE; + } + + MsgWaitForMultipleObjects( 1, &m_hEvent, FALSE, nMaxSleepTimeMS, QS_ALLEVENTS ); +#elif defined( USE_SDL ) + m_pLauncherMgr->WaitUntilUserInput( nMaxSleepTimeMS ); +#else +#warning "need a SleepUntilInput impl" +#endif +} + + + +//----------------------------------------------------------------------------- +// Callback to call into our class +//----------------------------------------------------------------------------- +#if defined( PLATFORM_WINDOWS ) +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( PLATFORM_WINDOWS ) + m_ChainedWndProc = (WNDPROC)GetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC ); + SetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC, (LONG_PTR)InputSystemWindowProc ); +#endif + + m_hAttachedHWnd = (HWND)hWnd; + +#if defined( PLATFORM_WINDOWS_PC ) && !defined( USE_SDL ) + // NVNT inform novint devices of window + AttachWindowToNovintDevices( hWnd ); + + // 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(); +} + + +//----------------------------------------------------------------------------- +// Unhooks input listening from a window +//----------------------------------------------------------------------------- +void CInputSystem::DetachFromWindow( ) +{ + if ( !m_hAttachedHWnd ) + return; + + ResetInputState(); + +#if defined( PLATFORM_WINDOWS ) + if ( m_ChainedWndProc ) + { + SetWindowLongPtrW( m_hAttachedHWnd, GWLP_WNDPROC, (LONG_PTR)m_ChainedWndProc ); + m_ChainedWndProc = 0; + } +#endif + +#if defined( PLATFORM_WINDOWS_PC ) + // NVNT inform novint devices loss of window + DetachWindowFromNovintDevices( ); +#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() +{ + 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) ); + state.m_Events.Purge(); + state.m_bDirty = false; + } + memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) ); +} + +//----------------------------------------------------------------------------- +// Resets the input state +//----------------------------------------------------------------------------- +void CInputSystem::ResetInputState() +{ + ReleaseAllButtons(); + ZeroAnalogState( 0, ANALOG_CODE_LAST - 1 ); + memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) ); + + m_mouseRawAccumX = m_mouseRawAccumY = 0; +} + + +//----------------------------------------------------------------------------- +// 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, m_bXController ); +} + +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 ) +{ + InputEvent_t event; + + memset( &event, 0, sizeof(event) ); + event.m_nType = nType; + event.m_nTick = nTick; + event.m_nData = nData; + event.m_nData2 = nData2; + event.m_nData3 = nData3; + + PostUserEvent( event ); +} + + +//----------------------------------------------------------------------------- +// 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 defined( _X360 ) + // FIXME: Remove! Fake a windows message for vguimatsurface's input handler + if ( IsJoystickCode( scanCode ) ) + { + ProcessEvent( WM_XCONTROLLER_KEY, scanCode, 1 ); + } +#endif + } +} + + +//----------------------------------------------------------------------------- +// 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 ); + +#if defined( _X360 ) + // FIXME: Remove! Fake a windows message for vguimatsurface's input handler + if ( IsJoystickCode( scanCode ) ) + { + ProcessEvent( WM_XCONTROLLER_KEY, scanCode, 0 ); + } +#endif + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Pass Joystick button events through the engine's window procs +//----------------------------------------------------------------------------- +void CInputSystem::ProcessEvent( UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ +#if !defined( 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( PLATFORM_WINDOWS_PC ) +void CInputSystem::PollInputState_Windows() +{ + if ( 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; + } + + TranslateMessage( &msg ); + DispatchMessage( &msg ); + } + + // NOTE: Under some implementations of Win9x, + // dispatching messages can cause the FPU control word to change + SetupFPUControlWord(); + } +} +#endif + +#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; +} + +bool MapCocoaVirtualKeyToButtonCode( int nCocoaVirtualKeyCode, ButtonCode_t *pOut ) +{ + if ( nCocoaVirtualKeyCode < 0 ) + *pOut = (ButtonCode_t)(-1 * nCocoaVirtualKeyCode); + else + { + nCocoaVirtualKeyCode &= 0x000000ff; + + *pOut = (ButtonCode_t)scantokey[nCocoaVirtualKeyCode]; + } + + return true; +} + +void CInputSystem::PollInputState_Platform() +{ + 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; + + if( ( scanCode != BUTTON_CODE_NONE ) ) + { + // 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(); + // IE_KeyCodeTyped + event.m_nType = IE_FirstVguiEvent + 4; + event.m_nData = scanCode; + g_pInputSystem->PostUserEvent( event ); + + } + + if ( !(pEvent->m_ModifierKeyMask & (1<m_VirtualKeyCode >= 0 && pEvent->m_UnicodeKey > 0 ) + { + InputEvent_t event; + memset( &event, 0, sizeof(event) ); + event.m_nTick = GetPollTick(); + // IE_KeyTyped + event.m_nType = IE_FirstVguiEvent + 3; + event.m_nData = (int)pEvent->m_UnicodeKey; + g_pInputSystem->PostUserEvent( event ); + } + + } + break; + + case CocoaEvent_KeyUp: + { + ButtonCode_t virtualCode; + if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) ) + { + if( virtualCode != BUTTON_CODE_NONE ) + { + 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(); + // IE_LocateMouseClick + event.m_nType = IE_FirstVguiEvent + 1; + 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 ); + + state.m_pAnalogDelta[ MOUSE_WHEEL ] = pEvent->m_MousePos[1]; + 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 + 2; // IE_AppActivated (defined in sys_mainwind.cpp). + event.m_nData = (bool)(pEvent->m_ModifierKeyMask != 0); + + g_pInputSystem->PostUserEvent( event ); + + if( pEvent->m_ModifierKeyMask == 0 ) + { + // App just lost focus. Handle like WM_ACTIVATEAPP in CInputSystem::WindowProc(). + // Otherwise alt+tab will bring focus away from our app, vgui will still think that + // the alt key is down, and when we regain focus, fun ensues. + g_pInputSystem->ResetInputState(); + } + } + break; + case CocoaEvent_AppQuit: + { + PostEvent( IE_Quit, m_nLastSampleTick ); + + } + break; + break; + } + } + } +} +#endif // USE_SDL + + +//----------------------------------------------------------------------------- +// Polls the current input state +//----------------------------------------------------------------------------- +void CInputSystem::PollInputState() +{ + m_bIsPolling = true; + ++m_nPollCount; + + // 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_WINDOWS_PC ) + PollInputState_Windows(); +#endif + +#if defined( USE_SDL ) + PollInputState_Platform(); +#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(); + + PollJoystick(); + +#if defined( PLATFORM_WINDOWS_PC ) + // NVNT if we have device/s poll them. + if ( m_bNovintDevices ) + { + PollNovintDevices(); + } +#endif + + PollSteamControllers(); +} + +//----------------------------------------------------------------------------- +// Purpose: Sets a player as the primary user - all other controllers will be ignored. +//----------------------------------------------------------------------------- +void CInputSystem::SetPrimaryUserId( int userId ) +{ + if ( userId >= XUSER_MAX_COUNT || userId < 0 ) + { + m_PrimaryUserId = INVALID_USER_ID; + } + else + { + m_PrimaryUserId = userId; + } +#if !defined(POSIX) + XBX_SetPrimaryUserId( m_PrimaryUserId ); +#endif + ConMsg("PrimaryUserId is %d\n", m_PrimaryUserId ); +} + +//----------------------------------------------------------------------------- +// Purpose: Forwards rumble info to attached devices +//----------------------------------------------------------------------------- +void CInputSystem::SetRumble( float fLeftMotor, float fRightMotor, int userId ) +{ + SetXDeviceRumble( fLeftMotor, fRightMotor, userId ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Force an immediate stop, transmits immediately to all devices +//----------------------------------------------------------------------------- +void CInputSystem::StopRumble( void ) +{ +#ifdef _X360 + 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 + for ( int i = 0; i < XUSER_MAX_COUNT; ++i ) + { + SetRumble(0.0, 0.0, i); + } +#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]; +} + + +//----------------------------------------------------------------------------- +// 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( 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( PLATFORM_WINDOWS ) + 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( PLATFORM_WINDOWS ) + POINT pt; + pt.x = x; pt.y = y; + ClientToScreen( (HWND)m_hAttachedHWnd, &pt ); + SetCursorPos( pt.x, pt.y ); +#elif defined( USE_SDL ) + m_pLauncherMgr->SetCursorPosition( x, y ); +#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::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 ] ); + } +} + + +//----------------------------------------------------------------------------- +// Handles input messages +//----------------------------------------------------------------------------- +LRESULT CInputSystem::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ +#if defined( PLATFORM_WINDOWS ) // We use this even for SDL to handle mouse move. + if ( !m_bEnabled ) + return ChainWindowMessage( hwnd, uMsg, wParam, lParam ); + + if ( hwnd != m_hAttachedHWnd ) + return ChainWindowMessage( hwnd, uMsg, wParam, lParam ); + + InputState_t &state = m_InputState[ m_bIsPolling ]; + switch( uMsg ) + { + +#if !defined( USE_SDL ) + case WM_ACTIVATEAPP: + if ( hwnd == m_hAttachedHWnd ) + { + bool bActivated = ( wParam == 1 ); + if ( !bActivated ) + { + ResetInputState(); + } + } + break; + + case WM_LBUTTONDOWN: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true ); + ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + case WM_LBUTTONUP: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, false ); + ETWMouseUp( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + case WM_RBUTTONDOWN: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true ); + ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + case WM_RBUTTONUP: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, false ); + ETWMouseUp( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + case WM_MBUTTONDOWN: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true ); + ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + case WM_MBUTTONUP: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, false ); + ETWMouseUp( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + case MS_WM_XBUTTONDOWN: + { + 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: + { + 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: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true ); + ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask, MOUSE_LEFT ); + } + break; + + case WM_RBUTTONDBLCLK: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true ); + ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask, MOUSE_RIGHT ); + } + break; + + case WM_MBUTTONDBLCLK: + { + int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true ); + ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + UpdateMouseButtonState( nButtonMask, MOUSE_MIDDLE ); + } + break; + + case MS_WM_XBUTTONDBLCLK: + { + 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 ); + } + } + } + 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; + +#if defined( PLATFORM_WINDOWS_PC ) + case WM_INPUT: + { + if ( m_bRawInputSupported ) + { + UINT dwSize = 40; + static BYTE lpb[40]; + + 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 + +#endif // !USE_SDL + + case WM_MOUSEMOVE: + { + UpdateMousePositionState( state, (short)LOWORD(lParam), (short)HIWORD(lParam) ); + + int nButtonMask = ButtonMaskFromMouseWParam( wParam ); + UpdateMouseButtonState( nButtonMask ); + } + break; + + } + +#if defined( PLATFORM_WINDOWS_PC ) && !defined( USE_SDL ) + // Can't put this in the case statement, it's not constant + if ( 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 ] ); + } +#endif + +#endif // PLATFORM_WINDOWS + return ChainWindowMessage( hwnd, uMsg, wParam, lParam ); +} + +bool CInputSystem::GetRawMouseAccumulators( int& accumX, int& accumY ) +{ +#if defined( USE_SDL ) + + if ( m_pLauncherMgr ) + { + m_pLauncherMgr->GetMouseDelta( accumX, accumY, false ); + return true; + } + return false; + +#else + + accumX = m_mouseRawAccumX; + accumY = m_mouseRawAccumY; + m_mouseRawAccumX = m_mouseRawAccumY = 0; + return m_bRawInputSupported; + +#endif +} + +void CInputSystem::SetConsoleTextMode( bool bConsoleTextMode ) +{ + /* If someone calls this after init, shut it down. */ + if ( bConsoleTextMode && m_bJoystickInitialized ) + { + ShutdownJoysticks(); + } + + m_bConsoleTextMode = bConsoleTextMode; +} + +ISteamController* CInputSystem::SteamControllerInterface() +{ + if ( m_bSkipControllerInitialization ) + { + return nullptr; + } + else + { + return m_SteamAPIContext.SteamController(); + } +} -- cgit v1.2.3