Counter Strike : Global Offensive Source Code
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.

1370 lines
34 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. #include "keys.h"
  9. #include "cdll_engine_int.h"
  10. #include "cmd.h"
  11. #include "toolframework/itoolframework.h"
  12. #include "toolframework/itoolsystem.h"
  13. #include "tier1/utlbuffer.h"
  14. #include "vgui_baseui_interface.h"
  15. #include "tier2/tier2.h"
  16. #include "inputsystem/iinputsystem.h"
  17. #include "keyvalues.h"
  18. #include "host.h"
  19. #include "filesystem.h"
  20. #include "filesystem_engine.h"
  21. #include "cheatcodes.h"
  22. #include "baseclientstate.h" // splitscreen interface
  23. #include "tier1/fmtstr.h"
  24. #include "EngineSoundInternal.h"
  25. #ifndef DEDICATED
  26. #include "cl_splitscreen.h"
  27. #endif
  28. #if defined( INCLUDE_SCALEFORM )
  29. #include "scaleformui/scaleformui.h"
  30. #endif
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. ConVar in_forceuser( "in_forceuser", "0", FCVAR_CHEAT, "Force user input to this split screen player." );
  34. enum KeyUpTarget_t
  35. {
  36. KEY_UP_ANYTARGET = 0,
  37. KEY_UP_ENGINE,
  38. KEY_UP_VGUI,
  39. KEY_UP_TOOLS,
  40. KEY_UP_CLIENT,
  41. KEY_UP_GAMEUI,
  42. KEY_UP_SCALEFORM,
  43. KEY_UP_OVERLAY,
  44. KEY_UP_TARGET_COUNT
  45. };
  46. struct KeyInfo_t
  47. {
  48. char *m_pKeyBinding;
  49. unsigned char m_nKeyUpTarget : 4; // see KeyUpTarget_t
  50. unsigned char m_bKeyDown : 1;
  51. };
  52. //-----------------------------------------------------------------------------
  53. // Current keypress state
  54. //-----------------------------------------------------------------------------
  55. struct KeyContext_t
  56. {
  57. KeyContext_t()
  58. {
  59. // NOTE: If this compile-time assert fails, modify the bit count
  60. // in KeyInfo_t::m_nKeyUpTarget
  61. COMPILE_TIME_ASSERT( KEY_UP_TARGET_COUNT < 16 );
  62. Q_memset( m_pKeyInfo, 0, sizeof( m_pKeyInfo ) );
  63. m_bTrapMode = false;
  64. m_bDoneTrapping = false;
  65. m_nTrapKeyUp = BUTTON_CODE_INVALID;
  66. m_nTrapKey = BUTTON_CODE_INVALID;
  67. }
  68. KeyInfo_t m_pKeyInfo[BUTTON_CODE_LAST];
  69. //-----------------------------------------------------------------------------
  70. // Trap mode is used by the keybinding UI
  71. //-----------------------------------------------------------------------------
  72. bool m_bTrapMode;
  73. bool m_bDoneTrapping;
  74. ButtonCode_t m_nTrapKeyUp;
  75. ButtonCode_t m_nTrapKey;
  76. };
  77. static KeyContext_t s_KeyContext;
  78. //-----------------------------------------------------------------------------
  79. // Can keys be passed to various targets?
  80. //-----------------------------------------------------------------------------
  81. static inline bool ShouldPassKeyUpToTarget( ButtonCode_t code, KeyUpTarget_t target )
  82. {
  83. if (code < 0 || code >= BUTTON_CODE_LAST)
  84. return true;
  85. return ( s_KeyContext.m_pKeyInfo[code].m_nKeyUpTarget == target ) || ( s_KeyContext.m_pKeyInfo[code].m_nKeyUpTarget == KEY_UP_ANYTARGET );
  86. }
  87. ButtonCode_t GetSplitPlayerJoystickCode( ButtonCode_t bc )
  88. {
  89. #ifdef DEDICATED
  90. return bc;
  91. #else
  92. if ( !IsJoystickCode( bc ) && !IsSteamControllerCode( bc ) )
  93. return bc;
  94. int nJoystick = GET_ACTIVE_SPLITSCREEN_SLOT();
  95. return ButtonCodeToJoystickButtonCode( bc, nJoystick );
  96. #endif
  97. }
  98. void Key_SetBinding( ButtonCode_t keynum, const char *pBinding )
  99. {
  100. char *pNewBinding;
  101. int l;
  102. if ( keynum == BUTTON_CODE_INVALID )
  103. return;
  104. keynum = GetSplitPlayerJoystickCode( keynum );
  105. // free old bindings
  106. if ( s_KeyContext.m_pKeyInfo[keynum].m_pKeyBinding )
  107. {
  108. // Exactly the same, don't re-bind and fragment memory
  109. if ( !Q_strcmp( s_KeyContext.m_pKeyInfo[keynum].m_pKeyBinding, pBinding ) )
  110. return;
  111. delete[] s_KeyContext.m_pKeyInfo[keynum].m_pKeyBinding;
  112. s_KeyContext.m_pKeyInfo[keynum].m_pKeyBinding = NULL;
  113. }
  114. // allocate memory for new binding
  115. l = Q_strlen( pBinding );
  116. pNewBinding = (char *)new char[ l+1 ];
  117. Q_strncpy( pNewBinding, pBinding, l + 1 );
  118. pNewBinding[l] = 0;
  119. s_KeyContext.m_pKeyInfo[keynum].m_pKeyBinding = pNewBinding;
  120. #if defined( INCLUDE_SCALEFORM )
  121. if ( g_pScaleformUI )
  122. {
  123. g_pScaleformUI->UpdateBindingForButton( keynum, pBinding );
  124. }
  125. #endif
  126. #ifndef DEDICATED
  127. if ( g_ClientDLL )
  128. {
  129. g_ClientDLL->OnKeyBindingChanged( keynum, g_pInputSystem->ButtonCodeToString( (ButtonCode_t)keynum ), pNewBinding );
  130. }
  131. #endif
  132. }
  133. /*
  134. ===================
  135. Key_Unbind_f
  136. ===================
  137. */
  138. CON_COMMAND( unbind, "Unbind a key." )
  139. {
  140. ButtonCode_t b;
  141. if ( args.ArgC() != 2 )
  142. {
  143. ConMsg( "unbind <key> : remove commands from a key\n" );
  144. return;
  145. }
  146. b = g_pInputSystem->StringToButtonCode( args[1] );
  147. if ( b == BUTTON_CODE_INVALID )
  148. {
  149. ConMsg( "\"%s\" isn't a valid key\n", args[1] );
  150. return;
  151. }
  152. if ( b == KEY_ESCAPE )
  153. {
  154. ConMsg( "Can't unbind ESCAPE key\n" );
  155. return;
  156. }
  157. Key_SetBinding( b, "" );
  158. }
  159. CON_COMMAND( unbindall, "Unbind all keys." )
  160. {
  161. int i;
  162. for ( i=0; i<BUTTON_CODE_LAST; i++ )
  163. {
  164. if ( !s_KeyContext.m_pKeyInfo[i].m_pKeyBinding )
  165. continue;
  166. // Don't ever unbind escape or console key
  167. if ( i == KEY_ESCAPE )
  168. continue;
  169. if ( i == KEY_BACKQUOTE )
  170. continue;
  171. Key_SetBinding( (ButtonCode_t)i, "" );
  172. }
  173. }
  174. CON_COMMAND( unbindalljoystick, "Unbind all joystick keys." )
  175. {
  176. int i;
  177. for ( i = 0; i < BUTTON_CODE_LAST; i++ )
  178. {
  179. if ( !s_KeyContext.m_pKeyInfo[i].m_pKeyBinding )
  180. continue;
  181. // Don't ever unbind escape or console key
  182. if ( i == KEY_ESCAPE )
  183. continue;
  184. if ( i == KEY_BACKQUOTE )
  185. continue;
  186. if ( IsJoystickCode( ( ButtonCode_t )i ) )
  187. {
  188. Key_SetBinding( (ButtonCode_t)i, "" );
  189. }
  190. }
  191. }
  192. CON_COMMAND( unbindallmousekeyboard, "Unbind all mouse / keyboard keys." )
  193. {
  194. int i;
  195. for ( i = 0; i < BUTTON_CODE_LAST; i++ )
  196. {
  197. if ( !s_KeyContext.m_pKeyInfo[i].m_pKeyBinding )
  198. continue;
  199. // Don't ever unbind escape or console key
  200. if ( i == KEY_ESCAPE )
  201. continue;
  202. if ( i == KEY_BACKQUOTE )
  203. continue;
  204. if ( IsMouseCode( ( ButtonCode_t )i ) || IsKeyCode( ( ButtonCode_t )i ) )
  205. {
  206. Key_SetBinding( (ButtonCode_t)i, "" );
  207. }
  208. }
  209. }
  210. #ifndef DEDICATED
  211. CON_COMMAND_F( escape, "Escape key pressed.", FCVAR_CLIENTCMD_CAN_EXECUTE )
  212. {
  213. EngineVGui()->HideGameUI();
  214. }
  215. #endif
  216. /*
  217. ===================
  218. Key_Bind_f
  219. ===================
  220. */
  221. static void BindHelper( const CCommand &args )
  222. {
  223. int i, c;
  224. ButtonCode_t b;
  225. char cmd[1024];
  226. c = args.ArgC();
  227. if ( c != 2 && c != 3 )
  228. {
  229. ConMsg( "bind <key> [command] : attach a command to a key\n" );
  230. return;
  231. }
  232. b = g_pInputSystem->StringToButtonCode( args[1] );
  233. if ( b == BUTTON_CODE_INVALID )
  234. {
  235. ConMsg( "\"%s\" isn't a valid key\n", args[1] );
  236. return;
  237. }
  238. if ( c == 2 )
  239. {
  240. b = GetSplitPlayerJoystickCode( b );
  241. if (s_KeyContext.m_pKeyInfo[b].m_pKeyBinding)
  242. {
  243. ConMsg( "\"%s\" = \"%s\"\n", args[1], s_KeyContext.m_pKeyInfo[b].m_pKeyBinding );
  244. }
  245. else
  246. {
  247. ConMsg( "\"%s\" is not bound\n", args[1] );
  248. }
  249. return;
  250. }
  251. if ( b == KEY_ESCAPE )
  252. {
  253. Q_strncpy( cmd, "cancelselect", sizeof( cmd ) );
  254. }
  255. else
  256. {
  257. // copy the rest of the command line
  258. cmd[0] = 0; // start out with a null string
  259. for ( i=2 ; i< c ; i++ )
  260. {
  261. if (i > 2)
  262. {
  263. Q_strncat( cmd, " ", sizeof( cmd ), COPY_ALL_CHARACTERS );
  264. }
  265. Q_strncat( cmd, args[i], sizeof( cmd ), COPY_ALL_CHARACTERS );
  266. }
  267. }
  268. Key_SetBinding( b, cmd );
  269. }
  270. CON_COMMAND( bind, "Bind a key." )
  271. {
  272. BindHelper( args );
  273. }
  274. CON_COMMAND( bind_osx, "Bind a key for OSX only." )
  275. {
  276. if ( IsOSX() )
  277. BindHelper( args );
  278. }
  279. /*
  280. ============
  281. GetDefaultKeyBindings
  282. Returns a KeyValues object holding default keybindings. Caller is responsible for freeing
  283. the KeyValues object.
  284. ============
  285. */
  286. KeyValues *GetDefaultKeyBindings( void )
  287. {
  288. KeyValues *defaults = new KeyValues( "defaults" );
  289. FileHandle_t f;
  290. char szFileName[ _MAX_PATH ];
  291. char token[ 1024 ];
  292. char szKeyName[ 256 ];
  293. // read kb_def file to get default key binds
  294. Q_snprintf( szFileName, sizeof( szFileName ), "%skb_def" PLATFORM_EXT ".lst", SCRIPT_DIR );
  295. f = g_pFileSystem->Open( szFileName, "r");
  296. if ( !f )
  297. {
  298. ConMsg( "Couldn't open kb_def.lst\n" );
  299. return defaults;
  300. }
  301. // read file into memory
  302. int size = g_pFileSystem->Size(f);
  303. char *startbuf = new char[ size + 1 ];
  304. g_pFileSystem->Read( startbuf, size, f );
  305. g_pFileSystem->Close( f );
  306. startbuf[size] = 0;
  307. const char *buf = startbuf;
  308. while ( 1 )
  309. {
  310. buf = COM_ParseFile( buf, token, sizeof( token ) );
  311. if ( strlen( token ) <= 0 )
  312. break;
  313. Q_strncpy ( szKeyName, token, sizeof( szKeyName ) );
  314. buf = COM_ParseFile( buf, token, sizeof( token ) );
  315. if ( strlen( token ) <= 0 ) // Error
  316. break;
  317. defaults->SetString( token, szKeyName );
  318. }
  319. delete [] startbuf; // cleanup on the way out
  320. return defaults;
  321. }
  322. const char *GetSuggestedBinding( const char *command, KeyValues *defaults )
  323. {
  324. if ( !defaults )
  325. return NULL;
  326. const char *suggestedKeyString = defaults->GetString( command, NULL );
  327. if ( suggestedKeyString )
  328. {
  329. return suggestedKeyString;
  330. }
  331. // If no exact match, scan for substring matches
  332. for ( KeyValues *keybind = defaults->GetFirstSubKey(); keybind != NULL; keybind = keybind->GetNextKey() )
  333. {
  334. const char *suggestedKeyString = keybind->GetString();
  335. const char *suggestedCommand = keybind->GetName();
  336. const char *subStr = V_stristr( suggestedCommand, command );
  337. if ( subStr != NULL )
  338. {
  339. return suggestedKeyString;
  340. }
  341. }
  342. return NULL;
  343. }
  344. /*
  345. ============
  346. Key_ForceBind_f
  347. Find empty keys for the incoming command strings and bind them
  348. ============
  349. */
  350. void Key_ForceBind_f( const CCommand &args )
  351. {
  352. int argc = args.ArgC();
  353. if ( argc < 2 )
  354. return;
  355. KeyValues *defaults = GetDefaultKeyBindings();
  356. Color boundColor( 0, 255, 64, 255 );
  357. Color unboundColor( 255, 0, 64, 255 );
  358. for ( int arg = 1; arg<argc; ++arg )
  359. {
  360. const char *command = args[arg];
  361. const char *currentKeyBinding = Key_NameForBinding(command);
  362. const char *suggestedKeyString = GetSuggestedBinding(command, defaults);
  363. if ( !suggestedKeyString && currentKeyBinding )
  364. {
  365. // if we don't have a suggested keybind, unbind the command
  366. ButtonCode_t key = g_pInputSystem->StringToButtonCode( currentKeyBinding );
  367. if ( key == - 1 || key == KEY_ESCAPE )
  368. {
  369. continue;
  370. }
  371. ConColorMsg( boundColor, "Unbound obsolete command \"%s\"\n", command );
  372. Key_SetBinding( key, "" );
  373. continue;
  374. }
  375. if ( currentKeyBinding != NULL )
  376. continue; // Already bound
  377. if ( suggestedKeyString == NULL )
  378. continue; // Was a key to be unbound
  379. ButtonCode_t suggestedKeynum = g_pInputSystem->StringToButtonCode( suggestedKeyString );
  380. const char *suggestedBind = Key_BindingForKey( suggestedKeynum );
  381. if ( suggestedKeynum != -1 && (suggestedBind == NULL || *suggestedBind == '\0') )
  382. {
  383. ConColorMsg( boundColor, "Bound \"%s\" to key %s\n", command, suggestedKeyString );
  384. Key_SetBinding(suggestedKeynum, command);
  385. }
  386. else
  387. {
  388. // Their suggestion was taken, so we need to find a key since we can't return empty handed.
  389. int newTry = suggestedKeynum + 1;
  390. if ( newTry > KEY_SCROLLLOCKTOGGLE )
  391. newTry = KEY_0;
  392. while( newTry != suggestedKeynum )
  393. {
  394. const char *newString = g_pInputSystem->ButtonCodeToString( (ButtonCode_t)newTry );
  395. const char *newTryBind = Key_BindingForKey( (ButtonCode_t)newTry );
  396. if ( newTryBind == NULL || *newTryBind == '\0' ) // Never set, or unset.
  397. {
  398. ConColorMsg( boundColor, "Bound \"%s\" to key %s\n", command, newString );
  399. Key_SetBinding( (ButtonCode_t)newTry, command );
  400. break;
  401. }
  402. newTry++;
  403. if ( newTry > KEY_SCROLLLOCKTOGGLE )
  404. newTry = KEY_0;
  405. }
  406. }
  407. if ( Key_NameForBinding(command) == NULL )
  408. {
  409. ConColorMsg( unboundColor, "Unable to bind \"%s\" to a key\n", command );
  410. }
  411. }
  412. defaults->deleteThis();
  413. }
  414. static ConCommand forcebind("forcebind",Key_ForceBind_f, "Bind a command to an available key. (forcebind command opt:suggestedKey)" );
  415. /*
  416. ============
  417. Key_CountBindings
  418. Count number of lines of bindings we'll be writing
  419. ============
  420. */
  421. int Key_CountBindings( void )
  422. {
  423. int i;
  424. int c = 0;
  425. for ( i = 0; i < BUTTON_CODE_LAST; i++ )
  426. {
  427. if ( !s_KeyContext.m_pKeyInfo[i].m_pKeyBinding || !s_KeyContext.m_pKeyInfo[i].m_pKeyBinding[0] )
  428. continue;
  429. c++;
  430. }
  431. return c;
  432. }
  433. /*
  434. ============
  435. Key_WriteBindings
  436. Writes lines containing "bind key value"
  437. ============
  438. */
  439. void Key_WriteBindings( CUtlBuffer &buf, const int iSplitscreenSlot /*= -1*/ )
  440. {
  441. int i;
  442. CUtlVector< CUtlString > deferred[ MAX_SPLITSCREEN_CLIENTS ];
  443. for ( i = 0 ; i < BUTTON_CODE_LAST ; i++ )
  444. {
  445. char const *pszBinding = s_KeyContext.m_pKeyInfo[i].m_pKeyBinding;
  446. if ( !pszBinding || !*pszBinding)
  447. continue;
  448. char const *pszKeyName = g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i );
  449. int nSlot = GetJoystickForCode( (ButtonCode_t)i );
  450. if ( iSplitscreenSlot >= 0 )
  451. {
  452. // only bind commands for this controller, don't use cmd2
  453. if ( iSplitscreenSlot != nSlot )
  454. continue;
  455. buf.Printf( "bind \"%s\" \"%s\"\n", pszKeyName, pszBinding );
  456. }
  457. else
  458. {
  459. if ( nSlot == 0 )
  460. {
  461. buf.Printf( "bind \"%s\" \"%s\"\n", pszKeyName, pszBinding );
  462. }
  463. else
  464. {
  465. CUtlString str;
  466. str = CFmtStrN< 2048 >( "cmd%d bind \"%s\" \"%s\"\n", nSlot + 1, pszKeyName, pszBinding );
  467. deferred[ nSlot ].AddToTail( str );
  468. }
  469. }
  470. }
  471. for ( int nSlot = 1; nSlot < host_state.max_splitscreen_players; ++nSlot )
  472. {
  473. int c = deferred[ nSlot ].Count();
  474. for ( int i = 0; i < c; ++i )
  475. {
  476. buf.Printf( "%s", deferred[ nSlot ][ i ].String() );
  477. }
  478. }
  479. }
  480. /*
  481. ============
  482. Key_NameForBinding
  483. Returns the keyname to which a binding string is bound. E.g., if
  484. TAB is bound to +use then searching for +use will return "TAB"
  485. ============
  486. */
  487. static bool IsKeyBoundedToBinding( int i, const char* pBind )
  488. {
  489. char *pszBinding = s_KeyContext.m_pKeyInfo[i].m_pKeyBinding;
  490. if ( pszBinding && *pszBinding )
  491. {
  492. if ( !strchr( pszBinding, ';' ) )
  493. {
  494. // Not a list of commands, do it the easy way
  495. if ( pszBinding[ 0 ] == '+' )
  496. {
  497. ++pszBinding;
  498. }
  499. if ( !Q_strcasecmp( pszBinding, (char *)pBind ) )
  500. {
  501. return true;
  502. }
  503. }
  504. else
  505. {
  506. // Tokenize the binding name (could be more than one binding)
  507. char szBinding[ 256 ];
  508. Q_strncpy( szBinding, pszBinding, sizeof( szBinding ) );
  509. pszBinding = strtok( szBinding, ";" );
  510. while ( pszBinding )
  511. {
  512. if ( pszBinding[ 0 ] == '+' )
  513. {
  514. ++pszBinding;
  515. }
  516. if ( !Q_strcasecmp( pszBinding, (char *)pBind ) )
  517. {
  518. return true;
  519. }
  520. pszBinding = strtok( NULL, ";" );
  521. }
  522. }
  523. }
  524. return false;
  525. }
  526. const char *Key_NameForBinding( const char *pBinding, int userId, int iStartCount, BindingLookupOption_t nFlags )
  527. {
  528. int i = Key_CodeForBinding( pBinding, userId, iStartCount, nFlags );
  529. if( pBinding && i != BUTTON_CODE_INVALID )
  530. {
  531. const char *pBind = pBinding;
  532. if ( pBinding[0] == '+' )
  533. {
  534. ++pBind;
  535. }
  536. if ( IsKeyBoundedToBinding( i, pBind ) )
  537. {
  538. return g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i );
  539. }
  540. }
  541. return NULL;
  542. }
  543. /*
  544. ============
  545. Key_CodeForBinding
  546. Returns the keycode to which a binding string is bound. E.g., if
  547. TAB is bound to +use then searching for +use will return KEY_TAB
  548. ============
  549. */
  550. int Key_CodeForBinding( const char *pBinding, int userId, int iStartCount, BindingLookupOption_t nFlags )
  551. {
  552. if( pBinding )
  553. {
  554. const char *pBind = pBinding;
  555. if ( pBinding[0] == '+' )
  556. {
  557. ++pBind;
  558. }
  559. int iCount = 0;
  560. if ( IsPC() || userId < 0 || nFlags == BINDINGLOOKUP_ALL )
  561. {
  562. for( int i=0 ; i < BUTTON_CODE_LAST ; i++ )
  563. {
  564. if( IsKeyBoundedToBinding( i, pBind ) )
  565. {
  566. if ( nFlags == BINDINGLOOKUP_ALL ||
  567. ( nFlags == BINDINGLOOKUP_STEAMCONTROLLER_ONLY && ( i >= STEAMCONTROLLER_FIRST && i <= STEAMCONTROLLER_LAST ) ) ||
  568. ( nFlags == BINDINGLOOKUP_KEYBOARD_ONLY && ( i < JOYSTICK_FIRST || i > JOYSTICK_LAST ) && ( i < STEAMCONTROLLER_FIRST || i > STEAMCONTROLLER_LAST ) ) ||
  569. ( nFlags == BINDINGLOOKUP_JOYSTICK_ONLY && ( i >= JOYSTICK_FIRST && i <= JOYSTICK_LAST ) ) )
  570. {
  571. if ( iCount == iStartCount )
  572. {
  573. return i;
  574. }
  575. else
  576. {
  577. ++iCount;
  578. }
  579. }
  580. }
  581. }
  582. }
  583. else
  584. {
  585. int first = userId * JOYSTICK_MAX_BUTTON_COUNT + JOYSTICK_FIRST_BUTTON;
  586. int last = first + JOYSTICK_MAX_BUTTON_COUNT;
  587. for( int i = first; i < last; ++i )
  588. {
  589. if( IsKeyBoundedToBinding( i, pBind ) )
  590. {
  591. if ( iCount == iStartCount )
  592. {
  593. return i;
  594. }
  595. else
  596. {
  597. ++iCount;
  598. }
  599. }
  600. }
  601. first = userId * JOYSTICK_POV_BUTTON_COUNT + JOYSTICK_FIRST_POV_BUTTON;
  602. last = first + JOYSTICK_POV_BUTTON_COUNT;
  603. for( int i = first; i < last; ++i )
  604. {
  605. if( IsKeyBoundedToBinding( i, pBind ) )
  606. {
  607. if ( iCount == iStartCount )
  608. {
  609. return i;
  610. }
  611. else
  612. {
  613. ++iCount;
  614. }
  615. }
  616. }
  617. first = userId * JOYSTICK_AXIS_BUTTON_COUNT + JOYSTICK_FIRST_AXIS_BUTTON;
  618. last = first + JOYSTICK_AXIS_BUTTON_COUNT;
  619. for( int i = first; i < last; ++i )
  620. {
  621. if( IsKeyBoundedToBinding( i, pBind ) )
  622. {
  623. if ( iCount == iStartCount )
  624. {
  625. return i;
  626. }
  627. else
  628. {
  629. ++iCount;
  630. }
  631. }
  632. }
  633. first = userId * STEAMCONTROLLER_MAX_BUTTON_COUNT + STEAMCONTROLLER_FIRST_BUTTON;
  634. last = first + STEAMCONTROLLER_MAX_BUTTON_COUNT;
  635. for( int i = first; i < last; ++i )
  636. {
  637. if( IsKeyBoundedToBinding( i, pBind ) )
  638. {
  639. if ( iCount == iStartCount )
  640. {
  641. return i;
  642. }
  643. else
  644. {
  645. ++iCount;
  646. }
  647. }
  648. }
  649. first = userId * STEAMCONTROLLER_AXIS_BUTTON_COUNT + STEAMCONTROLLER_FIRST_AXIS_BUTTON;
  650. last = first + STEAMCONTROLLER_AXIS_BUTTON_COUNT;
  651. for( int i = first; i < last; ++i )
  652. {
  653. if( IsKeyBoundedToBinding( i, pBind ) )
  654. {
  655. if ( iCount == iStartCount )
  656. {
  657. return i;
  658. }
  659. else
  660. {
  661. ++iCount;
  662. }
  663. }
  664. }
  665. }
  666. // Xbox 360 controller: Handle the dual bindings for duck and zoom
  667. if ( !Q_stricmp( "duck", pBind ) )
  668. return Key_CodeForBinding( "toggle_duck", userId, iCount );
  669. if ( !Q_stricmp( "zoom", pBind ) )
  670. return Key_CodeForBinding( "toggle_zoom", userId, iCount );
  671. }
  672. return BUTTON_CODE_INVALID;
  673. }
  674. const char *Key_BindingForKey( ButtonCode_t code )
  675. {
  676. if ( code < 0 || code > BUTTON_CODE_LAST )
  677. return NULL;
  678. return s_KeyContext.m_pKeyInfo[ code ].m_pKeyBinding;
  679. }
  680. CON_COMMAND( key_listboundkeys, "List bound keys with bindings." )
  681. {
  682. int i;
  683. for (i=0 ; i<BUTTON_CODE_LAST ; i++)
  684. {
  685. const char *pBinding = Key_BindingForKey( (ButtonCode_t)i );
  686. if ( !pBinding || !pBinding[0] )
  687. continue;
  688. if ( !IsJoystickCode( (ButtonCode_t)i ) && !IsSteamControllerCode( (ButtonCode_t)i ) )
  689. {
  690. ConMsg( "\"%s\" = \"%s\"\n", g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i ), pBinding );
  691. }
  692. else
  693. {
  694. int nSlot = GetJoystickForCode( (ButtonCode_t)i );
  695. ConMsg( "[%d:%d]\"%s\" = \"%s\"\n", i, nSlot, g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i ), pBinding );
  696. }
  697. }
  698. }
  699. CON_COMMAND( key_findbinding, "Find key bound to specified command string." )
  700. {
  701. if ( args.ArgC() != 2 )
  702. {
  703. ConMsg( "usage: key_findbinding substring\n" );
  704. return;
  705. }
  706. const char *substring = args[1];
  707. if ( !substring || !substring[ 0 ] )
  708. {
  709. ConMsg( "usage: key_findbinding substring\n" );
  710. return;
  711. }
  712. int i;
  713. for (i=0 ; i<BUTTON_CODE_LAST ; i++)
  714. {
  715. const char *pBinding = Key_BindingForKey( (ButtonCode_t)i );
  716. if ( !pBinding || !pBinding[0] )
  717. continue;
  718. if ( Q_strstr( pBinding, substring ) )
  719. {
  720. if ( !IsJoystickCode( (ButtonCode_t)i ) )
  721. {
  722. ConMsg( "\"%s\" = \"%s\"\n",
  723. g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i ), pBinding );
  724. }
  725. else
  726. {
  727. int nSlot = GetJoystickForCode( (ButtonCode_t)i );
  728. ConMsg( "[%d] \"%s\" = \"%s\"\n",
  729. nSlot, g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i ), pBinding );
  730. }
  731. }
  732. }
  733. }
  734. //-----------------------------------------------------------------------------
  735. // Initialization, shutdown
  736. //-----------------------------------------------------------------------------
  737. void Key_Init (void)
  738. {
  739. ReadCheatCommandsFromFile( "scripts/cheatcodes.txt" );
  740. ReadCheatCommandsFromFile( "scripts/mod_cheatcodes.txt" );
  741. }
  742. void Key_Shutdown( void )
  743. {
  744. for ( int i = 0; i < ARRAYSIZE( s_KeyContext.m_pKeyInfo ); ++i )
  745. {
  746. delete[] s_KeyContext.m_pKeyInfo[ i ].m_pKeyBinding;
  747. s_KeyContext.m_pKeyInfo[ i ].m_pKeyBinding = NULL;
  748. }
  749. ClearCheatCommands();
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose: Starts trap mode (used for keybinding UI)
  753. //-----------------------------------------------------------------------------
  754. void Key_StartTrapMode( void )
  755. {
  756. if ( s_KeyContext.m_bTrapMode )
  757. return;
  758. Assert( !s_KeyContext.m_bDoneTrapping && s_KeyContext.m_nTrapKeyUp == BUTTON_CODE_INVALID );
  759. s_KeyContext.m_bDoneTrapping = false;
  760. s_KeyContext.m_bTrapMode = true;
  761. s_KeyContext.m_nTrapKeyUp = BUTTON_CODE_INVALID;
  762. }
  763. //-----------------------------------------------------------------------------
  764. // We're done trapping once the first key is hit
  765. //-----------------------------------------------------------------------------
  766. bool Key_CheckDoneTrapping( ButtonCode_t& code )
  767. {
  768. if ( s_KeyContext.m_bTrapMode )
  769. return false;
  770. if ( !s_KeyContext.m_bDoneTrapping )
  771. return false;
  772. code = s_KeyContext.m_nTrapKey;
  773. s_KeyContext.m_nTrapKey = BUTTON_CODE_INVALID;
  774. // Reset since we retrieved the results
  775. s_KeyContext.m_bDoneTrapping = false;
  776. return true;
  777. }
  778. //-----------------------------------------------------------------------------
  779. // Filter out trapped keys
  780. //-----------------------------------------------------------------------------
  781. static bool FilterTrappedKey( ButtonCode_t code, bool bDown )
  782. {
  783. // After we've trapped a key, we want to capture the button up message for that key
  784. if ( s_KeyContext.m_nTrapKeyUp == code && !bDown )
  785. {
  786. s_KeyContext.m_nTrapKeyUp = BUTTON_CODE_INVALID;
  787. return true;
  788. }
  789. // Only key down events are trapped
  790. if ( s_KeyContext.m_bTrapMode && bDown )
  791. {
  792. s_KeyContext.m_nTrapKey = code;
  793. s_KeyContext.m_bTrapMode = false;
  794. s_KeyContext.m_bDoneTrapping = true;
  795. s_KeyContext.m_nTrapKeyUp = code;
  796. return true;
  797. }
  798. return false;
  799. }
  800. #ifndef DEDICATED
  801. //-----------------------------------------------------------------------------
  802. // Lets tools have a whack at key events
  803. //-----------------------------------------------------------------------------
  804. static bool HandleToolKey( const InputEvent_t &event )
  805. {
  806. // Tools are not given typed messages
  807. if ( event.m_nType == IE_KeyTyped || event.m_nType == IE_KeyCodeTyped )
  808. return false;
  809. IToolSystem *toolsys = toolframework->GetTopmostTool();
  810. return toolsys && toolsys->TrapKey( (ButtonCode_t)event.m_nData, ( event.m_nType != IE_ButtonReleased ) );
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Lets vgui have a whack at key events
  814. //-----------------------------------------------------------------------------
  815. static bool HandleVGuiKey( const InputEvent_t &event )
  816. {
  817. bool bDown = ( event.m_nType == IE_ButtonPressed ) || ( event.m_nType == IE_ButtonDoubleClicked );
  818. if ( bDown && IsGameConsole() )
  819. {
  820. ButtonCode_t code = (ButtonCode_t)event.m_nData;
  821. LogKeyPress( code );
  822. CheckCheatCodes();
  823. }
  824. return EngineVGui()->Key_Event( event );
  825. }
  826. #if defined( INCLUDE_SCALEFORM )
  827. //-----------------------------------------------------------------------------
  828. // Lets scaleform have a whack at key events
  829. //-----------------------------------------------------------------------------
  830. static bool HandleScaleformKey( const InputEvent_t &event )
  831. {
  832. ButtonCode_t code = ( ButtonCode_t ) event.m_nData;
  833. bool bDown = ( event.m_nType == IE_ButtonPressed ) || ( event.m_nType == IE_ButtonDoubleClicked );
  834. if ( bDown )
  835. {
  836. if ( IsX360() )
  837. {
  838. LogKeyPress( code );
  839. CheckCheatCodes();
  840. }
  841. #if defined ( CSTRIKE15 )
  842. if ( !g_ClientDLL->IsLoadingScreenRaised() && !g_ClientDLL->IsChatRaised() && !g_ClientDLL->IsBindMenuRaised() && !g_ClientDLL->IsRadioPanelRaised() && !g_ClientDLL->IsTeamMenuRaised() )
  843. #endif
  844. {
  845. const char * szBinding = s_KeyContext.m_pKeyInfo[ code ].m_pKeyBinding;
  846. if ( szBinding && szBinding[0] )
  847. {
  848. // Add entries to this list if you want to PREVENT scaleform from filtering them
  849. static const char *szNoFilterList[] =
  850. {
  851. "screenshot",
  852. };
  853. static const int kNumNoFilterEntries = sizeof( szNoFilterList ) / sizeof( szNoFilterList[0] );
  854. for ( int idx=0; idx < kNumNoFilterEntries; ++idx )
  855. {
  856. if ( StringHasPrefix( szBinding, szNoFilterList[idx] ) )
  857. {
  858. return false;
  859. }
  860. }
  861. // Only filter these binds when the GameUI is active
  862. static const char *szNoFilterListGameUI[] =
  863. {
  864. "messagemode",
  865. "messagemode2",
  866. "+showscores",
  867. "togglescores",
  868. "+voicerecord",
  869. };
  870. static const int kNumNoFilterEntriesGameUI = sizeof( szNoFilterListGameUI ) / sizeof( szNoFilterListGameUI[0] );
  871. if ( !EngineVGui()->IsGameUIVisible() )
  872. {
  873. for ( int idx=0; idx < kNumNoFilterEntriesGameUI; ++idx )
  874. {
  875. if ( StringHasPrefix( szBinding, szNoFilterListGameUI[idx] ) )
  876. {
  877. return false;
  878. }
  879. }
  880. }
  881. }
  882. }
  883. }
  884. return g_pScaleformUI->HandleInputEvent( event );
  885. }
  886. #endif
  887. //-----------------------------------------------------------------------------
  888. // Lets the client have a whack at key events
  889. //-----------------------------------------------------------------------------
  890. static bool HandleClientKey( const InputEvent_t &event )
  891. {
  892. // KeyTyped events are not handled by the client
  893. if ( ( event.m_nType == IE_KeyCodeTyped ) || ( event.m_nType == IE_KeyTyped ) )
  894. return false;
  895. bool bDown = event.m_nType != IE_ButtonReleased;
  896. ButtonCode_t code = (ButtonCode_t)event.m_nData;
  897. if ( g_ClientDLL && g_ClientDLL->IN_KeyEvent( bDown ? 1 : 0, code, s_KeyContext.m_pKeyInfo[ code ].m_pKeyBinding ) == 0 )
  898. return true;
  899. return false;
  900. }
  901. //-----------------------------------------------------------------------------
  902. // Lets the new Game UI system have a whack at key events
  903. //-----------------------------------------------------------------------------
  904. static bool HandleGameUIKey( const InputEvent_t &event )
  905. {
  906. if ( g_ClientDLL && g_ClientDLL->HandleGameUIEvent( event ) )
  907. return true;
  908. return false;
  909. }
  910. #endif
  911. //-----------------------------------------------------------------------------
  912. // Lets the engine have a whack at key events
  913. //-----------------------------------------------------------------------------
  914. static bool HandleEngineKey( const InputEvent_t &event )
  915. {
  916. // KeyTyped events are not handled by the client
  917. if ( ( event.m_nType == IE_KeyCodeTyped ) || ( event.m_nType == IE_KeyTyped ) )
  918. return false;
  919. bool bDown = event.m_nType != IE_ButtonReleased;
  920. ButtonCode_t code = (ButtonCode_t)event.m_nData;
  921. // Warn about unbound keys
  922. if ( IsPC() && bDown )
  923. {
  924. if ( IsJoystickCode( code ) &&
  925. !IsJoystickAxisCode( code ) &&
  926. !s_KeyContext.m_pKeyInfo[code].m_pKeyBinding )
  927. {
  928. ConDMsg( "[joy %d]%s is unbound.\n", GetJoystickForCode( code ), g_pInputSystem->ButtonCodeToString( code ) );
  929. }
  930. }
  931. // key up events only generate commands if the game key binding is
  932. // a button command (leading + sign). These will occur even in console mode,
  933. // to keep the character from continuing an action started before a console
  934. // switch. Button commands include the kenum as a parameter, so multiple
  935. // downs can be matched with ups
  936. char *kb = s_KeyContext.m_pKeyInfo[ code ].m_pKeyBinding;
  937. if ( !kb || !kb[0] )
  938. return false;
  939. #if !defined( DEDICATED )
  940. // Prevent keybindings from doing funky stuff unless in game (except toggleconsole)
  941. if ( Q_stricmp( kb, "toggleconsole" ) )
  942. {
  943. extern IVEngineClient *engineClient;
  944. if ( engineClient && !engineClient->IsConnected() )
  945. return false; // prevent the keybinding from being processed without a game
  946. }
  947. #endif
  948. char cmd[1024];
  949. if ( !bDown )
  950. {
  951. if ( kb[0] == '+' )
  952. {
  953. Q_snprintf( cmd, sizeof( cmd ), "-%s %i\n", kb+1, code );
  954. Cbuf_AddText( Cbuf_GetCurrentPlayer(), cmd, kCommandSrcUserInput );
  955. return true;
  956. }
  957. return false;
  958. }
  959. // Send to the interpreter
  960. if (kb[0] == '+')
  961. {
  962. // button commands add keynum as a parm
  963. Q_snprintf( cmd, sizeof( cmd ), "%s %i\n", kb, code );
  964. Cbuf_AddText( Cbuf_GetCurrentPlayer(), cmd, kCommandSrcUserInput );
  965. return true;
  966. }
  967. // Swallow console toggle if any modifier keys are down if it's bound to toggleconsole (the default)
  968. if ( !Q_stricmp( kb, "toggleconsole" ) )
  969. {
  970. if ( s_KeyContext.m_pKeyInfo[KEY_LALT].m_bKeyDown || s_KeyContext.m_pKeyInfo[KEY_LSHIFT].m_bKeyDown || s_KeyContext.m_pKeyInfo[KEY_LCONTROL].m_bKeyDown ||
  971. s_KeyContext.m_pKeyInfo[KEY_RALT].m_bKeyDown || s_KeyContext.m_pKeyInfo[KEY_RSHIFT].m_bKeyDown || s_KeyContext.m_pKeyInfo[KEY_RCONTROL].m_bKeyDown )
  972. return false;
  973. }
  974. Cbuf_AddText( Cbuf_GetCurrentPlayer(), kb, kCommandSrcUserInput );
  975. return true;
  976. }
  977. //-----------------------------------------------------------------------------
  978. // Helper function to make sure key down/key up events go to the right places
  979. //-----------------------------------------------------------------------------
  980. typedef bool (*FilterKeyFunc_t)( const InputEvent_t &event );
  981. static bool FilterKey( const InputEvent_t &event, KeyUpTarget_t target, FilterKeyFunc_t func )
  982. {
  983. // Don't pass the key up or keytyped messages to this target if some other system wants it
  984. ButtonCode_t code = (ButtonCode_t)event.m_nData;
  985. bool bFilterableEvent = ( event.m_nType == IE_ButtonReleased ) ||
  986. ( event.m_nType == IE_KeyTyped ) || ( event.m_nType == IE_KeyCodeTyped );
  987. if ( bFilterableEvent && !ShouldPassKeyUpToTarget( code, target ) )
  988. return false;
  989. // Try to process the input message
  990. bool bFiltered = func( event );
  991. // If we filtered it, then we need to get the key up event
  992. if ( ( event.m_nType == IE_ButtonPressed ) || ( event.m_nType == IE_ButtonDoubleClicked ) )
  993. {
  994. if ( bFiltered )
  995. {
  996. Assert( s_KeyContext.m_pKeyInfo[code].m_nKeyUpTarget == KEY_UP_ANYTARGET );
  997. s_KeyContext.m_pKeyInfo[code].m_nKeyUpTarget = target;
  998. }
  999. }
  1000. else if ( event.m_nType == IE_ButtonReleased ) // Up case
  1001. {
  1002. if ( s_KeyContext.m_pKeyInfo[code].m_nKeyUpTarget == target )
  1003. {
  1004. s_KeyContext.m_pKeyInfo[code].m_nKeyUpTarget = KEY_UP_ANYTARGET;
  1005. bFiltered = true;
  1006. }
  1007. else
  1008. {
  1009. // NOTE: It is illegal to trap up key events. The system will do it for us
  1010. Assert( !bFiltered );
  1011. }
  1012. }
  1013. // We do nothing special for the KeyTyped/KeyCodeTyped cases; they do not change state
  1014. return bFiltered;
  1015. }
  1016. inline bool IsESC( const InputEvent_t &event )
  1017. {
  1018. switch ( event.m_nType )
  1019. {
  1020. case IE_ButtonPressed:
  1021. case IE_ButtonReleased:
  1022. case IE_ButtonDoubleClicked:
  1023. case IE_KeyCodeTyped:
  1024. {
  1025. return ( (ButtonCode_t)event.m_nData == KEY_ESCAPE );
  1026. }
  1027. break;
  1028. case IE_KeyTyped:
  1029. {
  1030. // ASCII Escape character
  1031. return ( event.m_nData == 27 );
  1032. }
  1033. break;
  1034. }
  1035. return false;
  1036. }
  1037. void Key_Event( const InputEvent_t &event )
  1038. {
  1039. #ifdef DEDICATED
  1040. return;
  1041. #else
  1042. ASSERT_NO_REENTRY();
  1043. ButtonCode_t code = (ButtonCode_t)event.m_nData;
  1044. int nSplitScreenPlayerSlot = 0;
  1045. if ( !IsJoystickCode( code ) && !IsSteamControllerCode( code ) )
  1046. {
  1047. if ( splitscreen->IsValidSplitScreenSlot( in_forceuser.GetInt() ) )
  1048. {
  1049. nSplitScreenPlayerSlot = in_forceuser.GetInt();
  1050. }
  1051. }
  1052. else
  1053. {
  1054. nSplitScreenPlayerSlot = GetJoystickForCode( code );
  1055. }
  1056. ACTIVE_SPLITSCREEN_PLAYER_GUARD( nSplitScreenPlayerSlot );
  1057. // Don't handle key ups if the key's already up.
  1058. // NOTE: This should already be taken care of by the input system
  1059. // NOTE ALSO: KeyTyped and KeyCodeTyped messages are going to come through here also
  1060. // Only the VGui and the new GameUI system should accept them; and only if
  1061. // they accepted the keydown event
  1062. if ( ( event.m_nType != IE_KeyCodeTyped ) && ( event.m_nType != IE_KeyTyped ) )
  1063. {
  1064. bool bDown = ( event.m_nType == IE_ButtonPressed ) || ( event.m_nType == IE_ButtonDoubleClicked );
  1065. Assert( s_KeyContext.m_pKeyInfo[code].m_bKeyDown != bDown );
  1066. if ( s_KeyContext.m_pKeyInfo[code].m_bKeyDown == bDown )
  1067. return;
  1068. s_KeyContext.m_pKeyInfo[code].m_bKeyDown = bDown;
  1069. // Deal with trapped keys
  1070. if ( FilterTrappedKey( code, bDown ) )
  1071. return;
  1072. }
  1073. // Make sure vgui is initialzied
  1074. if ( !EngineVGui()->IsInitialized() )
  1075. return;
  1076. // Keep vgui's notion of which keys are down up-to-date regardless of filtering
  1077. // Necessary because vgui has multiple input contexts, so vgui can't directly
  1078. // ask the input system for this information.
  1079. // QUESTION: Should GameUI do the same thing?
  1080. EngineVGui()->UpdateButtonState( event );
  1081. if ( IsPC() && EngineVGui()->IsGameUIVisible() && scr_drawloading && IsESC( event ) )
  1082. {
  1083. // prevent ESC key during loading
  1084. return;
  1085. }
  1086. // Let tools have a whack at keys
  1087. if ( FilterKey( event, KEY_UP_TOOLS, HandleToolKey ) )
  1088. return;
  1089. // If we happen to be checking the MAGICAL ESC key then
  1090. // let's have the client check first since we do magical things
  1091. // with this key otherwise.
  1092. // [jason] Bypass the ButtonPress event for the BACKQUOTE key ("~") so that HandleGameUIKey can intercept it and bring up the Console Window
  1093. if ( !IsESC( event ) && !(event.m_nType == IE_ButtonPressed && code == KEY_BACKQUOTE) )
  1094. {
  1095. // Let vgui have a whack at keys
  1096. if ( FilterKey( event, KEY_UP_VGUI, HandleVGuiKey ) )
  1097. return;
  1098. #if defined( INCLUDE_SCALEFORM )
  1099. // scaleform goes first
  1100. if ( FilterKey( event, KEY_UP_SCALEFORM, HandleScaleformKey ) )
  1101. return;
  1102. #endif
  1103. }
  1104. #if defined ( CSTRIKE15 )
  1105. else if ( g_ClientDLL->IsChatRaised() || g_ClientDLL->IsBindMenuRaised() )
  1106. {
  1107. if ( FilterKey( event, KEY_UP_SCALEFORM, HandleScaleformKey ) )
  1108. return;
  1109. }
  1110. #endif
  1111. // Let the new GameUI system have a whack at keys
  1112. if ( FilterKey( event, KEY_UP_GAMEUI, HandleGameUIKey ) )
  1113. return;
  1114. // Let the client have a whack at keys
  1115. if ( FilterKey( event, KEY_UP_CLIENT, HandleClientKey ) )
  1116. return;
  1117. // Ok the client wants nothing to do with the magical ESC key
  1118. // let's see if VGUI wants to do something with it.
  1119. if ( IsESC( event ) )
  1120. {
  1121. #if defined( INCLUDE_SCALEFORM )
  1122. bool bAllowScaleformFilter = true;
  1123. static ConVarRef cv_console_window_open( "console_window_open" );
  1124. static ConVarRef cv_server_browser_dialog_open( "server_browser_dialog_open" );
  1125. if ( ( cv_console_window_open.GetBool() ) ||
  1126. ( cv_server_browser_dialog_open.GetBool() ) ||
  1127. ( EngineSoundClient() && EngineSoundClient()->IsMoviePlaying() ) )
  1128. {
  1129. // make closing the console window, server browser dialog, or exiting a movie priority
  1130. bAllowScaleformFilter = false;
  1131. }
  1132. if ( bAllowScaleformFilter && FilterKey( event, KEY_UP_SCALEFORM, HandleScaleformKey ) )
  1133. return;
  1134. #endif // INCLUDE_SCALEFORM
  1135. // Let vgui have a whack at keys
  1136. if ( FilterKey( event, KEY_UP_VGUI, HandleVGuiKey ) )
  1137. return;
  1138. }
  1139. // Finally, let the engine deal. Here's where keybindings occur.
  1140. FilterKey( event, KEY_UP_ENGINE, HandleEngineKey );
  1141. #endif
  1142. }