Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1550 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "inputsystem.h"
  7. #include "key_translation.h"
  8. #include "inputsystem/ButtonCode.h"
  9. #include "inputsystem/AnalogCode.h"
  10. #include "tier0/etwprof.h"
  11. #include "tier1/convar.h"
  12. #include "tier0/icommandline.h"
  13. #if defined( USE_SDL )
  14. #undef M_PI
  15. #include "SDL.h"
  16. static void initKeymap(void);
  17. #endif
  18. #ifdef _X360
  19. #include "xbox/xbox_win32stubs.h"
  20. #endif
  21. ConVar joy_xcontroller_found( "joy_xcontroller_found", "1", FCVAR_HIDDEN, "Automatically set to 1 if an xcontroller has been detected." );
  22. //-----------------------------------------------------------------------------
  23. // Singleton instance
  24. //-----------------------------------------------------------------------------
  25. static CInputSystem g_InputSystem;
  26. EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CInputSystem, IInputSystem,
  27. INPUTSYSTEM_INTERFACE_VERSION, g_InputSystem );
  28. #if defined( WIN32 ) && !defined( _X360 )
  29. typedef BOOL (WINAPI *RegisterRawInputDevices_t)
  30. (
  31. PCRAWINPUTDEVICE pRawInputDevices,
  32. UINT uiNumDevices,
  33. UINT cbSize
  34. );
  35. typedef UINT (WINAPI *GetRawInputData_t)
  36. (
  37. HRAWINPUT hRawInput,
  38. UINT uiCommand,
  39. LPVOID pData,
  40. PUINT pcbSize,
  41. UINT cbSizeHeader
  42. );
  43. RegisterRawInputDevices_t pfnRegisterRawInputDevices;
  44. GetRawInputData_t pfnGetRawInputData;
  45. #endif
  46. //-----------------------------------------------------------------------------
  47. // Constructor, destructor
  48. //-----------------------------------------------------------------------------
  49. CInputSystem::CInputSystem()
  50. {
  51. m_nLastPollTick = m_nLastSampleTick = m_StartupTimeTick = 0;
  52. m_ChainedWndProc = 0;
  53. m_hAttachedHWnd = 0;
  54. m_hEvent = NULL;
  55. m_bEnabled = true;
  56. m_bPumpEnabled = true;
  57. m_bIsPolling = false;
  58. m_JoysticksEnabled.ClearAllFlags();
  59. m_nJoystickCount = 0;
  60. m_bJoystickInitialized = false;
  61. m_nPollCount = 0;
  62. m_PrimaryUserId = INVALID_USER_ID;
  63. m_uiMouseWheel = 0;
  64. m_bXController = false;
  65. m_bRawInputSupported = false;
  66. m_bSteamController = false;
  67. m_bSteamControllerActionsInitialized = false;
  68. m_bSteamControllerActive = false;
  69. Assert( (MAX_JOYSTICKS + 7) >> 3 << sizeof(unsigned short) );
  70. m_pXInputDLL = NULL;
  71. m_pRawInputDLL = NULL;
  72. #if defined ( _WIN32 ) && !defined ( _X360 )
  73. // NVNT DLL
  74. m_pNovintDLL = NULL;
  75. #endif
  76. m_bConsoleTextMode = false;
  77. m_bSkipControllerInitialization = false;
  78. if ( CommandLine()->CheckParm( "-nosteamcontroller" ) )
  79. {
  80. m_bSkipControllerInitialization = true;
  81. }
  82. }
  83. CInputSystem::~CInputSystem()
  84. {
  85. if ( m_pXInputDLL )
  86. {
  87. Sys_UnloadModule( m_pXInputDLL );
  88. m_pXInputDLL = NULL;
  89. }
  90. if ( m_pRawInputDLL )
  91. {
  92. Sys_UnloadModule( m_pRawInputDLL );
  93. m_pRawInputDLL = NULL;
  94. }
  95. #if defined ( _WIN32 ) && !defined ( _X360 )
  96. // NVNT DLL unload
  97. if ( m_pNovintDLL )
  98. {
  99. Sys_UnloadModule( m_pNovintDLL );
  100. m_pNovintDLL = NULL;
  101. }
  102. #endif
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Initialization
  106. //-----------------------------------------------------------------------------
  107. InitReturnVal_t CInputSystem::Init()
  108. {
  109. InitReturnVal_t nRetVal = BaseClass::Init();
  110. if ( nRetVal != INIT_OK )
  111. return nRetVal;
  112. m_StartupTimeTick = Plat_MSTime();
  113. #if !defined( POSIX )
  114. if ( IsPC() )
  115. {
  116. m_uiMouseWheel = RegisterWindowMessage( "MSWHEEL_ROLLMSG" );
  117. }
  118. m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  119. if ( !m_hEvent )
  120. return INIT_FAILED;
  121. #endif
  122. // 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).
  123. if ( !m_bSkipControllerInitialization && SteamAPI_InitSafe() )
  124. {
  125. m_SteamAPIContext.Init();
  126. if ( m_SteamAPIContext.SteamController() )
  127. {
  128. m_SteamAPIContext.SteamController()->Init();
  129. m_bSteamController = InitializeSteamControllers();
  130. m_bSteamControllerActionsInitialized = m_bSteamController && InitializeSteamControllerActionSets();
  131. if ( m_bSteamControllerActionsInitialized )
  132. {
  133. ActivateSteamControllerActionSet( GAME_ACTION_SET_MENUCONTROLS );
  134. }
  135. }
  136. }
  137. ButtonCode_InitKeyTranslationTable();
  138. ButtonCode_UpdateScanCodeLayout();
  139. joy_xcontroller_found.SetValue( 0 );
  140. if ( IsPC() && !m_bConsoleTextMode )
  141. {
  142. InitializeJoysticks();
  143. if ( m_bXController )
  144. joy_xcontroller_found.SetValue( 1 );
  145. #if defined( PLATFORM_WINDOWS_PC )
  146. // NVNT try and load and initialize through the haptic dll, but only if the drivers are installed
  147. HMODULE hdl = LoadLibraryEx( "hdl.dll", NULL, LOAD_LIBRARY_AS_DATAFILE );
  148. if ( hdl )
  149. {
  150. m_pNovintDLL = Sys_LoadModule( "haptics.dll" );
  151. if ( m_pNovintDLL )
  152. {
  153. InitializeNovintDevices();
  154. }
  155. FreeLibrary( hdl );
  156. }
  157. #endif
  158. }
  159. #if defined( _X360 )
  160. SetPrimaryUserId( XBX_GetPrimaryUserId() );
  161. InitializeXDevices();
  162. m_bXController = true;
  163. #endif
  164. #if defined( USE_SDL )
  165. m_bRawInputSupported = true;
  166. initKeymap();
  167. #elif defined( WIN32 ) && !defined( _X360 )
  168. // Check if this version of windows supports raw mouse input (later than win2k)
  169. m_bRawInputSupported = false;
  170. CSysModule *m_pRawInputDLL = Sys_LoadModule( "USER32.dll" );
  171. if ( m_pRawInputDLL )
  172. {
  173. pfnRegisterRawInputDevices = (RegisterRawInputDevices_t)GetProcAddress( (HMODULE)m_pRawInputDLL, "RegisterRawInputDevices" );
  174. pfnGetRawInputData = (GetRawInputData_t)GetProcAddress( (HMODULE)m_pRawInputDLL, "GetRawInputData" );
  175. if ( pfnRegisterRawInputDevices && pfnGetRawInputData )
  176. m_bRawInputSupported = true;
  177. }
  178. #endif
  179. return INIT_OK;
  180. }
  181. bool CInputSystem::Connect( CreateInterfaceFn factory )
  182. {
  183. if ( !BaseClass::Connect( factory ) )
  184. return false;
  185. #if defined( USE_SDL )
  186. m_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL );
  187. #endif
  188. return true;
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Shutdown
  192. //-----------------------------------------------------------------------------
  193. void CInputSystem::Shutdown()
  194. {
  195. #if !defined( POSIX )
  196. if ( m_hEvent != NULL )
  197. {
  198. CloseHandle( m_hEvent );
  199. m_hEvent = NULL;
  200. }
  201. #endif
  202. if ( IsPC() )
  203. {
  204. ShutdownJoysticks();
  205. }
  206. BaseClass::Shutdown();
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Sleep until input
  210. //-----------------------------------------------------------------------------
  211. void CInputSystem::SleepUntilInput( int nMaxSleepTimeMS )
  212. {
  213. #if defined( _WIN32 ) && !defined( USE_SDL )
  214. if ( nMaxSleepTimeMS < 0 )
  215. {
  216. nMaxSleepTimeMS = INFINITE;
  217. }
  218. MsgWaitForMultipleObjects( 1, &m_hEvent, FALSE, nMaxSleepTimeMS, QS_ALLEVENTS );
  219. #elif defined( USE_SDL )
  220. m_pLauncherMgr->WaitUntilUserInput( nMaxSleepTimeMS );
  221. #else
  222. #warning "need a SleepUntilInput impl"
  223. #endif
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Callback to call into our class
  227. //-----------------------------------------------------------------------------
  228. #if defined( PLATFORM_WINDOWS )
  229. static LRESULT CALLBACK InputSystemWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  230. {
  231. return g_InputSystem.WindowProc( hwnd, uMsg, wParam, lParam );
  232. }
  233. #endif
  234. //-----------------------------------------------------------------------------
  235. // Hooks input listening up to a window
  236. //-----------------------------------------------------------------------------
  237. void CInputSystem::AttachToWindow( void* hWnd )
  238. {
  239. Assert( m_hAttachedHWnd == 0 );
  240. if ( m_hAttachedHWnd )
  241. {
  242. Warning( "CInputSystem::AttachToWindow: Cannot attach to two windows at once!\n" );
  243. return;
  244. }
  245. #if defined( PLATFORM_WINDOWS )
  246. m_ChainedWndProc = (WNDPROC)GetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC );
  247. SetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC, (LONG_PTR)InputSystemWindowProc );
  248. #endif
  249. m_hAttachedHWnd = (HWND)hWnd;
  250. #if defined( PLATFORM_WINDOWS_PC ) && !defined( USE_SDL )
  251. // NVNT inform novint devices of window
  252. AttachWindowToNovintDevices( hWnd );
  253. // register to read raw mouse input
  254. #if !defined(HID_USAGE_PAGE_GENERIC)
  255. #define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
  256. #endif
  257. #if !defined(HID_USAGE_GENERIC_MOUSE)
  258. #define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
  259. #endif
  260. if ( m_bRawInputSupported )
  261. {
  262. RAWINPUTDEVICE Rid[1];
  263. Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
  264. Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
  265. Rid[0].dwFlags = RIDEV_INPUTSINK;
  266. Rid[0].hwndTarget = g_InputSystem.m_hAttachedHWnd; // GetHhWnd;
  267. pfnRegisterRawInputDevices(Rid, ARRAYSIZE(Rid), sizeof(Rid[0]));
  268. }
  269. #endif
  270. // New window, clear input state
  271. ClearInputState();
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Unhooks input listening from a window
  275. //-----------------------------------------------------------------------------
  276. void CInputSystem::DetachFromWindow( )
  277. {
  278. if ( !m_hAttachedHWnd )
  279. return;
  280. ResetInputState();
  281. #if defined( PLATFORM_WINDOWS )
  282. if ( m_ChainedWndProc )
  283. {
  284. SetWindowLongPtrW( m_hAttachedHWnd, GWLP_WNDPROC, (LONG_PTR)m_ChainedWndProc );
  285. m_ChainedWndProc = 0;
  286. }
  287. #endif
  288. #if defined( PLATFORM_WINDOWS_PC )
  289. // NVNT inform novint devices loss of window
  290. DetachWindowFromNovintDevices( );
  291. #endif
  292. m_hAttachedHWnd = 0;
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Enables/disables input
  296. //-----------------------------------------------------------------------------
  297. void CInputSystem::EnableInput( bool bEnable )
  298. {
  299. m_bEnabled = bEnable;
  300. }
  301. //-----------------------------------------------------------------------------
  302. // Enables/disables the inputsystem windows message pump
  303. //-----------------------------------------------------------------------------
  304. void CInputSystem::EnableMessagePump( bool bEnable )
  305. {
  306. m_bPumpEnabled = bEnable;
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Clears the input state, doesn't generate key-up messages
  310. //-----------------------------------------------------------------------------
  311. void CInputSystem::ClearInputState()
  312. {
  313. for ( int i = 0; i < INPUT_STATE_COUNT; ++i )
  314. {
  315. InputState_t& state = m_InputState[i];
  316. state.m_ButtonState.ClearAll();
  317. memset( state.m_pAnalogDelta, 0, ANALOG_CODE_LAST * sizeof(int) );
  318. memset( state.m_pAnalogValue, 0, ANALOG_CODE_LAST * sizeof(int) );
  319. memset( state.m_ButtonPressedTick, 0, BUTTON_CODE_LAST * sizeof(int) );
  320. memset( state.m_ButtonReleasedTick, 0, BUTTON_CODE_LAST * sizeof(int) );
  321. state.m_Events.Purge();
  322. state.m_bDirty = false;
  323. }
  324. memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) );
  325. }
  326. //-----------------------------------------------------------------------------
  327. // Resets the input state
  328. //-----------------------------------------------------------------------------
  329. void CInputSystem::ResetInputState()
  330. {
  331. ReleaseAllButtons();
  332. ZeroAnalogState( 0, ANALOG_CODE_LAST - 1 );
  333. memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) );
  334. m_mouseRawAccumX = m_mouseRawAccumY = 0;
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Convert back + forth between ButtonCode/AnalogCode + strings
  338. //-----------------------------------------------------------------------------
  339. const char *CInputSystem::ButtonCodeToString( ButtonCode_t code ) const
  340. {
  341. return ButtonCode_ButtonCodeToString( code, m_bXController );
  342. }
  343. const char *CInputSystem::AnalogCodeToString( AnalogCode_t code ) const
  344. {
  345. return AnalogCode_AnalogCodeToString( code );
  346. }
  347. ButtonCode_t CInputSystem::StringToButtonCode( const char *pString ) const
  348. {
  349. return ButtonCode_StringToButtonCode( pString, m_bXController );
  350. }
  351. AnalogCode_t CInputSystem::StringToAnalogCode( const char *pString ) const
  352. {
  353. return AnalogCode_StringToAnalogCode( pString );
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Convert back + forth between virtual codes + button codes
  357. // FIXME: This is a temporary piece of code
  358. //-----------------------------------------------------------------------------
  359. ButtonCode_t CInputSystem::VirtualKeyToButtonCode( int nVirtualKey ) const
  360. {
  361. return ButtonCode_VirtualKeyToButtonCode( nVirtualKey );
  362. }
  363. int CInputSystem::ButtonCodeToVirtualKey( ButtonCode_t code ) const
  364. {
  365. return ButtonCode_ButtonCodeToVirtualKey( code );
  366. }
  367. ButtonCode_t CInputSystem::XKeyToButtonCode( int nPort, int nXKey ) const
  368. {
  369. if ( m_bXController )
  370. return ButtonCode_XKeyToButtonCode( nPort, nXKey );
  371. return KEY_NONE;
  372. }
  373. ButtonCode_t CInputSystem::ScanCodeToButtonCode( int lParam ) const
  374. {
  375. return ButtonCode_ScanCodeToButtonCode( lParam );
  376. }
  377. ButtonCode_t CInputSystem::SKeyToButtonCode( int nPort, int nXKey ) const
  378. {
  379. return ButtonCode_SKeyToButtonCode( nPort, nXKey );
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Post an event to the queue
  383. //-----------------------------------------------------------------------------
  384. void CInputSystem::PostEvent( int nType, int nTick, int nData, int nData2, int nData3 )
  385. {
  386. InputEvent_t event;
  387. memset( &event, 0, sizeof(event) );
  388. event.m_nType = nType;
  389. event.m_nTick = nTick;
  390. event.m_nData = nData;
  391. event.m_nData2 = nData2;
  392. event.m_nData3 = nData3;
  393. PostUserEvent( event );
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Post an button press event to the queue
  397. //-----------------------------------------------------------------------------
  398. void CInputSystem::PostButtonPressedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode )
  399. {
  400. InputState_t &state = m_InputState[ m_bIsPolling ];
  401. if ( !state.m_ButtonState.IsBitSet( scanCode ) )
  402. {
  403. // Update button state
  404. state.m_ButtonState.Set( scanCode );
  405. state.m_ButtonPressedTick[ scanCode ] = nTick;
  406. // Add this event to the app-visible event queue
  407. PostEvent( nType, nTick, scanCode, virtualCode );
  408. #if defined( _X360 )
  409. // FIXME: Remove! Fake a windows message for vguimatsurface's input handler
  410. if ( IsJoystickCode( scanCode ) )
  411. {
  412. ProcessEvent( WM_XCONTROLLER_KEY, scanCode, 1 );
  413. }
  414. #endif
  415. }
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Post an button release event to the queue
  419. //-----------------------------------------------------------------------------
  420. void CInputSystem::PostButtonReleasedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode )
  421. {
  422. InputState_t &state = m_InputState[ m_bIsPolling ];
  423. if ( state.m_ButtonState.IsBitSet( scanCode ) )
  424. {
  425. // Update button state
  426. state.m_ButtonState.Clear( scanCode );
  427. state.m_ButtonReleasedTick[ scanCode ] = nTick;
  428. // Add this event to the app-visible event queue
  429. PostEvent( nType, nTick, scanCode, virtualCode );
  430. #if defined( _X360 )
  431. // FIXME: Remove! Fake a windows message for vguimatsurface's input handler
  432. if ( IsJoystickCode( scanCode ) )
  433. {
  434. ProcessEvent( WM_XCONTROLLER_KEY, scanCode, 0 );
  435. }
  436. #endif
  437. }
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Purpose: Pass Joystick button events through the engine's window procs
  441. //-----------------------------------------------------------------------------
  442. void CInputSystem::ProcessEvent( UINT uMsg, WPARAM wParam, LPARAM lParam )
  443. {
  444. #if !defined( POSIX )
  445. // To prevent subtle input timing bugs, all button events must be fed
  446. // through the window proc once per frame, same as the keyboard and mouse.
  447. HWND hWnd = GetFocus();
  448. WNDPROC windowProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_WNDPROC );
  449. if ( windowProc )
  450. {
  451. windowProc( hWnd, uMsg, wParam, lParam );
  452. }
  453. #endif
  454. }
  455. //-----------------------------------------------------------------------------
  456. // Copies the input state record over
  457. //-----------------------------------------------------------------------------
  458. void CInputSystem::CopyInputState( InputState_t *pDest, const InputState_t &src, bool bCopyEvents )
  459. {
  460. pDest->m_Events.RemoveAll();
  461. pDest->m_bDirty = false;
  462. if ( src.m_bDirty )
  463. {
  464. pDest->m_ButtonState = src.m_ButtonState;
  465. memcpy( &pDest->m_ButtonPressedTick, &src.m_ButtonPressedTick, sizeof( pDest->m_ButtonPressedTick ) );
  466. memcpy( &pDest->m_ButtonReleasedTick, &src.m_ButtonReleasedTick, sizeof( pDest->m_ButtonReleasedTick ) );
  467. memcpy( &pDest->m_pAnalogDelta, &src.m_pAnalogDelta, sizeof( pDest->m_pAnalogDelta ) );
  468. memcpy( &pDest->m_pAnalogValue, &src.m_pAnalogValue, sizeof( pDest->m_pAnalogValue ) );
  469. if ( bCopyEvents )
  470. {
  471. if ( src.m_Events.Count() > 0 )
  472. {
  473. pDest->m_Events.EnsureCount( src.m_Events.Count() );
  474. memcpy( pDest->m_Events.Base(), src.m_Events.Base(), src.m_Events.Count() * sizeof(InputEvent_t) );
  475. }
  476. }
  477. }
  478. }
  479. #if defined( PLATFORM_WINDOWS_PC )
  480. void CInputSystem::PollInputState_Windows()
  481. {
  482. if ( m_bPumpEnabled )
  483. {
  484. // Poll mouse + keyboard
  485. MSG msg;
  486. while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
  487. {
  488. if ( msg.message == WM_QUIT )
  489. {
  490. PostEvent( IE_Quit, m_nLastSampleTick );
  491. break;
  492. }
  493. TranslateMessage( &msg );
  494. DispatchMessage( &msg );
  495. }
  496. // NOTE: Under some implementations of Win9x,
  497. // dispatching messages can cause the FPU control word to change
  498. SetupFPUControlWord();
  499. }
  500. }
  501. #endif
  502. #if defined( USE_SDL )
  503. static BYTE scantokey[SDL_NUM_SCANCODES];
  504. static void initKeymap(void)
  505. {
  506. memset(scantokey, '\0', sizeof (scantokey));
  507. for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_Z; i++)
  508. scantokey[i] = KEY_A + (i - SDL_SCANCODE_A);
  509. for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_9; i++)
  510. scantokey[i] = KEY_1 + (i - SDL_SCANCODE_1);
  511. for (int i = SDL_SCANCODE_F1; i <= SDL_SCANCODE_F12; i++)
  512. scantokey[i] = KEY_F1 + (i - SDL_SCANCODE_F1);
  513. for (int i = SDL_SCANCODE_KP_1; i <= SDL_SCANCODE_KP_9; i++)
  514. scantokey[i] = KEY_PAD_1 + (i - SDL_SCANCODE_KP_1);
  515. scantokey[SDL_SCANCODE_0] = KEY_0;
  516. scantokey[SDL_SCANCODE_KP_0] = KEY_PAD_0;
  517. scantokey[SDL_SCANCODE_RETURN] = KEY_ENTER;
  518. scantokey[SDL_SCANCODE_ESCAPE] = KEY_ESCAPE;
  519. scantokey[SDL_SCANCODE_BACKSPACE] = KEY_BACKSPACE;
  520. scantokey[SDL_SCANCODE_TAB] = KEY_TAB;
  521. scantokey[SDL_SCANCODE_SPACE] = KEY_SPACE;
  522. scantokey[SDL_SCANCODE_MINUS] = KEY_MINUS;
  523. scantokey[SDL_SCANCODE_EQUALS] = KEY_EQUAL;
  524. scantokey[SDL_SCANCODE_LEFTBRACKET] = KEY_LBRACKET;
  525. scantokey[SDL_SCANCODE_RIGHTBRACKET] = KEY_RBRACKET;
  526. scantokey[SDL_SCANCODE_BACKSLASH] = KEY_BACKSLASH;
  527. scantokey[SDL_SCANCODE_SEMICOLON] = KEY_SEMICOLON;
  528. scantokey[SDL_SCANCODE_APOSTROPHE] = KEY_APOSTROPHE;
  529. scantokey[SDL_SCANCODE_GRAVE] = KEY_BACKQUOTE;
  530. scantokey[SDL_SCANCODE_COMMA] = KEY_COMMA;
  531. scantokey[SDL_SCANCODE_PERIOD] = KEY_PERIOD;
  532. scantokey[SDL_SCANCODE_SLASH] = KEY_SLASH;
  533. scantokey[SDL_SCANCODE_CAPSLOCK] = KEY_CAPSLOCK;
  534. scantokey[SDL_SCANCODE_SCROLLLOCK] = KEY_SCROLLLOCK;
  535. scantokey[SDL_SCANCODE_INSERT] = KEY_INSERT;
  536. scantokey[SDL_SCANCODE_HOME] = KEY_HOME;
  537. scantokey[SDL_SCANCODE_PAGEUP] = KEY_PAGEUP;
  538. scantokey[SDL_SCANCODE_DELETE] = KEY_DELETE;
  539. scantokey[SDL_SCANCODE_END] = KEY_END;
  540. scantokey[SDL_SCANCODE_PAGEDOWN] = KEY_PAGEDOWN;
  541. scantokey[SDL_SCANCODE_RIGHT] = KEY_RIGHT;
  542. scantokey[SDL_SCANCODE_LEFT] = KEY_LEFT;
  543. scantokey[SDL_SCANCODE_DOWN] = KEY_DOWN;
  544. scantokey[SDL_SCANCODE_UP] = KEY_UP;
  545. scantokey[SDL_SCANCODE_NUMLOCKCLEAR] = KEY_NUMLOCK;
  546. scantokey[SDL_SCANCODE_KP_DIVIDE] = KEY_PAD_DIVIDE;
  547. scantokey[SDL_SCANCODE_KP_MULTIPLY] = KEY_PAD_MULTIPLY;
  548. scantokey[SDL_SCANCODE_KP_MINUS] = KEY_PAD_MINUS;
  549. scantokey[SDL_SCANCODE_KP_PLUS] = KEY_PAD_PLUS;
  550. // Map keybad enter to enter for vgui. This means vgui dialog won't ever see KEY_PAD_ENTER
  551. scantokey[SDL_SCANCODE_KP_ENTER] = KEY_ENTER;
  552. scantokey[SDL_SCANCODE_KP_PERIOD] = KEY_PAD_DECIMAL;
  553. scantokey[SDL_SCANCODE_APPLICATION] = KEY_APP;
  554. scantokey[SDL_SCANCODE_LCTRL] = KEY_LCONTROL;
  555. scantokey[SDL_SCANCODE_LSHIFT] = KEY_LSHIFT;
  556. scantokey[SDL_SCANCODE_LALT] = KEY_LALT;
  557. scantokey[SDL_SCANCODE_LGUI] = KEY_LWIN;
  558. scantokey[SDL_SCANCODE_RCTRL] = KEY_RCONTROL;
  559. scantokey[SDL_SCANCODE_RSHIFT] = KEY_RSHIFT;
  560. scantokey[SDL_SCANCODE_RALT] = KEY_RALT;
  561. scantokey[SDL_SCANCODE_RGUI] = KEY_RWIN;
  562. }
  563. bool MapCocoaVirtualKeyToButtonCode( int nCocoaVirtualKeyCode, ButtonCode_t *pOut )
  564. {
  565. if ( nCocoaVirtualKeyCode < 0 )
  566. *pOut = (ButtonCode_t)(-1 * nCocoaVirtualKeyCode);
  567. else
  568. {
  569. nCocoaVirtualKeyCode &= 0x000000ff;
  570. *pOut = (ButtonCode_t)scantokey[nCocoaVirtualKeyCode];
  571. }
  572. return true;
  573. }
  574. void CInputSystem::PollInputState_Platform()
  575. {
  576. InputState_t &state = m_InputState[ m_bIsPolling ];
  577. if ( m_bPumpEnabled )
  578. m_pLauncherMgr->PumpWindowsMessageLoop();
  579. // These are Carbon virtual key codes. AFAIK they don't have a header that defines these, but they are supposed to map
  580. // to the same letters across international keyboards, so our mapping here should work.
  581. CCocoaEvent events[32];
  582. while ( 1 )
  583. {
  584. int nEvents = m_pLauncherMgr->GetEvents( events, ARRAYSIZE( events ) );
  585. if ( nEvents == 0 )
  586. break;
  587. for ( int iEvent=0; iEvent < nEvents; iEvent++ )
  588. {
  589. CCocoaEvent *pEvent = &events[iEvent];
  590. switch( pEvent->m_EventType )
  591. {
  592. case CocoaEvent_Deleted:
  593. break;
  594. case CocoaEvent_KeyDown:
  595. {
  596. ButtonCode_t virtualCode;
  597. if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) )
  598. {
  599. ButtonCode_t scanCode = virtualCode;
  600. if( ( scanCode != BUTTON_CODE_NONE ) )
  601. {
  602. // For SDL, hitting spacebar causes a SDL_KEYDOWN event, then SDL_TEXTINPUT with
  603. // event.text.text[0] = ' ', and then we get here and wind up sending two events
  604. // to PostButtonPressedEvent. The first is virtualCode = ' ', the 2nd has virtualCode = 0.
  605. // This will confuse Button::OnKeyCodePressed(), which is checking for space keydown
  606. // followed by space keyup. So we ignore all BUTTON_CODE_NONE events here.
  607. PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, scanCode, virtualCode );
  608. }
  609. InputEvent_t event;
  610. memset( &event, 0, sizeof(event) );
  611. event.m_nTick = GetPollTick();
  612. // IE_KeyCodeTyped
  613. event.m_nType = IE_FirstVguiEvent + 4;
  614. event.m_nData = scanCode;
  615. g_pInputSystem->PostUserEvent( event );
  616. }
  617. if ( !(pEvent->m_ModifierKeyMask & (1<<eCommandKey) ) && pEvent->m_VirtualKeyCode >= 0 && pEvent->m_UnicodeKey > 0 )
  618. {
  619. InputEvent_t event;
  620. memset( &event, 0, sizeof(event) );
  621. event.m_nTick = GetPollTick();
  622. // IE_KeyTyped
  623. event.m_nType = IE_FirstVguiEvent + 3;
  624. event.m_nData = (int)pEvent->m_UnicodeKey;
  625. g_pInputSystem->PostUserEvent( event );
  626. }
  627. }
  628. break;
  629. case CocoaEvent_KeyUp:
  630. {
  631. ButtonCode_t virtualCode;
  632. if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) )
  633. {
  634. if( virtualCode != BUTTON_CODE_NONE )
  635. {
  636. ButtonCode_t scanCode = virtualCode;
  637. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, scanCode, virtualCode );
  638. }
  639. }
  640. }
  641. break;
  642. case CocoaEvent_MouseButtonDown:
  643. {
  644. int nButtonMask = pEvent->m_MouseButtonFlags;
  645. ButtonCode_t dblClickCode = BUTTON_CODE_INVALID;
  646. if ( pEvent->m_nMouseClickCount > 1 )
  647. {
  648. switch( pEvent->m_MouseButton )
  649. {
  650. default:
  651. case COCOABUTTON_LEFT:
  652. dblClickCode = MOUSE_LEFT;
  653. break;
  654. case COCOABUTTON_RIGHT:
  655. dblClickCode = MOUSE_RIGHT;
  656. break;
  657. case COCOABUTTON_MIDDLE:
  658. dblClickCode = MOUSE_MIDDLE;
  659. break;
  660. case COCOABUTTON_4:
  661. dblClickCode = MOUSE_4;
  662. break;
  663. case COCOABUTTON_5:
  664. dblClickCode = MOUSE_5;
  665. break;
  666. }
  667. }
  668. UpdateMouseButtonState( nButtonMask, dblClickCode );
  669. }
  670. break;
  671. case CocoaEvent_MouseButtonUp:
  672. {
  673. int nButtonMask = pEvent->m_MouseButtonFlags;
  674. UpdateMouseButtonState( nButtonMask );
  675. }
  676. break;
  677. case CocoaEvent_MouseMove:
  678. {
  679. UpdateMousePositionState( state, (short)pEvent->m_MousePos[0], (short)pEvent->m_MousePos[1] );
  680. InputEvent_t event;
  681. memset( &event, 0, sizeof(event) );
  682. event.m_nTick = GetPollTick();
  683. // IE_LocateMouseClick
  684. event.m_nType = IE_FirstVguiEvent + 1;
  685. event.m_nData = (short)pEvent->m_MousePos[0];
  686. event.m_nData2 = (short)pEvent->m_MousePos[1];
  687. g_pInputSystem->PostUserEvent( event );
  688. }
  689. break;
  690. case CocoaEvent_MouseScroll:
  691. {
  692. ButtonCode_t code = (short)pEvent->m_MousePos[1] > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
  693. state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
  694. PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
  695. PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
  696. state.m_pAnalogDelta[ MOUSE_WHEEL ] = pEvent->m_MousePos[1];
  697. state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
  698. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
  699. }
  700. break;
  701. case CocoaEvent_AppActivate:
  702. {
  703. InputEvent_t event;
  704. memset( &event, 0, sizeof(event) );
  705. event.m_nType = IE_FirstAppEvent + 2; // IE_AppActivated (defined in sys_mainwind.cpp).
  706. event.m_nData = (bool)(pEvent->m_ModifierKeyMask != 0);
  707. g_pInputSystem->PostUserEvent( event );
  708. if( pEvent->m_ModifierKeyMask == 0 )
  709. {
  710. // App just lost focus. Handle like WM_ACTIVATEAPP in CInputSystem::WindowProc().
  711. // Otherwise alt+tab will bring focus away from our app, vgui will still think that
  712. // the alt key is down, and when we regain focus, fun ensues.
  713. g_pInputSystem->ResetInputState();
  714. }
  715. }
  716. break;
  717. case CocoaEvent_AppQuit:
  718. {
  719. PostEvent( IE_Quit, m_nLastSampleTick );
  720. }
  721. break;
  722. break;
  723. }
  724. }
  725. }
  726. }
  727. #endif // USE_SDL
  728. //-----------------------------------------------------------------------------
  729. // Polls the current input state
  730. //-----------------------------------------------------------------------------
  731. void CInputSystem::PollInputState()
  732. {
  733. m_bIsPolling = true;
  734. ++m_nPollCount;
  735. // Deals with polled input events
  736. InputState_t &queuedState = m_InputState[ INPUT_STATE_QUEUED ];
  737. CopyInputState( &m_InputState[ INPUT_STATE_CURRENT ], queuedState, true );
  738. // Sample the joystick
  739. SampleDevices();
  740. // NOTE: This happens after SampleDevices since that updates LastSampleTick
  741. // Also, I believe it's correct to post the joystick events with
  742. // the LastPollTick not updated (not 100% sure though)
  743. m_nLastPollTick = m_nLastSampleTick;
  744. #if defined( PLATFORM_WINDOWS_PC )
  745. PollInputState_Windows();
  746. #endif
  747. #if defined( USE_SDL )
  748. PollInputState_Platform();
  749. #endif
  750. // Leave the queued state up-to-date with the current
  751. CopyInputState( &queuedState, m_InputState[ INPUT_STATE_CURRENT ], false );
  752. m_bIsPolling = false;
  753. }
  754. //-----------------------------------------------------------------------------
  755. // Computes the sample tick
  756. //-----------------------------------------------------------------------------
  757. int CInputSystem::ComputeSampleTick()
  758. {
  759. // This logic will only fail if the app has been running for 49.7 days
  760. int nSampleTick;
  761. DWORD nCurrentTick = Plat_MSTime();
  762. if ( nCurrentTick >= m_StartupTimeTick )
  763. {
  764. nSampleTick = (int)( nCurrentTick - m_StartupTimeTick );
  765. }
  766. else
  767. {
  768. DWORD nDelta = (DWORD)0xFFFFFFFF - m_StartupTimeTick;
  769. nSampleTick = (int)( nCurrentTick + nDelta ) + 1;
  770. }
  771. return nSampleTick;
  772. }
  773. //-----------------------------------------------------------------------------
  774. // How many times has poll been called?
  775. //-----------------------------------------------------------------------------
  776. int CInputSystem::GetPollCount() const
  777. {
  778. return m_nPollCount;
  779. }
  780. //-----------------------------------------------------------------------------
  781. // Samples attached devices and appends events to the input queue
  782. //-----------------------------------------------------------------------------
  783. void CInputSystem::SampleDevices( void )
  784. {
  785. m_nLastSampleTick = ComputeSampleTick();
  786. PollJoystick();
  787. #if defined( PLATFORM_WINDOWS_PC )
  788. // NVNT if we have device/s poll them.
  789. if ( m_bNovintDevices )
  790. {
  791. PollNovintDevices();
  792. }
  793. #endif
  794. PollSteamControllers();
  795. }
  796. //-----------------------------------------------------------------------------
  797. // Purpose: Sets a player as the primary user - all other controllers will be ignored.
  798. //-----------------------------------------------------------------------------
  799. void CInputSystem::SetPrimaryUserId( int userId )
  800. {
  801. if ( userId >= XUSER_MAX_COUNT || userId < 0 )
  802. {
  803. m_PrimaryUserId = INVALID_USER_ID;
  804. }
  805. else
  806. {
  807. m_PrimaryUserId = userId;
  808. }
  809. #if !defined(POSIX)
  810. XBX_SetPrimaryUserId( m_PrimaryUserId );
  811. #endif
  812. ConMsg("PrimaryUserId is %d\n", m_PrimaryUserId );
  813. }
  814. //-----------------------------------------------------------------------------
  815. // Purpose: Forwards rumble info to attached devices
  816. //-----------------------------------------------------------------------------
  817. void CInputSystem::SetRumble( float fLeftMotor, float fRightMotor, int userId )
  818. {
  819. SetXDeviceRumble( fLeftMotor, fRightMotor, userId );
  820. }
  821. //-----------------------------------------------------------------------------
  822. // Purpose: Force an immediate stop, transmits immediately to all devices
  823. //-----------------------------------------------------------------------------
  824. void CInputSystem::StopRumble( void )
  825. {
  826. #ifdef _X360
  827. xdevice_t* pXDevice = &m_XDevices[0];
  828. for ( int i = 0; i < XUSER_MAX_COUNT; ++i, ++pXDevice )
  829. {
  830. if ( pXDevice->active )
  831. {
  832. pXDevice->vibration.wLeftMotorSpeed = 0;
  833. pXDevice->vibration.wRightMotorSpeed = 0;
  834. pXDevice->pendingRumbleUpdate = true;
  835. WriteToXDevice( pXDevice );
  836. }
  837. }
  838. #else
  839. for ( int i = 0; i < XUSER_MAX_COUNT; ++i )
  840. {
  841. SetRumble(0.0, 0.0, i);
  842. }
  843. #endif
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Joystick interface
  847. //-----------------------------------------------------------------------------
  848. int CInputSystem::GetJoystickCount() const
  849. {
  850. return m_nJoystickCount;
  851. }
  852. void CInputSystem::EnableJoystickInput( int nJoystick, bool bEnable )
  853. {
  854. m_JoysticksEnabled.SetFlag( 1 << nJoystick, bEnable );
  855. }
  856. void CInputSystem::EnableJoystickDiagonalPOV( int nJoystick, bool bEnable )
  857. {
  858. m_pJoystickInfo[ nJoystick ].m_bDiagonalPOVControlEnabled = bEnable;
  859. }
  860. //-----------------------------------------------------------------------------
  861. // Poll current state
  862. //-----------------------------------------------------------------------------
  863. int CInputSystem::GetPollTick() const
  864. {
  865. return m_nLastPollTick;
  866. }
  867. bool CInputSystem::IsButtonDown( ButtonCode_t code ) const
  868. {
  869. return m_InputState[INPUT_STATE_CURRENT].m_ButtonState.IsBitSet( code );
  870. }
  871. int CInputSystem::GetAnalogValue( AnalogCode_t code ) const
  872. {
  873. return m_InputState[INPUT_STATE_CURRENT].m_pAnalogValue[code];
  874. }
  875. int CInputSystem::GetAnalogDelta( AnalogCode_t code ) const
  876. {
  877. return m_InputState[INPUT_STATE_CURRENT].m_pAnalogDelta[code];
  878. }
  879. int CInputSystem::GetButtonPressedTick( ButtonCode_t code ) const
  880. {
  881. return m_InputState[INPUT_STATE_CURRENT].m_ButtonPressedTick[code];
  882. }
  883. int CInputSystem::GetButtonReleasedTick( ButtonCode_t code ) const
  884. {
  885. return m_InputState[INPUT_STATE_CURRENT].m_ButtonReleasedTick[code];
  886. }
  887. //-----------------------------------------------------------------------------
  888. // Returns the input events since the last poll
  889. //-----------------------------------------------------------------------------
  890. int CInputSystem::GetEventCount() const
  891. {
  892. return m_InputState[INPUT_STATE_CURRENT].m_Events.Count();
  893. }
  894. const InputEvent_t* CInputSystem::GetEventData( ) const
  895. {
  896. return m_InputState[INPUT_STATE_CURRENT].m_Events.Base();
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Posts a user-defined event into the event queue; this is expected
  900. // to be called in overridden wndprocs connected to the root panel.
  901. //-----------------------------------------------------------------------------
  902. void CInputSystem::PostUserEvent( const InputEvent_t &event )
  903. {
  904. InputState_t &state = m_InputState[ m_bIsPolling ];
  905. state.m_Events.AddToTail( event );
  906. state.m_bDirty = true;
  907. }
  908. //-----------------------------------------------------------------------------
  909. // Chains the window message to the previous wndproc
  910. //-----------------------------------------------------------------------------
  911. inline LRESULT CInputSystem::ChainWindowMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  912. {
  913. #if !defined( POSIX )
  914. if ( m_ChainedWndProc )
  915. return CallWindowProc( m_ChainedWndProc, hwnd, uMsg, wParam, lParam );
  916. #endif
  917. // FIXME: This comment is lifted from vguimatsurface;
  918. // may not apply in future when the system is completed.
  919. // This means the application is driving the messages (calling our window procedure manually)
  920. // rather than us hooking their window procedure. The engine needs to do this in order for VCR
  921. // mode to play back properly.
  922. return 0;
  923. }
  924. //-----------------------------------------------------------------------------
  925. // Release all buttons
  926. //-----------------------------------------------------------------------------
  927. void CInputSystem::ReleaseAllButtons( int nFirstButton, int nLastButton )
  928. {
  929. // Force button up messages for all down buttons
  930. for ( int i = nFirstButton; i <= nLastButton; ++i )
  931. {
  932. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, (ButtonCode_t)i, (ButtonCode_t)i );
  933. }
  934. }
  935. //-----------------------------------------------------------------------------
  936. // Zero analog state
  937. //-----------------------------------------------------------------------------
  938. void CInputSystem::ZeroAnalogState( int nFirstState, int nLastState )
  939. {
  940. InputState_t &state = m_InputState[ m_bIsPolling ];
  941. memset( &state.m_pAnalogDelta[nFirstState], 0, ( nLastState - nFirstState + 1 ) * sizeof(int) );
  942. memset( &state.m_pAnalogValue[nFirstState], 0, ( nLastState - nFirstState + 1 ) * sizeof(int) );
  943. }
  944. //-----------------------------------------------------------------------------
  945. // Determines all mouse button presses
  946. //-----------------------------------------------------------------------------
  947. int CInputSystem::ButtonMaskFromMouseWParam( WPARAM wParam, ButtonCode_t code, bool bDown ) const
  948. {
  949. int nButtonMask = 0;
  950. #if defined( PLATFORM_WINDOWS )
  951. if ( wParam & MK_LBUTTON )
  952. {
  953. nButtonMask |= 1;
  954. }
  955. if ( wParam & MK_RBUTTON )
  956. {
  957. nButtonMask |= 2;
  958. }
  959. if ( wParam & MK_MBUTTON )
  960. {
  961. nButtonMask |= 4;
  962. }
  963. if ( wParam & MS_MK_BUTTON4 )
  964. {
  965. nButtonMask |= 8;
  966. }
  967. if ( wParam & MS_MK_BUTTON5 )
  968. {
  969. nButtonMask |= 16;
  970. }
  971. #endif
  972. #ifdef _DEBUG
  973. if ( code != BUTTON_CODE_INVALID )
  974. {
  975. int nMsgMask = 1 << ( code - MOUSE_FIRST );
  976. int nTestMask = bDown ? nMsgMask : 0;
  977. Assert( ( nButtonMask & nMsgMask ) == nTestMask );
  978. }
  979. #endif
  980. return nButtonMask;
  981. }
  982. //-----------------------------------------------------------------------------
  983. // Updates the state of all mouse buttons
  984. //-----------------------------------------------------------------------------
  985. void CInputSystem::UpdateMouseButtonState( int nButtonMask, ButtonCode_t dblClickCode )
  986. {
  987. for ( int i = 0; i < 5; ++i )
  988. {
  989. ButtonCode_t code = (ButtonCode_t)( MOUSE_FIRST + i );
  990. bool bDown = ( nButtonMask & ( 1 << i ) ) != 0;
  991. if ( bDown )
  992. {
  993. InputEventType_t type = ( code != dblClickCode ) ? IE_ButtonPressed : IE_ButtonDoubleClicked;
  994. PostButtonPressedEvent( type, m_nLastSampleTick, code, code );
  995. }
  996. else
  997. {
  998. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
  999. }
  1000. }
  1001. }
  1002. //-----------------------------------------------------------------------------
  1003. // Handles input messages
  1004. //-----------------------------------------------------------------------------
  1005. void CInputSystem::SetCursorPosition( int x, int y )
  1006. {
  1007. if ( !m_hAttachedHWnd )
  1008. return;
  1009. #if defined( PLATFORM_WINDOWS )
  1010. POINT pt;
  1011. pt.x = x; pt.y = y;
  1012. ClientToScreen( (HWND)m_hAttachedHWnd, &pt );
  1013. SetCursorPos( pt.x, pt.y );
  1014. #elif defined( USE_SDL )
  1015. m_pLauncherMgr->SetCursorPosition( x, y );
  1016. #endif
  1017. InputState_t &state = m_InputState[ m_bIsPolling ];
  1018. bool bXChanged = ( state.m_pAnalogValue[ MOUSE_X ] != x );
  1019. bool bYChanged = ( state.m_pAnalogValue[ MOUSE_Y ] != y );
  1020. state.m_pAnalogValue[ MOUSE_X ] = x;
  1021. state.m_pAnalogValue[ MOUSE_Y ] = y;
  1022. state.m_pAnalogDelta[ MOUSE_X ] = 0;
  1023. state.m_pAnalogDelta[ MOUSE_Y ] = 0;
  1024. if ( bXChanged )
  1025. {
  1026. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogDelta[ MOUSE_X ] );
  1027. }
  1028. if ( bYChanged )
  1029. {
  1030. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, state.m_pAnalogValue[ MOUSE_Y ], state.m_pAnalogDelta[ MOUSE_Y ] );
  1031. }
  1032. if ( bXChanged || bYChanged )
  1033. {
  1034. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogValue[ MOUSE_Y ] );
  1035. }
  1036. }
  1037. void CInputSystem::UpdateMousePositionState( InputState_t &state, short x, short y )
  1038. {
  1039. int nOldX = state.m_pAnalogValue[ MOUSE_X ];
  1040. int nOldY = state.m_pAnalogValue[ MOUSE_Y ];
  1041. state.m_pAnalogValue[ MOUSE_X ] = x;
  1042. state.m_pAnalogValue[ MOUSE_Y ] = y;
  1043. state.m_pAnalogDelta[ MOUSE_X ] = state.m_pAnalogValue[ MOUSE_X ] - nOldX;
  1044. state.m_pAnalogDelta[ MOUSE_Y ] = state.m_pAnalogValue[ MOUSE_Y ] - nOldY;
  1045. if ( state.m_pAnalogDelta[ MOUSE_X ] != 0 )
  1046. {
  1047. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogDelta[ MOUSE_X ] );
  1048. }
  1049. if ( state.m_pAnalogDelta[ MOUSE_Y ] != 0 )
  1050. {
  1051. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, state.m_pAnalogValue[ MOUSE_Y ], state.m_pAnalogDelta[ MOUSE_Y ] );
  1052. }
  1053. if ( state.m_pAnalogDelta[ MOUSE_X ] != 0 || state.m_pAnalogDelta[ MOUSE_Y ] != 0 )
  1054. {
  1055. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogValue[ MOUSE_Y ] );
  1056. }
  1057. }
  1058. //-----------------------------------------------------------------------------
  1059. // Handles input messages
  1060. //-----------------------------------------------------------------------------
  1061. LRESULT CInputSystem::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  1062. {
  1063. #if defined( PLATFORM_WINDOWS ) // We use this even for SDL to handle mouse move.
  1064. if ( !m_bEnabled )
  1065. return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
  1066. if ( hwnd != m_hAttachedHWnd )
  1067. return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
  1068. InputState_t &state = m_InputState[ m_bIsPolling ];
  1069. switch( uMsg )
  1070. {
  1071. #if !defined( USE_SDL )
  1072. case WM_ACTIVATEAPP:
  1073. if ( hwnd == m_hAttachedHWnd )
  1074. {
  1075. bool bActivated = ( wParam == 1 );
  1076. if ( !bActivated )
  1077. {
  1078. ResetInputState();
  1079. }
  1080. }
  1081. break;
  1082. case WM_LBUTTONDOWN:
  1083. {
  1084. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true );
  1085. ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1086. UpdateMouseButtonState( nButtonMask );
  1087. }
  1088. break;
  1089. case WM_LBUTTONUP:
  1090. {
  1091. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, false );
  1092. ETWMouseUp( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1093. UpdateMouseButtonState( nButtonMask );
  1094. }
  1095. break;
  1096. case WM_RBUTTONDOWN:
  1097. {
  1098. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true );
  1099. ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1100. UpdateMouseButtonState( nButtonMask );
  1101. }
  1102. break;
  1103. case WM_RBUTTONUP:
  1104. {
  1105. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, false );
  1106. ETWMouseUp( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1107. UpdateMouseButtonState( nButtonMask );
  1108. }
  1109. break;
  1110. case WM_MBUTTONDOWN:
  1111. {
  1112. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true );
  1113. ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1114. UpdateMouseButtonState( nButtonMask );
  1115. }
  1116. break;
  1117. case WM_MBUTTONUP:
  1118. {
  1119. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, false );
  1120. ETWMouseUp( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1121. UpdateMouseButtonState( nButtonMask );
  1122. }
  1123. break;
  1124. case MS_WM_XBUTTONDOWN:
  1125. {
  1126. ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
  1127. int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, true );
  1128. UpdateMouseButtonState( nButtonMask );
  1129. // Windows docs say the XBUTTON messages we should return true from
  1130. return TRUE;
  1131. }
  1132. break;
  1133. case MS_WM_XBUTTONUP:
  1134. {
  1135. ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
  1136. int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, false );
  1137. UpdateMouseButtonState( nButtonMask );
  1138. // Windows docs say the XBUTTON messages we should return true from
  1139. return TRUE;
  1140. }
  1141. break;
  1142. case WM_LBUTTONDBLCLK:
  1143. {
  1144. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true );
  1145. ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1146. UpdateMouseButtonState( nButtonMask, MOUSE_LEFT );
  1147. }
  1148. break;
  1149. case WM_RBUTTONDBLCLK:
  1150. {
  1151. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true );
  1152. ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1153. UpdateMouseButtonState( nButtonMask, MOUSE_RIGHT );
  1154. }
  1155. break;
  1156. case WM_MBUTTONDBLCLK:
  1157. {
  1158. int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true );
  1159. ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1160. UpdateMouseButtonState( nButtonMask, MOUSE_MIDDLE );
  1161. }
  1162. break;
  1163. case MS_WM_XBUTTONDBLCLK:
  1164. {
  1165. ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
  1166. int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, true );
  1167. UpdateMouseButtonState( nButtonMask, code );
  1168. // Windows docs say the XBUTTON messages we should return true from
  1169. return TRUE;
  1170. }
  1171. break;
  1172. case WM_KEYDOWN:
  1173. case WM_SYSKEYDOWN:
  1174. {
  1175. // Suppress key repeats
  1176. if ( !( lParam & ( 1<<30 ) ) )
  1177. {
  1178. // NOTE: These two can be unequal! For example, keypad enter
  1179. // which returns KEY_ENTER from virtual keys, and KEY_PAD_ENTER from scan codes
  1180. // Since things like vgui care about virtual keys; we're going to
  1181. // put both scan codes in the input message
  1182. ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
  1183. ButtonCode_t scanCode = ButtonCode_ScanCodeToButtonCode( lParam );
  1184. PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, scanCode, virtualCode );
  1185. // Post ETW events describing key presses to help correlate input events to performance
  1186. // problems in the game.
  1187. ETWKeyDown( scanCode, virtualCode, ButtonCodeToString( virtualCode ) );
  1188. // Deal with toggles
  1189. if ( scanCode == KEY_CAPSLOCK || scanCode == KEY_SCROLLLOCK || scanCode == KEY_NUMLOCK )
  1190. {
  1191. int nVirtualKey;
  1192. ButtonCode_t toggleCode;
  1193. switch( scanCode )
  1194. {
  1195. default: case KEY_CAPSLOCK: nVirtualKey = VK_CAPITAL; toggleCode = KEY_CAPSLOCKTOGGLE; break;
  1196. case KEY_SCROLLLOCK: nVirtualKey = VK_SCROLL; toggleCode = KEY_SCROLLLOCKTOGGLE; break;
  1197. case KEY_NUMLOCK: nVirtualKey = VK_NUMLOCK; toggleCode = KEY_NUMLOCKTOGGLE; break;
  1198. };
  1199. SHORT wState = GetKeyState( nVirtualKey );
  1200. bool bToggleState = ( wState & 0x1 ) != 0;
  1201. PostButtonPressedEvent( bToggleState ? IE_ButtonPressed : IE_ButtonReleased, m_nLastSampleTick, toggleCode, toggleCode );
  1202. }
  1203. }
  1204. }
  1205. break;
  1206. case WM_KEYUP:
  1207. case WM_SYSKEYUP:
  1208. {
  1209. // Don't handle key ups if the key's already up. This can happen when we alt-tab back to the engine.
  1210. ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
  1211. ButtonCode_t scanCode = ButtonCode_ScanCodeToButtonCode( lParam );
  1212. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, scanCode, virtualCode );
  1213. }
  1214. break;
  1215. case WM_MOUSEWHEEL:
  1216. {
  1217. ButtonCode_t code = (short)HIWORD( wParam ) > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
  1218. state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
  1219. PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
  1220. PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
  1221. state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( (short)HIWORD(wParam) ) / WHEEL_DELTA;
  1222. state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
  1223. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
  1224. }
  1225. break;
  1226. #if defined( PLATFORM_WINDOWS_PC )
  1227. case WM_INPUT:
  1228. {
  1229. if ( m_bRawInputSupported )
  1230. {
  1231. UINT dwSize = 40;
  1232. static BYTE lpb[40];
  1233. pfnGetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
  1234. RAWINPUT* raw = (RAWINPUT*)lpb;
  1235. if (raw->header.dwType == RIM_TYPEMOUSE)
  1236. {
  1237. m_mouseRawAccumX += raw->data.mouse.lLastX;
  1238. m_mouseRawAccumY += raw->data.mouse.lLastY;
  1239. }
  1240. }
  1241. }
  1242. break;
  1243. #endif
  1244. #endif // !USE_SDL
  1245. case WM_MOUSEMOVE:
  1246. {
  1247. UpdateMousePositionState( state, (short)LOWORD(lParam), (short)HIWORD(lParam) );
  1248. int nButtonMask = ButtonMaskFromMouseWParam( wParam );
  1249. UpdateMouseButtonState( nButtonMask );
  1250. }
  1251. break;
  1252. }
  1253. #if defined( PLATFORM_WINDOWS_PC ) && !defined( USE_SDL )
  1254. // Can't put this in the case statement, it's not constant
  1255. if ( uMsg == m_uiMouseWheel )
  1256. {
  1257. ButtonCode_t code = ( ( int )wParam ) > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
  1258. state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
  1259. PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
  1260. PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
  1261. state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( ( int )wParam ) / WHEEL_DELTA;
  1262. state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
  1263. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
  1264. }
  1265. #endif
  1266. #endif // PLATFORM_WINDOWS
  1267. return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
  1268. }
  1269. bool CInputSystem::GetRawMouseAccumulators( int& accumX, int& accumY )
  1270. {
  1271. #if defined( USE_SDL )
  1272. if ( m_pLauncherMgr )
  1273. {
  1274. m_pLauncherMgr->GetMouseDelta( accumX, accumY, false );
  1275. return true;
  1276. }
  1277. return false;
  1278. #else
  1279. accumX = m_mouseRawAccumX;
  1280. accumY = m_mouseRawAccumY;
  1281. m_mouseRawAccumX = m_mouseRawAccumY = 0;
  1282. return m_bRawInputSupported;
  1283. #endif
  1284. }
  1285. void CInputSystem::SetConsoleTextMode( bool bConsoleTextMode )
  1286. {
  1287. /* If someone calls this after init, shut it down. */
  1288. if ( bConsoleTextMode && m_bJoystickInitialized )
  1289. {
  1290. ShutdownJoysticks();
  1291. }
  1292. m_bConsoleTextMode = bConsoleTextMode;
  1293. }
  1294. ISteamController* CInputSystem::SteamControllerInterface()
  1295. {
  1296. if ( m_bSkipControllerInitialization )
  1297. {
  1298. return nullptr;
  1299. }
  1300. else
  1301. {
  1302. return m_SteamAPIContext.SteamController();
  1303. }
  1304. }