//========= 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(); } }