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.

524 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implementation of the VGUI ISurface interface using the
  4. // material system to implement it
  5. //
  6. //===========================================================================//
  7. #if defined( WIN32 ) && !defined( _X360 )
  8. #include <windows.h>
  9. #include <zmouse.h>
  10. #endif
  11. #include "inputsystem/iinputsystem.h"
  12. #include "tier2/tier2.h"
  13. #include "Input.h"
  14. #include "vguimatsurface.h"
  15. #include "../vgui2/src/VPanel.h"
  16. #include <vgui/KeyCode.h>
  17. #include <vgui/MouseCode.h>
  18. #include <vgui/IVGui.h>
  19. #include <vgui/IPanel.h>
  20. #include <vgui/ISurface.h>
  21. #include <vgui/IClientPanel.h>
  22. #include "inputsystem/ButtonCode.h"
  23. #include "Cursor.h"
  24. #include "tier0/dbg.h"
  25. #include "../vgui2/src/vgui_key_translation.h"
  26. #include <vgui/IInputInternal.h>
  27. #include "tier0/icommandline.h"
  28. #ifdef _X360
  29. #include "xbox/xbox_win32stubs.h"
  30. #endif
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. using namespace vgui;
  34. //-----------------------------------------------------------------------------
  35. // Vgui input events
  36. //-----------------------------------------------------------------------------
  37. enum VguiInputEventType_t
  38. {
  39. IE_Close = IE_FirstVguiEvent,
  40. IE_LocateMouseClick,
  41. IE_SetCursor,
  42. IE_KeyTyped,
  43. IE_KeyCodeTyped,
  44. IE_InputLanguageChanged,
  45. IE_IMESetWindow,
  46. IE_IMEStartComposition,
  47. IE_IMEComposition,
  48. IE_IMEEndComposition,
  49. IE_IMEShowCandidates,
  50. IE_IMEChangeCandidates,
  51. IE_IMECloseCandidates,
  52. IE_IMERecomputeModes,
  53. };
  54. void InitInput()
  55. {
  56. EnableInput( true );
  57. }
  58. static bool s_bInputEnabled = true;
  59. #ifdef WIN32
  60. //-----------------------------------------------------------------------------
  61. // Translates actual keys into VGUI ids
  62. //-----------------------------------------------------------------------------
  63. static WNDPROC s_ChainedWindowProc = NULL;
  64. extern HWND thisWindow;
  65. //-----------------------------------------------------------------------------
  66. // Initializes the input system
  67. //-----------------------------------------------------------------------------
  68. static bool s_bIMEComposing = false;
  69. static HWND s_hLastHWnd = 0;
  70. //-----------------------------------------------------------------------------
  71. // Handles input messages
  72. //-----------------------------------------------------------------------------
  73. static LRESULT CALLBACK MatSurfaceWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
  74. {
  75. if ( !s_bInputEnabled )
  76. goto chainWndProc;
  77. InputEvent_t event;
  78. memset( &event, 0, sizeof(event) );
  79. event.m_nTick = g_pInputSystem->GetPollTick();
  80. if ( hwnd != s_hLastHWnd )
  81. {
  82. s_hLastHWnd = hwnd;
  83. event.m_nType = IE_IMESetWindow;
  84. event.m_nData = (int)s_hLastHWnd;
  85. g_pInputSystem->PostUserEvent( event );
  86. }
  87. switch(uMsg)
  88. {
  89. case WM_QUIT:
  90. // According to windows docs, WM_QUIT should never be passed to wndprocs
  91. Assert( 0 );
  92. break;
  93. case WM_CLOSE:
  94. // Handle close messages
  95. {
  96. LONG_PTR wndProc = GetWindowLongPtrW( hwnd, GWLP_WNDPROC );
  97. if ( wndProc == (LONG_PTR)MatSurfaceWindowProc )
  98. {
  99. event.m_nType = IE_Close;
  100. g_pInputSystem->PostUserEvent( event );
  101. }
  102. }
  103. return 0;
  104. // All mouse messages need to mark where the click occurred before chaining down
  105. case WM_LBUTTONDOWN:
  106. case WM_RBUTTONDOWN:
  107. case WM_MBUTTONDOWN:
  108. case MS_WM_XBUTTONDOWN:
  109. case WM_LBUTTONUP:
  110. case WM_RBUTTONUP:
  111. case WM_MBUTTONUP:
  112. case MS_WM_XBUTTONUP:
  113. case WM_LBUTTONDBLCLK:
  114. case WM_RBUTTONDBLCLK:
  115. case WM_MBUTTONDBLCLK:
  116. case MS_WM_XBUTTONDBLCLK:
  117. event.m_nType = IE_LocateMouseClick;
  118. event.m_nData = (short)LOWORD(lParam);
  119. event.m_nData2 = (short)HIWORD(lParam);
  120. g_pInputSystem->PostUserEvent( event );
  121. break;
  122. case WM_SETCURSOR:
  123. event.m_nType = IE_SetCursor;
  124. g_pInputSystem->PostUserEvent( event );
  125. break;
  126. case WM_XCONTROLLER_KEY:
  127. if ( IsX360() )
  128. {
  129. // First have to insert the edge case event
  130. int nRetVal = 0;
  131. if ( s_ChainedWindowProc )
  132. {
  133. nRetVal = CallWindowProcW( s_ChainedWindowProc, hwnd, uMsg, wParam, lParam );
  134. }
  135. // xboxissue - as yet HL2 input hasn't been made aware of analog inputs or ports
  136. // so just digital step on the sample range
  137. int sample = LOWORD( lParam );
  138. if ( sample )
  139. {
  140. event.m_nType = IE_KeyCodeTyped;
  141. event.m_nData = (vgui::KeyCode)wParam;
  142. g_pInputSystem->PostUserEvent( event );
  143. }
  144. }
  145. break;
  146. // Need to deal with key repeat for keydown since inputsystem doesn't
  147. case WM_KEYDOWN:
  148. case WM_SYSKEYDOWN:
  149. {
  150. // First have to insert the edge case event
  151. int nRetVal = 0;
  152. if ( s_ChainedWindowProc )
  153. {
  154. nRetVal = CallWindowProcW( s_ChainedWindowProc, hwnd, uMsg, wParam, lParam );
  155. }
  156. int nKeyRepeat = LOWORD( lParam );
  157. for ( int i = 0; i < nKeyRepeat; ++i )
  158. {
  159. event.m_nType = IE_KeyCodeTyped;
  160. event.m_nData = KeyCode_VirtualKeyToVGUI( wParam );
  161. g_pInputSystem->PostUserEvent( event );
  162. }
  163. return nRetVal;
  164. }
  165. case WM_SYSCHAR:
  166. case WM_CHAR:
  167. if ( !s_bIMEComposing )
  168. {
  169. event.m_nType = IE_KeyTyped;
  170. event.m_nData = (int)wParam;
  171. g_pInputSystem->PostUserEvent( event );
  172. }
  173. break;
  174. case WM_INPUTLANGCHANGE:
  175. event.m_nType = IE_InputLanguageChanged;
  176. g_pInputSystem->PostUserEvent( event );
  177. break;
  178. case WM_IME_STARTCOMPOSITION:
  179. s_bIMEComposing = true;
  180. event.m_nType = IE_IMEStartComposition;
  181. g_pInputSystem->PostUserEvent( event );
  182. return TRUE;
  183. case WM_IME_COMPOSITION:
  184. event.m_nType = IE_IMEComposition;
  185. event.m_nData = (int)lParam;
  186. g_pInputSystem->PostUserEvent( event );
  187. return TRUE;
  188. case WM_IME_ENDCOMPOSITION:
  189. s_bIMEComposing = false;
  190. event.m_nType = IE_IMEEndComposition;
  191. g_pInputSystem->PostUserEvent( event );
  192. return TRUE;
  193. case WM_IME_NOTIFY:
  194. {
  195. switch (wParam)
  196. {
  197. default:
  198. break;
  199. case 14: // Chinese Traditional IMN_PRIVATE...
  200. break;
  201. case IMN_OPENCANDIDATE:
  202. event.m_nType = IE_IMEShowCandidates;
  203. g_pInputSystem->PostUserEvent( event );
  204. return 1;
  205. case IMN_CHANGECANDIDATE:
  206. event.m_nType = IE_IMEChangeCandidates;
  207. g_pInputSystem->PostUserEvent( event );
  208. return 0;
  209. case IMN_CLOSECANDIDATE:
  210. event.m_nType = IE_IMECloseCandidates;
  211. g_pInputSystem->PostUserEvent( event );
  212. break;
  213. // To detect the change of IME mode, or the toggling of Japanese IME
  214. case IMN_SETCONVERSIONMODE:
  215. case IMN_SETSENTENCEMODE:
  216. case IMN_SETOPENSTATUS:
  217. event.m_nType = IE_IMERecomputeModes;
  218. g_pInputSystem->PostUserEvent( event );
  219. if ( wParam == IMN_SETOPENSTATUS )
  220. return 0;
  221. break;
  222. case IMN_CLOSESTATUSWINDOW:
  223. case IMN_GUIDELINE:
  224. case IMN_OPENSTATUSWINDOW:
  225. case IMN_SETCANDIDATEPOS:
  226. case IMN_SETCOMPOSITIONFONT:
  227. case IMN_SETCOMPOSITIONWINDOW:
  228. case IMN_SETSTATUSWINDOWPOS:
  229. break;
  230. }
  231. }
  232. case WM_IME_SETCONTEXT:
  233. // We draw all IME windows ourselves
  234. lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  235. lParam &= ~ISC_SHOWUIGUIDELINE;
  236. lParam &= ~ISC_SHOWUIALLCANDIDATEWINDOW;
  237. break;
  238. case WM_IME_CHAR:
  239. // We need to process this message so that the IME doesn't double convert the unicode IME characters into garbage characters and post
  240. // them to our window... (get ? marks after text entry ).
  241. return 0;
  242. }
  243. chainWndProc:
  244. if ( s_ChainedWindowProc )
  245. return CallWindowProcW( s_ChainedWindowProc, hwnd, uMsg, wParam, lParam );
  246. // This means the application is driving the messages (calling our window procedure manually)
  247. // rather than us hooking their window procedure. The engine needs to do this in order for VCR
  248. // mode to play back properly.
  249. return 0;
  250. }
  251. #endif
  252. //-----------------------------------------------------------------------------
  253. // Enables/disables input (enabled by default)
  254. //-----------------------------------------------------------------------------
  255. void EnableInput( bool bEnable )
  256. {
  257. #if 0 // #ifdef BENCHMARK
  258. s_bInputEnabled = false;
  259. #else
  260. s_bInputEnabled = bEnable;
  261. #endif
  262. }
  263. #ifdef WIN32
  264. //-----------------------------------------------------------------------------
  265. // Hooks input listening up to a window
  266. //-----------------------------------------------------------------------------
  267. void InputAttachToWindow(void *hwnd)
  268. {
  269. #if !defined( USE_SDL )
  270. s_ChainedWindowProc = (WNDPROC)GetWindowLongPtrW( (HWND)hwnd, GWLP_WNDPROC );
  271. SetWindowLongPtrW( (HWND)hwnd, GWLP_WNDPROC, (LONG_PTR)MatSurfaceWindowProc );
  272. #endif
  273. }
  274. void InputDetachFromWindow(void *hwnd)
  275. {
  276. if (!hwnd)
  277. return;
  278. if ( s_ChainedWindowProc )
  279. {
  280. SetWindowLongPtrW( (HWND)hwnd, GWLP_WNDPROC, (LONG_PTR) s_ChainedWindowProc );
  281. s_ChainedWindowProc = NULL;
  282. }
  283. }
  284. #else
  285. void InputAttachToWindow(void *hwnd)
  286. {
  287. #if !defined( OSX ) && !defined( LINUX )
  288. if ( hwnd && !HushAsserts() )
  289. {
  290. // under OSX we use the Cocoa mgr to route events rather than hooking winprocs
  291. // and under Linux we use SDL
  292. Assert( !"Implement me" );
  293. }
  294. #endif
  295. }
  296. void InputDetachFromWindow(void *hwnd)
  297. {
  298. #if !defined( OSX ) && !defined( LINUX )
  299. if ( hwnd && !HushAsserts() )
  300. {
  301. // under OSX we use the Cocoa mgr to route events rather than hooking winprocs
  302. // and under Linux we use SDL
  303. Assert( !"Implement me" );
  304. }
  305. #endif
  306. }
  307. #endif
  308. //-----------------------------------------------------------------------------
  309. // Converts an input system button code to a vgui key code
  310. // FIXME: Remove notion of vgui::KeyCode + vgui::MouseCode altogether
  311. //-----------------------------------------------------------------------------
  312. static vgui::KeyCode ButtonCodeToKeyCode( ButtonCode_t buttonCode )
  313. {
  314. return ( vgui::KeyCode )buttonCode;
  315. }
  316. static vgui::MouseCode ButtonCodeToMouseCode( ButtonCode_t buttonCode )
  317. {
  318. return ( vgui::MouseCode )buttonCode;
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Handles an input event, returns true if the event should be filtered
  322. // from the rest of the game
  323. //-----------------------------------------------------------------------------
  324. bool InputHandleInputEvent( const InputEvent_t &event )
  325. {
  326. switch( event.m_nType )
  327. {
  328. case IE_ButtonPressed:
  329. {
  330. // NOTE: data2 is the virtual key code (data1 contains the scan-code one)
  331. ButtonCode_t code = (ButtonCode_t)event.m_nData2;
  332. if ( IsKeyCode( code ) || IsJoystickCode( code ) )
  333. {
  334. vgui::KeyCode keyCode = ButtonCodeToKeyCode( code );
  335. return g_pIInput->InternalKeyCodePressed( keyCode );
  336. }
  337. if ( IsJoystickCode( code ) )
  338. {
  339. vgui::KeyCode keyCode = ButtonCodeToKeyCode( code );
  340. return g_pIInput->InternalKeyCodePressed( keyCode );
  341. }
  342. if ( IsMouseCode( code ) )
  343. {
  344. vgui::MouseCode mouseCode = ButtonCodeToMouseCode( code );
  345. return g_pIInput->InternalMousePressed( mouseCode );
  346. }
  347. }
  348. break;
  349. case IE_ButtonReleased:
  350. {
  351. // NOTE: data2 is the virtual key code (data1 contains the scan-code one)
  352. ButtonCode_t code = (ButtonCode_t)event.m_nData2;
  353. if ( IsKeyCode( code ) || IsJoystickCode( code ) )
  354. {
  355. vgui::KeyCode keyCode = ButtonCodeToKeyCode( code );
  356. return g_pIInput->InternalKeyCodeReleased( keyCode );
  357. }
  358. if ( IsJoystickCode( code ) )
  359. {
  360. vgui::KeyCode keyCode = ButtonCodeToKeyCode( code );
  361. return g_pIInput->InternalKeyCodeReleased( keyCode );
  362. }
  363. if ( IsMouseCode( code ) )
  364. {
  365. vgui::MouseCode mouseCode = ButtonCodeToMouseCode( code );
  366. return g_pIInput->InternalMouseReleased( mouseCode );
  367. }
  368. }
  369. break;
  370. case IE_ButtonDoubleClicked:
  371. {
  372. // NOTE: data2 is the virtual key code (data1 contains the scan-code one)
  373. ButtonCode_t code = (ButtonCode_t)event.m_nData2;
  374. if ( IsMouseCode( code ) )
  375. {
  376. vgui::MouseCode mouseCode = ButtonCodeToMouseCode( code );
  377. return g_pIInput->InternalMouseDoublePressed( mouseCode );
  378. }
  379. }
  380. break;
  381. case IE_AnalogValueChanged:
  382. {
  383. if ( event.m_nData == MOUSE_WHEEL )
  384. return g_pIInput->InternalMouseWheeled( event.m_nData3 );
  385. if ( event.m_nData == MOUSE_XY )
  386. return g_pIInput->InternalCursorMoved( event.m_nData2, event.m_nData3 );
  387. }
  388. break;
  389. case IE_KeyCodeTyped:
  390. {
  391. vgui::KeyCode code = (vgui::KeyCode)event.m_nData;
  392. g_pIInput->InternalKeyCodeTyped( code );
  393. }
  394. return true;
  395. case IE_KeyTyped:
  396. {
  397. vgui::KeyCode code = (vgui::KeyCode)event.m_nData;
  398. g_pIInput->InternalKeyTyped( code );
  399. }
  400. return true;
  401. case IE_Quit:
  402. g_pVGui->Stop();
  403. #if defined( USE_SDL )
  404. return false; // also let higher layers consume it
  405. #else
  406. return true;
  407. #endif
  408. case IE_Close:
  409. // FIXME: Change this so we don't stop until 'save' occurs, etc.
  410. g_pVGui->Stop();
  411. return true;
  412. case IE_SetCursor:
  413. ActivateCurrentCursor();
  414. return true;
  415. case IE_IMESetWindow:
  416. g_pIInput->SetIMEWindow( (void *)event.m_nData );
  417. return true;
  418. case IE_LocateMouseClick:
  419. g_pIInput->InternalCursorMoved( event.m_nData, event.m_nData2 );
  420. return true;
  421. case IE_InputLanguageChanged:
  422. g_pIInput->OnInputLanguageChanged();
  423. return true;
  424. case IE_IMEStartComposition:
  425. g_pIInput->OnIMEStartComposition();
  426. return true;
  427. case IE_IMEComposition:
  428. g_pIInput->OnIMEComposition( event.m_nData );
  429. return true;
  430. case IE_IMEEndComposition:
  431. g_pIInput->OnIMEEndComposition();
  432. return true;
  433. case IE_IMEShowCandidates:
  434. g_pIInput->OnIMEShowCandidates();
  435. return true;
  436. case IE_IMEChangeCandidates:
  437. g_pIInput->OnIMEChangeCandidates();
  438. return true;
  439. case IE_IMECloseCandidates:
  440. g_pIInput->OnIMECloseCandidates();
  441. return true;
  442. case IE_IMERecomputeModes:
  443. g_pIInput->OnIMERecomputeModes();
  444. return true;
  445. }
  446. return false;
  447. }