summaryrefslogtreecommitdiff
path: root/inputsystem
diff options
context:
space:
mode:
Diffstat (limited to 'inputsystem')
-rw-r--r--inputsystem/Xbox/xbox.def3
-rw-r--r--inputsystem/inputsystem.cpp1550
-rw-r--r--inputsystem/inputsystem.h469
-rw-r--r--inputsystem/inputsystem.vpc55
-rw-r--r--inputsystem/joystick_sdl.cpp615
-rw-r--r--inputsystem/key_translation.cpp911
-rw-r--r--inputsystem/key_translation.h38
-rw-r--r--inputsystem/novint.cpp138
-rw-r--r--inputsystem/posix_stubs.h75
-rw-r--r--inputsystem/steamcontroller.cpp695
10 files changed, 4549 insertions, 0 deletions
diff --git a/inputsystem/Xbox/xbox.def b/inputsystem/Xbox/xbox.def
new file mode 100644
index 0000000..715750d
--- /dev/null
+++ b/inputsystem/Xbox/xbox.def
@@ -0,0 +1,3 @@
+LIBRARY inputsystem_360.dll
+EXPORTS
+ CreateInterface @1
diff --git a/inputsystem/inputsystem.cpp b/inputsystem/inputsystem.cpp
new file mode 100644
index 0000000..5f11ac7
--- /dev/null
+++ b/inputsystem/inputsystem.cpp
@@ -0,0 +1,1550 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "inputsystem.h"
+#include "key_translation.h"
+#include "inputsystem/ButtonCode.h"
+#include "inputsystem/AnalogCode.h"
+#include "tier0/etwprof.h"
+#include "tier1/convar.h"
+#include "tier0/icommandline.h"
+
+#if defined( USE_SDL )
+#undef M_PI
+#include "SDL.h"
+static void initKeymap(void);
+#endif
+
+#ifdef _X360
+#include "xbox/xbox_win32stubs.h"
+#endif
+ConVar joy_xcontroller_found( "joy_xcontroller_found", "1", FCVAR_HIDDEN, "Automatically set to 1 if an xcontroller has been detected." );
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+static CInputSystem g_InputSystem;
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CInputSystem, IInputSystem,
+ INPUTSYSTEM_INTERFACE_VERSION, g_InputSystem );
+
+
+
+#if defined( WIN32 ) && !defined( _X360 )
+typedef BOOL (WINAPI *RegisterRawInputDevices_t)
+(
+ PCRAWINPUTDEVICE pRawInputDevices,
+ UINT uiNumDevices,
+ UINT cbSize
+);
+
+typedef UINT (WINAPI *GetRawInputData_t)
+(
+ HRAWINPUT hRawInput,
+ UINT uiCommand,
+ LPVOID pData,
+ PUINT pcbSize,
+ UINT cbSizeHeader
+);
+
+RegisterRawInputDevices_t pfnRegisterRawInputDevices;
+GetRawInputData_t pfnGetRawInputData;
+#endif
+
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CInputSystem::CInputSystem()
+{
+ m_nLastPollTick = m_nLastSampleTick = m_StartupTimeTick = 0;
+ m_ChainedWndProc = 0;
+ m_hAttachedHWnd = 0;
+ m_hEvent = NULL;
+ m_bEnabled = true;
+ m_bPumpEnabled = true;
+ m_bIsPolling = false;
+ m_JoysticksEnabled.ClearAllFlags();
+ m_nJoystickCount = 0;
+ m_bJoystickInitialized = false;
+ m_nPollCount = 0;
+ m_PrimaryUserId = INVALID_USER_ID;
+ m_uiMouseWheel = 0;
+ m_bXController = false;
+ m_bRawInputSupported = false;
+ m_bSteamController = false;
+ m_bSteamControllerActionsInitialized = false;
+ m_bSteamControllerActive = false;
+
+ Assert( (MAX_JOYSTICKS + 7) >> 3 << sizeof(unsigned short) );
+
+ m_pXInputDLL = NULL;
+ m_pRawInputDLL = NULL;
+
+#if defined ( _WIN32 ) && !defined ( _X360 )
+ // NVNT DLL
+ m_pNovintDLL = NULL;
+#endif
+
+ m_bConsoleTextMode = false;
+ m_bSkipControllerInitialization = false;
+
+ if ( CommandLine()->CheckParm( "-nosteamcontroller" ) )
+ {
+ m_bSkipControllerInitialization = true;
+ }
+}
+
+CInputSystem::~CInputSystem()
+{
+ if ( m_pXInputDLL )
+ {
+ Sys_UnloadModule( m_pXInputDLL );
+ m_pXInputDLL = NULL;
+ }
+
+ if ( m_pRawInputDLL )
+ {
+ Sys_UnloadModule( m_pRawInputDLL );
+ m_pRawInputDLL = NULL;
+ }
+
+#if defined ( _WIN32 ) && !defined ( _X360 )
+ // NVNT DLL unload
+ if ( m_pNovintDLL )
+ {
+ Sys_UnloadModule( m_pNovintDLL );
+ m_pNovintDLL = NULL;
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+InitReturnVal_t CInputSystem::Init()
+{
+ InitReturnVal_t nRetVal = BaseClass::Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ m_StartupTimeTick = Plat_MSTime();
+
+
+#if !defined( POSIX )
+ if ( IsPC() )
+ {
+ m_uiMouseWheel = RegisterWindowMessage( "MSWHEEL_ROLLMSG" );
+ }
+
+ m_hEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if ( !m_hEvent )
+ return INIT_FAILED;
+#endif
+
+ // Initialize the input system copy of the steam API context, for use by controller stuff (don't do this if we're a dedicated server).
+ if ( !m_bSkipControllerInitialization && SteamAPI_InitSafe() )
+ {
+ m_SteamAPIContext.Init();
+ if ( m_SteamAPIContext.SteamController() )
+ {
+ m_SteamAPIContext.SteamController()->Init();
+ m_bSteamController = InitializeSteamControllers();
+ m_bSteamControllerActionsInitialized = m_bSteamController && InitializeSteamControllerActionSets();
+ if ( m_bSteamControllerActionsInitialized )
+ {
+ ActivateSteamControllerActionSet( GAME_ACTION_SET_MENUCONTROLS );
+ }
+ }
+ }
+
+ ButtonCode_InitKeyTranslationTable();
+ ButtonCode_UpdateScanCodeLayout();
+
+ joy_xcontroller_found.SetValue( 0 );
+ if ( IsPC() && !m_bConsoleTextMode )
+ {
+ InitializeJoysticks();
+ if ( m_bXController )
+ joy_xcontroller_found.SetValue( 1 );
+
+
+#if defined( PLATFORM_WINDOWS_PC )
+ // NVNT try and load and initialize through the haptic dll, but only if the drivers are installed
+ HMODULE hdl = LoadLibraryEx( "hdl.dll", NULL, LOAD_LIBRARY_AS_DATAFILE );
+
+ if ( hdl )
+ {
+ m_pNovintDLL = Sys_LoadModule( "haptics.dll" );
+ if ( m_pNovintDLL )
+ {
+ InitializeNovintDevices();
+ }
+ FreeLibrary( hdl );
+ }
+#endif
+ }
+
+#if defined( _X360 )
+ SetPrimaryUserId( XBX_GetPrimaryUserId() );
+ InitializeXDevices();
+ m_bXController = true;
+#endif
+
+#if defined( USE_SDL )
+
+ m_bRawInputSupported = true;
+ initKeymap();
+
+#elif defined( WIN32 ) && !defined( _X360 )
+
+ // Check if this version of windows supports raw mouse input (later than win2k)
+ m_bRawInputSupported = false;
+
+ CSysModule *m_pRawInputDLL = Sys_LoadModule( "USER32.dll" );
+ if ( m_pRawInputDLL )
+ {
+ pfnRegisterRawInputDevices = (RegisterRawInputDevices_t)GetProcAddress( (HMODULE)m_pRawInputDLL, "RegisterRawInputDevices" );
+ pfnGetRawInputData = (GetRawInputData_t)GetProcAddress( (HMODULE)m_pRawInputDLL, "GetRawInputData" );
+ if ( pfnRegisterRawInputDevices && pfnGetRawInputData )
+ m_bRawInputSupported = true;
+ }
+
+#endif
+
+ return INIT_OK;
+}
+
+bool CInputSystem::Connect( CreateInterfaceFn factory )
+{
+ if ( !BaseClass::Connect( factory ) )
+ return false;
+
+#if defined( USE_SDL )
+ m_pLauncherMgr = (ILauncherMgr *)factory( SDLMGR_INTERFACE_VERSION, NULL );
+#endif
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Shutdown
+//-----------------------------------------------------------------------------
+void CInputSystem::Shutdown()
+{
+#if !defined( POSIX )
+ if ( m_hEvent != NULL )
+ {
+ CloseHandle( m_hEvent );
+ m_hEvent = NULL;
+ }
+#endif
+
+ if ( IsPC() )
+ {
+ ShutdownJoysticks();
+ }
+
+ BaseClass::Shutdown();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sleep until input
+//-----------------------------------------------------------------------------
+void CInputSystem::SleepUntilInput( int nMaxSleepTimeMS )
+{
+#if defined( _WIN32 ) && !defined( USE_SDL )
+ if ( nMaxSleepTimeMS < 0 )
+ {
+ nMaxSleepTimeMS = INFINITE;
+ }
+
+ MsgWaitForMultipleObjects( 1, &m_hEvent, FALSE, nMaxSleepTimeMS, QS_ALLEVENTS );
+#elif defined( USE_SDL )
+ m_pLauncherMgr->WaitUntilUserInput( nMaxSleepTimeMS );
+#else
+#warning "need a SleepUntilInput impl"
+#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Callback to call into our class
+//-----------------------------------------------------------------------------
+#if defined( PLATFORM_WINDOWS )
+static LRESULT CALLBACK InputSystemWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ return g_InputSystem.WindowProc( hwnd, uMsg, wParam, lParam );
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Hooks input listening up to a window
+//-----------------------------------------------------------------------------
+void CInputSystem::AttachToWindow( void* hWnd )
+{
+ Assert( m_hAttachedHWnd == 0 );
+ if ( m_hAttachedHWnd )
+ {
+ Warning( "CInputSystem::AttachToWindow: Cannot attach to two windows at once!\n" );
+ return;
+ }
+
+#if defined( PLATFORM_WINDOWS )
+ m_ChainedWndProc = (WNDPROC)GetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC );
+ SetWindowLongPtrW( (HWND)hWnd, GWLP_WNDPROC, (LONG_PTR)InputSystemWindowProc );
+#endif
+
+ m_hAttachedHWnd = (HWND)hWnd;
+
+#if defined( PLATFORM_WINDOWS_PC ) && !defined( USE_SDL )
+ // NVNT inform novint devices of window
+ AttachWindowToNovintDevices( hWnd );
+
+ // register to read raw mouse input
+
+#if !defined(HID_USAGE_PAGE_GENERIC)
+#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
+#endif
+#if !defined(HID_USAGE_GENERIC_MOUSE)
+#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
+#endif
+
+ if ( m_bRawInputSupported )
+ {
+ RAWINPUTDEVICE Rid[1];
+ Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
+ Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
+ Rid[0].dwFlags = RIDEV_INPUTSINK;
+ Rid[0].hwndTarget = g_InputSystem.m_hAttachedHWnd; // GetHhWnd;
+ pfnRegisterRawInputDevices(Rid, ARRAYSIZE(Rid), sizeof(Rid[0]));
+ }
+#endif
+
+ // New window, clear input state
+ ClearInputState();
+}
+
+
+//-----------------------------------------------------------------------------
+// Unhooks input listening from a window
+//-----------------------------------------------------------------------------
+void CInputSystem::DetachFromWindow( )
+{
+ if ( !m_hAttachedHWnd )
+ return;
+
+ ResetInputState();
+
+#if defined( PLATFORM_WINDOWS )
+ if ( m_ChainedWndProc )
+ {
+ SetWindowLongPtrW( m_hAttachedHWnd, GWLP_WNDPROC, (LONG_PTR)m_ChainedWndProc );
+ m_ChainedWndProc = 0;
+ }
+#endif
+
+#if defined( PLATFORM_WINDOWS_PC )
+ // NVNT inform novint devices loss of window
+ DetachWindowFromNovintDevices( );
+#endif
+ m_hAttachedHWnd = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Enables/disables input
+//-----------------------------------------------------------------------------
+void CInputSystem::EnableInput( bool bEnable )
+{
+ m_bEnabled = bEnable;
+}
+
+
+//-----------------------------------------------------------------------------
+// Enables/disables the inputsystem windows message pump
+//-----------------------------------------------------------------------------
+void CInputSystem::EnableMessagePump( bool bEnable )
+{
+ m_bPumpEnabled = bEnable;
+}
+
+
+//-----------------------------------------------------------------------------
+// Clears the input state, doesn't generate key-up messages
+//-----------------------------------------------------------------------------
+void CInputSystem::ClearInputState()
+{
+ for ( int i = 0; i < INPUT_STATE_COUNT; ++i )
+ {
+ InputState_t& state = m_InputState[i];
+ state.m_ButtonState.ClearAll();
+ memset( state.m_pAnalogDelta, 0, ANALOG_CODE_LAST * sizeof(int) );
+ memset( state.m_pAnalogValue, 0, ANALOG_CODE_LAST * sizeof(int) );
+ memset( state.m_ButtonPressedTick, 0, BUTTON_CODE_LAST * sizeof(int) );
+ memset( state.m_ButtonReleasedTick, 0, BUTTON_CODE_LAST * sizeof(int) );
+ state.m_Events.Purge();
+ state.m_bDirty = false;
+ }
+ memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) );
+}
+
+//-----------------------------------------------------------------------------
+// Resets the input state
+//-----------------------------------------------------------------------------
+void CInputSystem::ResetInputState()
+{
+ ReleaseAllButtons();
+ ZeroAnalogState( 0, ANALOG_CODE_LAST - 1 );
+ memset( m_appXKeys, 0, XUSER_MAX_COUNT * XK_MAX_KEYS * sizeof(appKey_t) );
+
+ m_mouseRawAccumX = m_mouseRawAccumY = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert back + forth between ButtonCode/AnalogCode + strings
+//-----------------------------------------------------------------------------
+const char *CInputSystem::ButtonCodeToString( ButtonCode_t code ) const
+{
+ return ButtonCode_ButtonCodeToString( code, m_bXController );
+}
+
+const char *CInputSystem::AnalogCodeToString( AnalogCode_t code ) const
+{
+ return AnalogCode_AnalogCodeToString( code );
+}
+
+ButtonCode_t CInputSystem::StringToButtonCode( const char *pString ) const
+{
+ return ButtonCode_StringToButtonCode( pString, m_bXController );
+}
+
+AnalogCode_t CInputSystem::StringToAnalogCode( const char *pString ) const
+{
+ return AnalogCode_StringToAnalogCode( pString );
+}
+
+
+//-----------------------------------------------------------------------------
+// Convert back + forth between virtual codes + button codes
+// FIXME: This is a temporary piece of code
+//-----------------------------------------------------------------------------
+ButtonCode_t CInputSystem::VirtualKeyToButtonCode( int nVirtualKey ) const
+{
+ return ButtonCode_VirtualKeyToButtonCode( nVirtualKey );
+}
+
+int CInputSystem::ButtonCodeToVirtualKey( ButtonCode_t code ) const
+{
+ return ButtonCode_ButtonCodeToVirtualKey( code );
+}
+
+ButtonCode_t CInputSystem::XKeyToButtonCode( int nPort, int nXKey ) const
+{
+ if ( m_bXController )
+ return ButtonCode_XKeyToButtonCode( nPort, nXKey );
+ return KEY_NONE;
+}
+
+ButtonCode_t CInputSystem::ScanCodeToButtonCode( int lParam ) const
+{
+ return ButtonCode_ScanCodeToButtonCode( lParam );
+}
+
+ButtonCode_t CInputSystem::SKeyToButtonCode( int nPort, int nXKey ) const
+{
+ return ButtonCode_SKeyToButtonCode( nPort, nXKey );
+}
+
+//-----------------------------------------------------------------------------
+// Post an event to the queue
+//-----------------------------------------------------------------------------
+void CInputSystem::PostEvent( int nType, int nTick, int nData, int nData2, int nData3 )
+{
+ InputEvent_t event;
+
+ memset( &event, 0, sizeof(event) );
+ event.m_nType = nType;
+ event.m_nTick = nTick;
+ event.m_nData = nData;
+ event.m_nData2 = nData2;
+ event.m_nData3 = nData3;
+
+ PostUserEvent( event );
+}
+
+
+//-----------------------------------------------------------------------------
+// Post an button press event to the queue
+//-----------------------------------------------------------------------------
+void CInputSystem::PostButtonPressedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode )
+{
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ if ( !state.m_ButtonState.IsBitSet( scanCode ) )
+ {
+ // Update button state
+ state.m_ButtonState.Set( scanCode );
+ state.m_ButtonPressedTick[ scanCode ] = nTick;
+
+ // Add this event to the app-visible event queue
+ PostEvent( nType, nTick, scanCode, virtualCode );
+
+#if defined( _X360 )
+ // FIXME: Remove! Fake a windows message for vguimatsurface's input handler
+ if ( IsJoystickCode( scanCode ) )
+ {
+ ProcessEvent( WM_XCONTROLLER_KEY, scanCode, 1 );
+ }
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Post an button release event to the queue
+//-----------------------------------------------------------------------------
+void CInputSystem::PostButtonReleasedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode )
+{
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ if ( state.m_ButtonState.IsBitSet( scanCode ) )
+ {
+ // Update button state
+ state.m_ButtonState.Clear( scanCode );
+ state.m_ButtonReleasedTick[ scanCode ] = nTick;
+
+ // Add this event to the app-visible event queue
+ PostEvent( nType, nTick, scanCode, virtualCode );
+
+#if defined( _X360 )
+ // FIXME: Remove! Fake a windows message for vguimatsurface's input handler
+ if ( IsJoystickCode( scanCode ) )
+ {
+ ProcessEvent( WM_XCONTROLLER_KEY, scanCode, 0 );
+ }
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Pass Joystick button events through the engine's window procs
+//-----------------------------------------------------------------------------
+void CInputSystem::ProcessEvent( UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+#if !defined( POSIX )
+ // To prevent subtle input timing bugs, all button events must be fed
+ // through the window proc once per frame, same as the keyboard and mouse.
+ HWND hWnd = GetFocus();
+ WNDPROC windowProc = (WNDPROC)GetWindowLongPtrW(hWnd, GWLP_WNDPROC );
+ if ( windowProc )
+ {
+ windowProc( hWnd, uMsg, wParam, lParam );
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies the input state record over
+//-----------------------------------------------------------------------------
+void CInputSystem::CopyInputState( InputState_t *pDest, const InputState_t &src, bool bCopyEvents )
+{
+ pDest->m_Events.RemoveAll();
+ pDest->m_bDirty = false;
+ if ( src.m_bDirty )
+ {
+ pDest->m_ButtonState = src.m_ButtonState;
+ memcpy( &pDest->m_ButtonPressedTick, &src.m_ButtonPressedTick, sizeof( pDest->m_ButtonPressedTick ) );
+ memcpy( &pDest->m_ButtonReleasedTick, &src.m_ButtonReleasedTick, sizeof( pDest->m_ButtonReleasedTick ) );
+ memcpy( &pDest->m_pAnalogDelta, &src.m_pAnalogDelta, sizeof( pDest->m_pAnalogDelta ) );
+ memcpy( &pDest->m_pAnalogValue, &src.m_pAnalogValue, sizeof( pDest->m_pAnalogValue ) );
+ if ( bCopyEvents )
+ {
+ if ( src.m_Events.Count() > 0 )
+ {
+ pDest->m_Events.EnsureCount( src.m_Events.Count() );
+ memcpy( pDest->m_Events.Base(), src.m_Events.Base(), src.m_Events.Count() * sizeof(InputEvent_t) );
+ }
+ }
+ }
+}
+
+
+#if defined( PLATFORM_WINDOWS_PC )
+void CInputSystem::PollInputState_Windows()
+{
+ if ( m_bPumpEnabled )
+ {
+ // Poll mouse + keyboard
+ MSG msg;
+ while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ if ( msg.message == WM_QUIT )
+ {
+ PostEvent( IE_Quit, m_nLastSampleTick );
+ break;
+ }
+
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+
+ // NOTE: Under some implementations of Win9x,
+ // dispatching messages can cause the FPU control word to change
+ SetupFPUControlWord();
+ }
+}
+#endif
+
+#if defined( USE_SDL )
+
+static BYTE scantokey[SDL_NUM_SCANCODES];
+
+static void initKeymap(void)
+{
+ memset(scantokey, '\0', sizeof (scantokey));
+
+ for (int i = SDL_SCANCODE_A; i <= SDL_SCANCODE_Z; i++)
+ scantokey[i] = KEY_A + (i - SDL_SCANCODE_A);
+ for (int i = SDL_SCANCODE_1; i <= SDL_SCANCODE_9; i++)
+ scantokey[i] = KEY_1 + (i - SDL_SCANCODE_1);
+ for (int i = SDL_SCANCODE_F1; i <= SDL_SCANCODE_F12; i++)
+ scantokey[i] = KEY_F1 + (i - SDL_SCANCODE_F1);
+ for (int i = SDL_SCANCODE_KP_1; i <= SDL_SCANCODE_KP_9; i++)
+ scantokey[i] = KEY_PAD_1 + (i - SDL_SCANCODE_KP_1);
+
+ scantokey[SDL_SCANCODE_0] = KEY_0;
+ scantokey[SDL_SCANCODE_KP_0] = KEY_PAD_0;
+ scantokey[SDL_SCANCODE_RETURN] = KEY_ENTER;
+ scantokey[SDL_SCANCODE_ESCAPE] = KEY_ESCAPE;
+ scantokey[SDL_SCANCODE_BACKSPACE] = KEY_BACKSPACE;
+ scantokey[SDL_SCANCODE_TAB] = KEY_TAB;
+ scantokey[SDL_SCANCODE_SPACE] = KEY_SPACE;
+ scantokey[SDL_SCANCODE_MINUS] = KEY_MINUS;
+ scantokey[SDL_SCANCODE_EQUALS] = KEY_EQUAL;
+ scantokey[SDL_SCANCODE_LEFTBRACKET] = KEY_LBRACKET;
+ scantokey[SDL_SCANCODE_RIGHTBRACKET] = KEY_RBRACKET;
+ scantokey[SDL_SCANCODE_BACKSLASH] = KEY_BACKSLASH;
+ scantokey[SDL_SCANCODE_SEMICOLON] = KEY_SEMICOLON;
+ scantokey[SDL_SCANCODE_APOSTROPHE] = KEY_APOSTROPHE;
+ scantokey[SDL_SCANCODE_GRAVE] = KEY_BACKQUOTE;
+ scantokey[SDL_SCANCODE_COMMA] = KEY_COMMA;
+ scantokey[SDL_SCANCODE_PERIOD] = KEY_PERIOD;
+ scantokey[SDL_SCANCODE_SLASH] = KEY_SLASH;
+ scantokey[SDL_SCANCODE_CAPSLOCK] = KEY_CAPSLOCK;
+ scantokey[SDL_SCANCODE_SCROLLLOCK] = KEY_SCROLLLOCK;
+ scantokey[SDL_SCANCODE_INSERT] = KEY_INSERT;
+ scantokey[SDL_SCANCODE_HOME] = KEY_HOME;
+ scantokey[SDL_SCANCODE_PAGEUP] = KEY_PAGEUP;
+ scantokey[SDL_SCANCODE_DELETE] = KEY_DELETE;
+ scantokey[SDL_SCANCODE_END] = KEY_END;
+ scantokey[SDL_SCANCODE_PAGEDOWN] = KEY_PAGEDOWN;
+ scantokey[SDL_SCANCODE_RIGHT] = KEY_RIGHT;
+ scantokey[SDL_SCANCODE_LEFT] = KEY_LEFT;
+ scantokey[SDL_SCANCODE_DOWN] = KEY_DOWN;
+ scantokey[SDL_SCANCODE_UP] = KEY_UP;
+ scantokey[SDL_SCANCODE_NUMLOCKCLEAR] = KEY_NUMLOCK;
+ scantokey[SDL_SCANCODE_KP_DIVIDE] = KEY_PAD_DIVIDE;
+ scantokey[SDL_SCANCODE_KP_MULTIPLY] = KEY_PAD_MULTIPLY;
+ scantokey[SDL_SCANCODE_KP_MINUS] = KEY_PAD_MINUS;
+ scantokey[SDL_SCANCODE_KP_PLUS] = KEY_PAD_PLUS;
+ // Map keybad enter to enter for vgui. This means vgui dialog won't ever see KEY_PAD_ENTER
+ scantokey[SDL_SCANCODE_KP_ENTER] = KEY_ENTER;
+ scantokey[SDL_SCANCODE_KP_PERIOD] = KEY_PAD_DECIMAL;
+ scantokey[SDL_SCANCODE_APPLICATION] = KEY_APP;
+ scantokey[SDL_SCANCODE_LCTRL] = KEY_LCONTROL;
+ scantokey[SDL_SCANCODE_LSHIFT] = KEY_LSHIFT;
+ scantokey[SDL_SCANCODE_LALT] = KEY_LALT;
+ scantokey[SDL_SCANCODE_LGUI] = KEY_LWIN;
+ scantokey[SDL_SCANCODE_RCTRL] = KEY_RCONTROL;
+ scantokey[SDL_SCANCODE_RSHIFT] = KEY_RSHIFT;
+ scantokey[SDL_SCANCODE_RALT] = KEY_RALT;
+ scantokey[SDL_SCANCODE_RGUI] = KEY_RWIN;
+}
+
+bool MapCocoaVirtualKeyToButtonCode( int nCocoaVirtualKeyCode, ButtonCode_t *pOut )
+{
+ if ( nCocoaVirtualKeyCode < 0 )
+ *pOut = (ButtonCode_t)(-1 * nCocoaVirtualKeyCode);
+ else
+ {
+ nCocoaVirtualKeyCode &= 0x000000ff;
+
+ *pOut = (ButtonCode_t)scantokey[nCocoaVirtualKeyCode];
+ }
+
+ return true;
+}
+
+void CInputSystem::PollInputState_Platform()
+{
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+
+ if ( m_bPumpEnabled )
+ m_pLauncherMgr->PumpWindowsMessageLoop();
+ // These are Carbon virtual key codes. AFAIK they don't have a header that defines these, but they are supposed to map
+ // to the same letters across international keyboards, so our mapping here should work.
+ CCocoaEvent events[32];
+ while ( 1 )
+ {
+ int nEvents = m_pLauncherMgr->GetEvents( events, ARRAYSIZE( events ) );
+ if ( nEvents == 0 )
+ break;
+
+ for ( int iEvent=0; iEvent < nEvents; iEvent++ )
+ {
+ CCocoaEvent *pEvent = &events[iEvent];
+
+ switch( pEvent->m_EventType )
+ {
+ case CocoaEvent_Deleted:
+ break;
+
+ case CocoaEvent_KeyDown:
+ {
+ ButtonCode_t virtualCode;
+ if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) )
+ {
+ ButtonCode_t scanCode = virtualCode;
+
+ if( ( scanCode != BUTTON_CODE_NONE ) )
+ {
+ // For SDL, hitting spacebar causes a SDL_KEYDOWN event, then SDL_TEXTINPUT with
+ // event.text.text[0] = ' ', and then we get here and wind up sending two events
+ // to PostButtonPressedEvent. The first is virtualCode = ' ', the 2nd has virtualCode = 0.
+ // This will confuse Button::OnKeyCodePressed(), which is checking for space keydown
+ // followed by space keyup. So we ignore all BUTTON_CODE_NONE events here.
+ PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, scanCode, virtualCode );
+ }
+
+ InputEvent_t event;
+ memset( &event, 0, sizeof(event) );
+ event.m_nTick = GetPollTick();
+ // IE_KeyCodeTyped
+ event.m_nType = IE_FirstVguiEvent + 4;
+ event.m_nData = scanCode;
+ g_pInputSystem->PostUserEvent( event );
+
+ }
+
+ if ( !(pEvent->m_ModifierKeyMask & (1<<eCommandKey) ) && pEvent->m_VirtualKeyCode >= 0 && pEvent->m_UnicodeKey > 0 )
+ {
+ InputEvent_t event;
+ memset( &event, 0, sizeof(event) );
+ event.m_nTick = GetPollTick();
+ // IE_KeyTyped
+ event.m_nType = IE_FirstVguiEvent + 3;
+ event.m_nData = (int)pEvent->m_UnicodeKey;
+ g_pInputSystem->PostUserEvent( event );
+ }
+
+ }
+ break;
+
+ case CocoaEvent_KeyUp:
+ {
+ ButtonCode_t virtualCode;
+ if ( MapCocoaVirtualKeyToButtonCode( pEvent->m_VirtualKeyCode, &virtualCode ) )
+ {
+ if( virtualCode != BUTTON_CODE_NONE )
+ {
+ ButtonCode_t scanCode = virtualCode;
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, scanCode, virtualCode );
+ }
+ }
+ }
+ break;
+
+ case CocoaEvent_MouseButtonDown:
+ {
+ int nButtonMask = pEvent->m_MouseButtonFlags;
+ ButtonCode_t dblClickCode = BUTTON_CODE_INVALID;
+ if ( pEvent->m_nMouseClickCount > 1 )
+ {
+ switch( pEvent->m_MouseButton )
+ {
+ default:
+ case COCOABUTTON_LEFT:
+ dblClickCode = MOUSE_LEFT;
+ break;
+ case COCOABUTTON_RIGHT:
+ dblClickCode = MOUSE_RIGHT;
+ break;
+ case COCOABUTTON_MIDDLE:
+ dblClickCode = MOUSE_MIDDLE;
+ break;
+ case COCOABUTTON_4:
+ dblClickCode = MOUSE_4;
+ break;
+ case COCOABUTTON_5:
+ dblClickCode = MOUSE_5;
+ break;
+ }
+ }
+ UpdateMouseButtonState( nButtonMask, dblClickCode );
+ }
+ break;
+
+ case CocoaEvent_MouseButtonUp:
+ {
+ int nButtonMask = pEvent->m_MouseButtonFlags;
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case CocoaEvent_MouseMove:
+ {
+ UpdateMousePositionState( state, (short)pEvent->m_MousePos[0], (short)pEvent->m_MousePos[1] );
+
+ InputEvent_t event;
+ memset( &event, 0, sizeof(event) );
+ event.m_nTick = GetPollTick();
+ // IE_LocateMouseClick
+ event.m_nType = IE_FirstVguiEvent + 1;
+ event.m_nData = (short)pEvent->m_MousePos[0];
+ event.m_nData2 = (short)pEvent->m_MousePos[1];
+ g_pInputSystem->PostUserEvent( event );
+ }
+ break;
+
+ case CocoaEvent_MouseScroll:
+ {
+ ButtonCode_t code = (short)pEvent->m_MousePos[1] > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
+ state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
+ PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
+ PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
+
+ state.m_pAnalogDelta[ MOUSE_WHEEL ] = pEvent->m_MousePos[1];
+ state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
+ }
+ break;
+
+ case CocoaEvent_AppActivate:
+ {
+ InputEvent_t event;
+ memset( &event, 0, sizeof(event) );
+ event.m_nType = IE_FirstAppEvent + 2; // IE_AppActivated (defined in sys_mainwind.cpp).
+ event.m_nData = (bool)(pEvent->m_ModifierKeyMask != 0);
+
+ g_pInputSystem->PostUserEvent( event );
+
+ if( pEvent->m_ModifierKeyMask == 0 )
+ {
+ // App just lost focus. Handle like WM_ACTIVATEAPP in CInputSystem::WindowProc().
+ // Otherwise alt+tab will bring focus away from our app, vgui will still think that
+ // the alt key is down, and when we regain focus, fun ensues.
+ g_pInputSystem->ResetInputState();
+ }
+ }
+ break;
+ case CocoaEvent_AppQuit:
+ {
+ PostEvent( IE_Quit, m_nLastSampleTick );
+
+ }
+ break;
+ break;
+ }
+ }
+ }
+}
+#endif // USE_SDL
+
+
+//-----------------------------------------------------------------------------
+// Polls the current input state
+//-----------------------------------------------------------------------------
+void CInputSystem::PollInputState()
+{
+ m_bIsPolling = true;
+ ++m_nPollCount;
+
+ // Deals with polled input events
+ InputState_t &queuedState = m_InputState[ INPUT_STATE_QUEUED ];
+ CopyInputState( &m_InputState[ INPUT_STATE_CURRENT ], queuedState, true );
+
+ // Sample the joystick
+ SampleDevices();
+
+ // NOTE: This happens after SampleDevices since that updates LastSampleTick
+ // Also, I believe it's correct to post the joystick events with
+ // the LastPollTick not updated (not 100% sure though)
+ m_nLastPollTick = m_nLastSampleTick;
+
+#if defined( PLATFORM_WINDOWS_PC )
+ PollInputState_Windows();
+#endif
+
+#if defined( USE_SDL )
+ PollInputState_Platform();
+#endif
+
+ // Leave the queued state up-to-date with the current
+ CopyInputState( &queuedState, m_InputState[ INPUT_STATE_CURRENT ], false );
+
+ m_bIsPolling = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the sample tick
+//-----------------------------------------------------------------------------
+int CInputSystem::ComputeSampleTick()
+{
+ // This logic will only fail if the app has been running for 49.7 days
+ int nSampleTick;
+
+ DWORD nCurrentTick = Plat_MSTime();
+ if ( nCurrentTick >= m_StartupTimeTick )
+ {
+ nSampleTick = (int)( nCurrentTick - m_StartupTimeTick );
+ }
+ else
+ {
+ DWORD nDelta = (DWORD)0xFFFFFFFF - m_StartupTimeTick;
+ nSampleTick = (int)( nCurrentTick + nDelta ) + 1;
+ }
+ return nSampleTick;
+}
+
+
+//-----------------------------------------------------------------------------
+// How many times has poll been called?
+//-----------------------------------------------------------------------------
+int CInputSystem::GetPollCount() const
+{
+ return m_nPollCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Samples attached devices and appends events to the input queue
+//-----------------------------------------------------------------------------
+void CInputSystem::SampleDevices( void )
+{
+ m_nLastSampleTick = ComputeSampleTick();
+
+ PollJoystick();
+
+#if defined( PLATFORM_WINDOWS_PC )
+ // NVNT if we have device/s poll them.
+ if ( m_bNovintDevices )
+ {
+ PollNovintDevices();
+ }
+#endif
+
+ PollSteamControllers();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets a player as the primary user - all other controllers will be ignored.
+//-----------------------------------------------------------------------------
+void CInputSystem::SetPrimaryUserId( int userId )
+{
+ if ( userId >= XUSER_MAX_COUNT || userId < 0 )
+ {
+ m_PrimaryUserId = INVALID_USER_ID;
+ }
+ else
+ {
+ m_PrimaryUserId = userId;
+ }
+#if !defined(POSIX)
+ XBX_SetPrimaryUserId( m_PrimaryUserId );
+#endif
+ ConMsg("PrimaryUserId is %d\n", m_PrimaryUserId );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Forwards rumble info to attached devices
+//-----------------------------------------------------------------------------
+void CInputSystem::SetRumble( float fLeftMotor, float fRightMotor, int userId )
+{
+ SetXDeviceRumble( fLeftMotor, fRightMotor, userId );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Force an immediate stop, transmits immediately to all devices
+//-----------------------------------------------------------------------------
+void CInputSystem::StopRumble( void )
+{
+#ifdef _X360
+ xdevice_t* pXDevice = &m_XDevices[0];
+
+ for ( int i = 0; i < XUSER_MAX_COUNT; ++i, ++pXDevice )
+ {
+ if ( pXDevice->active )
+ {
+ pXDevice->vibration.wLeftMotorSpeed = 0;
+ pXDevice->vibration.wRightMotorSpeed = 0;
+ pXDevice->pendingRumbleUpdate = true;
+ WriteToXDevice( pXDevice );
+ }
+ }
+#else
+ for ( int i = 0; i < XUSER_MAX_COUNT; ++i )
+ {
+ SetRumble(0.0, 0.0, i);
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Joystick interface
+//-----------------------------------------------------------------------------
+int CInputSystem::GetJoystickCount() const
+{
+ return m_nJoystickCount;
+}
+
+void CInputSystem::EnableJoystickInput( int nJoystick, bool bEnable )
+{
+ m_JoysticksEnabled.SetFlag( 1 << nJoystick, bEnable );
+}
+
+void CInputSystem::EnableJoystickDiagonalPOV( int nJoystick, bool bEnable )
+{
+ m_pJoystickInfo[ nJoystick ].m_bDiagonalPOVControlEnabled = bEnable;
+}
+
+//-----------------------------------------------------------------------------
+// Poll current state
+//-----------------------------------------------------------------------------
+int CInputSystem::GetPollTick() const
+{
+ return m_nLastPollTick;
+}
+
+bool CInputSystem::IsButtonDown( ButtonCode_t code ) const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_ButtonState.IsBitSet( code );
+}
+
+int CInputSystem::GetAnalogValue( AnalogCode_t code ) const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_pAnalogValue[code];
+}
+
+int CInputSystem::GetAnalogDelta( AnalogCode_t code ) const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_pAnalogDelta[code];
+}
+
+int CInputSystem::GetButtonPressedTick( ButtonCode_t code ) const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_ButtonPressedTick[code];
+}
+
+int CInputSystem::GetButtonReleasedTick( ButtonCode_t code ) const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_ButtonReleasedTick[code];
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the input events since the last poll
+//-----------------------------------------------------------------------------
+int CInputSystem::GetEventCount() const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_Events.Count();
+}
+
+const InputEvent_t* CInputSystem::GetEventData( ) const
+{
+ return m_InputState[INPUT_STATE_CURRENT].m_Events.Base();
+}
+
+
+//-----------------------------------------------------------------------------
+// Posts a user-defined event into the event queue; this is expected
+// to be called in overridden wndprocs connected to the root panel.
+//-----------------------------------------------------------------------------
+void CInputSystem::PostUserEvent( const InputEvent_t &event )
+{
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ state.m_Events.AddToTail( event );
+ state.m_bDirty = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Chains the window message to the previous wndproc
+//-----------------------------------------------------------------------------
+inline LRESULT CInputSystem::ChainWindowMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+#if !defined( POSIX )
+ if ( m_ChainedWndProc )
+ return CallWindowProc( m_ChainedWndProc, hwnd, uMsg, wParam, lParam );
+#endif
+ // FIXME: This comment is lifted from vguimatsurface;
+ // may not apply in future when the system is completed.
+
+ // This means the application is driving the messages (calling our window procedure manually)
+ // rather than us hooking their window procedure. The engine needs to do this in order for VCR
+ // mode to play back properly.
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Release all buttons
+//-----------------------------------------------------------------------------
+void CInputSystem::ReleaseAllButtons( int nFirstButton, int nLastButton )
+{
+ // Force button up messages for all down buttons
+ for ( int i = nFirstButton; i <= nLastButton; ++i )
+ {
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, (ButtonCode_t)i, (ButtonCode_t)i );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Zero analog state
+//-----------------------------------------------------------------------------
+void CInputSystem::ZeroAnalogState( int nFirstState, int nLastState )
+{
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ memset( &state.m_pAnalogDelta[nFirstState], 0, ( nLastState - nFirstState + 1 ) * sizeof(int) );
+ memset( &state.m_pAnalogValue[nFirstState], 0, ( nLastState - nFirstState + 1 ) * sizeof(int) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines all mouse button presses
+//-----------------------------------------------------------------------------
+int CInputSystem::ButtonMaskFromMouseWParam( WPARAM wParam, ButtonCode_t code, bool bDown ) const
+{
+ int nButtonMask = 0;
+
+#if defined( PLATFORM_WINDOWS )
+ if ( wParam & MK_LBUTTON )
+ {
+ nButtonMask |= 1;
+ }
+
+ if ( wParam & MK_RBUTTON )
+ {
+ nButtonMask |= 2;
+ }
+
+ if ( wParam & MK_MBUTTON )
+ {
+ nButtonMask |= 4;
+ }
+
+ if ( wParam & MS_MK_BUTTON4 )
+ {
+ nButtonMask |= 8;
+ }
+
+ if ( wParam & MS_MK_BUTTON5 )
+ {
+ nButtonMask |= 16;
+ }
+#endif
+
+#ifdef _DEBUG
+ if ( code != BUTTON_CODE_INVALID )
+ {
+ int nMsgMask = 1 << ( code - MOUSE_FIRST );
+ int nTestMask = bDown ? nMsgMask : 0;
+ Assert( ( nButtonMask & nMsgMask ) == nTestMask );
+ }
+#endif
+
+ return nButtonMask;
+}
+
+
+//-----------------------------------------------------------------------------
+// Updates the state of all mouse buttons
+//-----------------------------------------------------------------------------
+void CInputSystem::UpdateMouseButtonState( int nButtonMask, ButtonCode_t dblClickCode )
+{
+ for ( int i = 0; i < 5; ++i )
+ {
+ ButtonCode_t code = (ButtonCode_t)( MOUSE_FIRST + i );
+ bool bDown = ( nButtonMask & ( 1 << i ) ) != 0;
+ if ( bDown )
+ {
+ InputEventType_t type = ( code != dblClickCode ) ? IE_ButtonPressed : IE_ButtonDoubleClicked;
+ PostButtonPressedEvent( type, m_nLastSampleTick, code, code );
+ }
+ else
+ {
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles input messages
+//-----------------------------------------------------------------------------
+void CInputSystem::SetCursorPosition( int x, int y )
+{
+ if ( !m_hAttachedHWnd )
+ return;
+
+#if defined( PLATFORM_WINDOWS )
+ POINT pt;
+ pt.x = x; pt.y = y;
+ ClientToScreen( (HWND)m_hAttachedHWnd, &pt );
+ SetCursorPos( pt.x, pt.y );
+#elif defined( USE_SDL )
+ m_pLauncherMgr->SetCursorPosition( x, y );
+#endif
+
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ bool bXChanged = ( state.m_pAnalogValue[ MOUSE_X ] != x );
+ bool bYChanged = ( state.m_pAnalogValue[ MOUSE_Y ] != y );
+
+ state.m_pAnalogValue[ MOUSE_X ] = x;
+ state.m_pAnalogValue[ MOUSE_Y ] = y;
+ state.m_pAnalogDelta[ MOUSE_X ] = 0;
+ state.m_pAnalogDelta[ MOUSE_Y ] = 0;
+
+ if ( bXChanged )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogDelta[ MOUSE_X ] );
+ }
+ if ( bYChanged )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, state.m_pAnalogValue[ MOUSE_Y ], state.m_pAnalogDelta[ MOUSE_Y ] );
+ }
+ if ( bXChanged || bYChanged )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogValue[ MOUSE_Y ] );
+ }
+}
+
+
+void CInputSystem::UpdateMousePositionState( InputState_t &state, short x, short y )
+{
+ int nOldX = state.m_pAnalogValue[ MOUSE_X ];
+ int nOldY = state.m_pAnalogValue[ MOUSE_Y ];
+
+ state.m_pAnalogValue[ MOUSE_X ] = x;
+ state.m_pAnalogValue[ MOUSE_Y ] = y;
+ state.m_pAnalogDelta[ MOUSE_X ] = state.m_pAnalogValue[ MOUSE_X ] - nOldX;
+ state.m_pAnalogDelta[ MOUSE_Y ] = state.m_pAnalogValue[ MOUSE_Y ] - nOldY;
+
+ if ( state.m_pAnalogDelta[ MOUSE_X ] != 0 )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_X, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogDelta[ MOUSE_X ] );
+ }
+ if ( state.m_pAnalogDelta[ MOUSE_Y ] != 0 )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_Y, state.m_pAnalogValue[ MOUSE_Y ], state.m_pAnalogDelta[ MOUSE_Y ] );
+ }
+ if ( state.m_pAnalogDelta[ MOUSE_X ] != 0 || state.m_pAnalogDelta[ MOUSE_Y ] != 0 )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_XY, state.m_pAnalogValue[ MOUSE_X ], state.m_pAnalogValue[ MOUSE_Y ] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Handles input messages
+//-----------------------------------------------------------------------------
+LRESULT CInputSystem::WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+#if defined( PLATFORM_WINDOWS ) // We use this even for SDL to handle mouse move.
+ if ( !m_bEnabled )
+ return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
+
+ if ( hwnd != m_hAttachedHWnd )
+ return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
+
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ switch( uMsg )
+ {
+
+#if !defined( USE_SDL )
+ case WM_ACTIVATEAPP:
+ if ( hwnd == m_hAttachedHWnd )
+ {
+ bool bActivated = ( wParam == 1 );
+ if ( !bActivated )
+ {
+ ResetInputState();
+ }
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true );
+ ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, false );
+ ETWMouseUp( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case WM_RBUTTONDOWN:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true );
+ ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case WM_RBUTTONUP:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, false );
+ ETWMouseUp( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case WM_MBUTTONDOWN:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true );
+ ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case WM_MBUTTONUP:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, false );
+ ETWMouseUp( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ case MS_WM_XBUTTONDOWN:
+ {
+ ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, true );
+ UpdateMouseButtonState( nButtonMask );
+
+ // Windows docs say the XBUTTON messages we should return true from
+ return TRUE;
+ }
+ break;
+
+ case MS_WM_XBUTTONUP:
+ {
+ ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, false );
+ UpdateMouseButtonState( nButtonMask );
+
+ // Windows docs say the XBUTTON messages we should return true from
+ return TRUE;
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_LEFT, true );
+ ETWMouseDown( 0, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask, MOUSE_LEFT );
+ }
+ break;
+
+ case WM_RBUTTONDBLCLK:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_RIGHT, true );
+ ETWMouseDown( 2, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask, MOUSE_RIGHT );
+ }
+ break;
+
+ case WM_MBUTTONDBLCLK:
+ {
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, MOUSE_MIDDLE, true );
+ ETWMouseDown( 1, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+ UpdateMouseButtonState( nButtonMask, MOUSE_MIDDLE );
+ }
+ break;
+
+ case MS_WM_XBUTTONDBLCLK:
+ {
+ ButtonCode_t code = ( HIWORD( wParam ) == 1 ) ? MOUSE_4 : MOUSE_5;
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam, code, true );
+ UpdateMouseButtonState( nButtonMask, code );
+
+ // Windows docs say the XBUTTON messages we should return true from
+ return TRUE;
+ }
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ {
+ // Suppress key repeats
+ if ( !( lParam & ( 1<<30 ) ) )
+ {
+ // NOTE: These two can be unequal! For example, keypad enter
+ // which returns KEY_ENTER from virtual keys, and KEY_PAD_ENTER from scan codes
+ // Since things like vgui care about virtual keys; we're going to
+ // put both scan codes in the input message
+ ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
+ ButtonCode_t scanCode = ButtonCode_ScanCodeToButtonCode( lParam );
+ PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, scanCode, virtualCode );
+
+ // Post ETW events describing key presses to help correlate input events to performance
+ // problems in the game.
+ ETWKeyDown( scanCode, virtualCode, ButtonCodeToString( virtualCode ) );
+
+ // Deal with toggles
+ if ( scanCode == KEY_CAPSLOCK || scanCode == KEY_SCROLLLOCK || scanCode == KEY_NUMLOCK )
+ {
+ int nVirtualKey;
+ ButtonCode_t toggleCode;
+ switch( scanCode )
+ {
+ default: case KEY_CAPSLOCK: nVirtualKey = VK_CAPITAL; toggleCode = KEY_CAPSLOCKTOGGLE; break;
+ case KEY_SCROLLLOCK: nVirtualKey = VK_SCROLL; toggleCode = KEY_SCROLLLOCKTOGGLE; break;
+ case KEY_NUMLOCK: nVirtualKey = VK_NUMLOCK; toggleCode = KEY_NUMLOCKTOGGLE; break;
+ };
+
+ SHORT wState = GetKeyState( nVirtualKey );
+ bool bToggleState = ( wState & 0x1 ) != 0;
+ PostButtonPressedEvent( bToggleState ? IE_ButtonPressed : IE_ButtonReleased, m_nLastSampleTick, toggleCode, toggleCode );
+ }
+ }
+ }
+ break;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ // Don't handle key ups if the key's already up. This can happen when we alt-tab back to the engine.
+ ButtonCode_t virtualCode = ButtonCode_VirtualKeyToButtonCode( wParam );
+ ButtonCode_t scanCode = ButtonCode_ScanCodeToButtonCode( lParam );
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, scanCode, virtualCode );
+ }
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ ButtonCode_t code = (short)HIWORD( wParam ) > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
+ state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
+ PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
+ PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
+
+ state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( (short)HIWORD(wParam) ) / WHEEL_DELTA;
+ state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
+ }
+ break;
+
+#if defined( PLATFORM_WINDOWS_PC )
+ case WM_INPUT:
+ {
+ if ( m_bRawInputSupported )
+ {
+ UINT dwSize = 40;
+ static BYTE lpb[40];
+
+ pfnGetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
+
+ RAWINPUT* raw = (RAWINPUT*)lpb;
+ if (raw->header.dwType == RIM_TYPEMOUSE)
+ {
+ m_mouseRawAccumX += raw->data.mouse.lLastX;
+ m_mouseRawAccumY += raw->data.mouse.lLastY;
+ }
+ }
+ }
+ break;
+#endif
+
+#endif // !USE_SDL
+
+ case WM_MOUSEMOVE:
+ {
+ UpdateMousePositionState( state, (short)LOWORD(lParam), (short)HIWORD(lParam) );
+
+ int nButtonMask = ButtonMaskFromMouseWParam( wParam );
+ UpdateMouseButtonState( nButtonMask );
+ }
+ break;
+
+ }
+
+#if defined( PLATFORM_WINDOWS_PC ) && !defined( USE_SDL )
+ // Can't put this in the case statement, it's not constant
+ if ( uMsg == m_uiMouseWheel )
+ {
+ ButtonCode_t code = ( ( int )wParam ) > 0 ? MOUSE_WHEEL_UP : MOUSE_WHEEL_DOWN;
+ state.m_ButtonPressedTick[ code ] = state.m_ButtonReleasedTick[ code ] = m_nLastSampleTick;
+ PostEvent( IE_ButtonPressed, m_nLastSampleTick, code, code );
+ PostEvent( IE_ButtonReleased, m_nLastSampleTick, code, code );
+
+ state.m_pAnalogDelta[ MOUSE_WHEEL ] = ( ( int )wParam ) / WHEEL_DELTA;
+ state.m_pAnalogValue[ MOUSE_WHEEL ] += state.m_pAnalogDelta[ MOUSE_WHEEL ];
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, MOUSE_WHEEL, state.m_pAnalogValue[ MOUSE_WHEEL ], state.m_pAnalogDelta[ MOUSE_WHEEL ] );
+ }
+#endif
+
+#endif // PLATFORM_WINDOWS
+ return ChainWindowMessage( hwnd, uMsg, wParam, lParam );
+}
+
+bool CInputSystem::GetRawMouseAccumulators( int& accumX, int& accumY )
+{
+#if defined( USE_SDL )
+
+ if ( m_pLauncherMgr )
+ {
+ m_pLauncherMgr->GetMouseDelta( accumX, accumY, false );
+ return true;
+ }
+ return false;
+
+#else
+
+ accumX = m_mouseRawAccumX;
+ accumY = m_mouseRawAccumY;
+ m_mouseRawAccumX = m_mouseRawAccumY = 0;
+ return m_bRawInputSupported;
+
+#endif
+}
+
+void CInputSystem::SetConsoleTextMode( bool bConsoleTextMode )
+{
+ /* If someone calls this after init, shut it down. */
+ if ( bConsoleTextMode && m_bJoystickInitialized )
+ {
+ ShutdownJoysticks();
+ }
+
+ m_bConsoleTextMode = bConsoleTextMode;
+}
+
+ISteamController* CInputSystem::SteamControllerInterface()
+{
+ if ( m_bSkipControllerInitialization )
+ {
+ return nullptr;
+ }
+ else
+ {
+ return m_SteamAPIContext.SteamController();
+ }
+}
diff --git a/inputsystem/inputsystem.h b/inputsystem/inputsystem.h
new file mode 100644
index 0000000..9a85148
--- /dev/null
+++ b/inputsystem/inputsystem.h
@@ -0,0 +1,469 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#ifndef INPUTSYSTEM_H
+#define INPUTSYSTEM_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#ifdef WIN32
+#if !defined( _X360 )
+#define _WIN32_WINNT 0x502
+#include <windows.h>
+#include <zmouse.h>
+#include "xbox/xboxstubs.h"
+#include "../../dx9sdk/include/XInput.h"
+#endif
+#endif
+
+#ifdef POSIX
+#include "posix_stubs.h"
+#endif // POSIX
+
+#include "appframework/ilaunchermgr.h"
+
+#include "inputsystem/iinputsystem.h"
+#include "tier2/tier2.h"
+
+#include "inputsystem/ButtonCode.h"
+#include "inputsystem/AnalogCode.h"
+#include "bitvec.h"
+#include "tier1/utlvector.h"
+#include "tier1/utlflags.h"
+
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#include "xbox/xbox_console.h"
+#endif
+
+#include "steam/steam_api.h"
+
+//-----------------------------------------------------------------------------
+// Implementation of the input system
+//-----------------------------------------------------------------------------
+class CInputSystem : public CTier2AppSystem< IInputSystem >
+{
+ typedef CTier2AppSystem< IInputSystem > BaseClass;
+
+public:
+ // Constructor, destructor
+ CInputSystem();
+ virtual ~CInputSystem();
+
+ // Inherited from IAppSystem
+ virtual InitReturnVal_t Init();
+ virtual bool Connect( CreateInterfaceFn factory );
+
+ virtual void Shutdown();
+
+ // Inherited from IInputSystem
+ virtual void AttachToWindow( void* hWnd );
+ virtual void DetachFromWindow();
+ virtual void EnableInput( bool bEnable );
+ virtual void EnableMessagePump( bool bEnable );
+ virtual int GetPollTick() const;
+ virtual void PollInputState();
+ virtual bool IsButtonDown( ButtonCode_t code ) const;
+ virtual int GetButtonPressedTick( ButtonCode_t code ) const;
+ virtual int GetButtonReleasedTick( ButtonCode_t code ) const;
+ virtual int GetAnalogValue( AnalogCode_t code ) const;
+ virtual int GetAnalogDelta( AnalogCode_t code ) const;
+ virtual int GetEventCount() const;
+ virtual const InputEvent_t* GetEventData() const;
+ virtual void PostUserEvent( const InputEvent_t &event );
+ virtual int GetJoystickCount() const;
+ virtual void EnableJoystickInput( int nJoystick, bool bEnable );
+ virtual void EnableJoystickDiagonalPOV( int nJoystick, bool bEnable );
+ virtual void SampleDevices( void );
+ virtual void SetRumble( float fLeftMotor, float fRightMotor, int userId );
+ virtual void StopRumble( void );
+ virtual void ResetInputState( void );
+ virtual void SetPrimaryUserId( int userId );
+ virtual const char *ButtonCodeToString( ButtonCode_t code ) const;
+ virtual const char *AnalogCodeToString( AnalogCode_t code ) const;
+ virtual ButtonCode_t StringToButtonCode( const char *pString ) const;
+ virtual AnalogCode_t StringToAnalogCode( const char *pString ) const;
+ virtual ButtonCode_t VirtualKeyToButtonCode( int nVirtualKey ) const;
+ virtual int ButtonCodeToVirtualKey( ButtonCode_t code ) const;
+ virtual ButtonCode_t ScanCodeToButtonCode( int lParam ) const;
+ virtual void SleepUntilInput( int nMaxSleepTimeMS );
+ virtual int GetPollCount() const;
+ virtual void SetCursorPosition( int x, int y );
+#if defined( WIN32 ) && !defined ( _X360 )
+ virtual void *GetHapticsInterfaceAddress() const;
+#else
+ virtual void *GetHapticsInterfaceAddress() const { return NULL;}
+#endif
+ bool GetRawMouseAccumulators( int& accumX, int& accumY );
+ virtual void SetConsoleTextMode( bool bConsoleTextMode );
+
+ // Windows proc
+ LRESULT WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
+
+
+private:
+ enum
+ {
+ STICK1_AXIS_LEFT,
+ STICK1_AXIS_RIGHT,
+ STICK1_AXIS_DOWN,
+ STICK1_AXIS_UP,
+ STICK2_AXIS_LEFT,
+ STICK2_AXIS_RIGHT,
+ STICK2_AXIS_DOWN,
+ STICK2_AXIS_UP,
+ MAX_STICKAXIS
+ };
+
+ // track Xbox stick keys from previous frame
+ enum
+ {
+ LASTKEY_STICK1_X,
+ LASTKEY_STICK1_Y,
+ LASTKEY_STICK2_X,
+ LASTKEY_STICK2_Y,
+ MAX_LASTKEY,
+ };
+
+ enum
+ {
+ INPUT_STATE_QUEUED = 0,
+ INPUT_STATE_CURRENT,
+
+ INPUT_STATE_COUNT,
+ };
+
+public:
+
+ struct JoystickInfo_t
+ {
+ void *m_pDevice; // Really an SDL_GameController*, NULL if not present.
+ void *m_pHaptic; // Really an SDL_Haptic*
+ float m_fCurrentRumble;
+ bool m_bRumbleEnabled;
+ int m_nButtonCount;
+ int m_nAxisFlags;
+ int m_nDeviceId;
+ bool m_bHasPOVControl;
+ bool m_bDiagonalPOVControlEnabled;
+ unsigned int m_nFlags;
+ unsigned int m_nLastPolledButtons;
+ unsigned int m_nLastPolledAxisButtons;
+ unsigned int m_nLastPolledPOVState;
+ unsigned long m_pLastPolledAxes[MAX_JOYSTICK_AXES];
+ };
+
+ struct xdevice_t
+ {
+ int userId;
+ byte type;
+ byte subtype;
+ word flags;
+ bool active;
+ XINPUT_STATE states[2];
+ int newState;
+ xKey_t lastStickKeys[MAX_LASTKEY];
+ int stickThreshold[MAX_STICKAXIS];
+ float stickScale[MAX_STICKAXIS];
+ int quitTimeout;
+ int dpadLock;
+ // rumble
+ XINPUT_VIBRATION vibration;
+ bool pendingRumbleUpdate;
+ };
+
+ struct appKey_t
+ {
+ int repeats;
+ int sample;
+ };
+
+ struct InputState_t
+ {
+ // Analog states
+ CBitVec<BUTTON_CODE_LAST> m_ButtonState;
+ int m_ButtonPressedTick[BUTTON_CODE_LAST];
+ int m_ButtonReleasedTick[BUTTON_CODE_LAST];
+ int m_pAnalogDelta[ANALOG_CODE_LAST];
+ int m_pAnalogValue[ANALOG_CODE_LAST];
+ CUtlVector< InputEvent_t > m_Events;
+ bool m_bDirty;
+ };
+
+ // Initializes all Xbox controllers
+ void InitializeXDevices( void );
+
+ // Opens an Xbox controller
+ void OpenXDevice( xdevice_t* pXDevice, int userId );
+
+ // Closes an Xbox controller
+ void CloseXDevice( xdevice_t* pXDevice );
+
+ // Samples the Xbox controllers
+ void PollXDevices( void );
+
+ // Samples an Xbox controller and queues input events
+ void ReadXDevice( xdevice_t* pXDevice );
+
+ // Submits force feedback data to an Xbox controller
+ void WriteToXDevice( xdevice_t* pXDevice );
+
+ // Sets rumble values for an Xbox controller
+ void SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId );
+
+ // Posts an Xbox key event, ignoring key repeats
+ void PostXKeyEvent( int nUserId, xKey_t xKey, int nSample );
+
+ // Dispatches all joystick button events through the game's window procs
+ void ProcessEvent( UINT uMsg, WPARAM wParam, LPARAM lParam );
+
+ // Initializes joysticks
+ void InitializeJoysticks( void );
+
+ // Shut down joysticks
+ void ShutdownJoysticks( void );
+
+ // Samples the joystick
+ void PollJoystick( void );
+
+ // Update the joystick button state
+ void UpdateJoystickButtonState( int nJoystick );
+
+ // Update the joystick POV control
+ void UpdateJoystickPOVControl( int nJoystick );
+
+ // Record button state and post the event
+ void JoystickButtonEvent( ButtonCode_t button, int sample );
+
+#if defined( WIN32 ) && !defined ( _X360 )
+ // NVNT attaches window to novint devices
+ void AttachWindowToNovintDevices( void * hWnd );
+
+ // NVNT detaches window from novint input
+ void DetachWindowFromNovintDevices( void );
+
+ // NVNT Initializes novint devices
+ void InitializeNovintDevices( void );
+
+ // NVNT Samples a novint device
+ void PollNovintDevices( void );
+
+ // NVNT Update the novint device button state
+ void UpdateNovintDeviceButtonState( int nDevice );
+
+ // NVNT Record button state and post the event
+ void NovintDeviceButtonEvent( ButtonCode_t button, int sample );
+
+ //Added called and set to true when binding input and set to false once bound
+ void SetNovintPure( bool bPure );
+#else
+ void SetNovintPure( bool bPure ) {} // to satify the IInput virtual interface
+#endif
+ // Chains the window message to the previous wndproc
+ LRESULT ChainWindowMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
+
+ // Post an event to the queue
+ void PostEvent( int nType, int nTick, int nData = 0, int nData2 = 0, int nData3 = 0 );
+
+ // Deals with app deactivation (sends a bunch of button released messages)
+ void ActivateInputSystem( bool bActivated );
+
+ // Determines all mouse button presses
+ int ButtonMaskFromMouseWParam( WPARAM wParam, ButtonCode_t code = BUTTON_CODE_INVALID, bool bDown = false ) const;
+
+ // Updates the state of all mouse buttons
+ void UpdateMouseButtonState( int nButtonMask, ButtonCode_t dblClickCode = BUTTON_CODE_INVALID );
+
+ // Copies the input state record over
+ void CopyInputState( InputState_t *pDest, const InputState_t &src, bool bCopyEvents );
+
+ // Post an button press/release event to the queue
+ void PostButtonPressedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode );
+ void PostButtonReleasedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode );
+
+ // Release all buttons
+ void ReleaseAllButtons( int nFirstButton = 0, int nLastButton = BUTTON_CODE_LAST - 1 );
+
+ // Zero analog state
+ void ZeroAnalogState( int nFirstState, int nLastState );
+
+ // Converts xbox keys to button codes
+ ButtonCode_t XKeyToButtonCode( int nUserId, int nXKey ) const;
+
+ // Computes the sample tick
+ int ComputeSampleTick();
+
+ // Clears the input state, doesn't generate key-up messages
+ void ClearInputState();
+
+ // Called for mouse move events. Sets the current x and y and posts events for the mouse moving.
+ void UpdateMousePositionState( InputState_t &state, short x, short y );
+
+ // Initializes SteamControllers - Returns true if steam is running and finds controllers, otherwise false
+ bool InitializeSteamControllers( void );
+
+ // Returns number of connected Steam Controllers
+ uint32 GetNumSteamControllersConnected();
+
+ // Update and sample steam controllers
+ void PollSteamControllers( void );
+
+#if defined( PLATFORM_WINDOWS_PC )
+ void PollInputState_Windows();
+#endif
+
+ void JoystickHotplugAdded( int joystickIndex );
+ void JoystickHotplugRemoved( int joystickId );
+ void JoystickButtonPress( int joystickId, int button ); // button is a SDL_CONTROLLER_BUTTON;
+ void JoystickButtonRelease( int joystickId, int button ); // same as above.
+ void JoystickAxisMotion( int joystickId, int axis, int value );
+
+ // Steam Controller
+ void ReadSteamController( int iIndex );
+ void PostKeyEvent( int iIndex, sKey_t sKey, int nSample );
+ const int GetSteamPadDeadZone( ESteamPadAxis axis );
+ bool IsSteamControllerConnected( void ) { return m_bSteamController; }
+ bool IsSteamControllerActive( void );
+ void ActivateSteamControllerActionSetForSlot( uint64 nSlot, GameActionSet_t eActionSet );
+ ControllerActionSetHandle_t GetActionSetHandle( GameActionSet_t eActionSet );
+ ControllerActionSetHandle_t GetActionSetHandle( const char* szActionSet );
+ bool GetControllerStateForSlot( int nSlot );
+ int GetSteamControllerIndexForSlot( int nSlot );
+ bool GetRadialMenuStickValues( int nSlot, float &fX, float &fY );
+ virtual ISteamController* SteamControllerInterface();
+ bool InitializeSteamControllerActionSets();
+
+ // Gets the action origin (i.e. which physical input) maps to the given action name
+ virtual EControllerActionOrigin GetSteamControllerActionOrigin( const char* action, GameActionSet_t action_set );
+ virtual EControllerActionOrigin GetSteamControllerActionOrigin( const char* action, ControllerActionSetHandle_t action_set_handle );
+
+ // Maps a Steam Controller action origin to a string (consisting of a single character) in our SC icon font
+ virtual const wchar_t* GetSteamControllerFontCharacterForActionOrigin( EControllerActionOrigin origin );
+
+ // Maps a Steam Controller action origin to a short text string (e.g. "X", "LB", "LDOWN") describing the control.
+ // Prefer to actually use the icon font wherever possible.
+ virtual const wchar_t* GetSteamControllerDescriptionForActionOrigin( EControllerActionOrigin origin );
+
+ // Converts SteamController keys to button codes
+ ButtonCode_t SKeyToButtonCode( int nUserId, int nXKey ) const;
+
+ // This is called with "true" by dedicated server initialization (before calling Init) in order to
+ // force us to skip initialization of Steam (which messes up dedicated servers).
+ virtual void SetSkipControllerInitialization( bool bSkip )
+ {
+ m_bSkipControllerInitialization = bSkip;
+ }
+
+#if defined( USE_SDL )
+ void PollInputState_Platform();
+
+ ILauncherMgr *m_pLauncherMgr;
+#endif
+
+ WNDPROC m_ChainedWndProc;
+ HWND m_hAttachedHWnd;
+ bool m_bEnabled;
+ bool m_bPumpEnabled;
+ bool m_bIsPolling;
+
+ // Current button state
+ InputState_t m_InputState[INPUT_STATE_COUNT];
+
+ // Current action set
+ GameActionSet_t m_currentActionSet[STEAM_CONTROLLER_MAX_COUNT];
+
+ DWORD m_StartupTimeTick;
+ int m_nLastPollTick;
+ int m_nLastSampleTick;
+ int m_nPollCount;
+
+ // Mouse wheel hack
+ UINT m_uiMouseWheel;
+
+ // Joystick info
+ CUtlFlags<unsigned short> m_JoysticksEnabled;
+ int m_nJoystickCount;
+ bool m_bJoystickInitialized;
+ bool m_bXController;
+ JoystickInfo_t m_pJoystickInfo[ MAX_JOYSTICKS ];
+
+ // Steam Controller
+ struct steampad_t
+ {
+ steampad_t()
+ {
+ m_nHardwareIndex = 0;
+ m_nJoystickIndex = INVALID_USER_ID;
+ m_nLastPacketIndex = 0;
+ active = false;
+ memset( lastAnalogKeys, 0, sizeof( lastAnalogKeys ) );
+ }
+ bool active;
+
+ sKey_t lastAnalogKeys[MAX_STEAMPADAXIS];
+ appKey_t m_appSKeys[SK_MAX_KEYS];
+ // Hardware index and joystick index don't necessarily match
+ // Joystick index will depend on the order of multiple initialized devices
+ // Which could include other controller types
+ // Hardware index should line up 1:1 with the order they're polled
+ // and could change based on removing devices, unlike Joystick Index
+ uint32 m_nHardwareIndex;
+ int m_nJoystickIndex;
+ uint32 m_nLastPacketIndex;
+ };
+
+ float m_pRadialMenuStickVal[STEAM_CONTROLLER_MAX_COUNT][2];
+ steampad_t m_Device[STEAM_CONTROLLER_MAX_COUNT];
+ uint32 m_unNumConnected;
+ float m_flLastSteamControllerInput;
+ int m_nJoystickBaseline;
+ int m_nControllerType[MAX_JOYSTICKS+STEAM_CONTROLLER_MAX_COUNT];
+
+ bool m_bSteamController; // true if the Steam Controller system has been initialized successfully (this doesn't mean one is actually connected necessarily)
+ bool m_bSteamControllerActionsInitialized; // true if our action sets and handles were successfully initialized (this doesn't mean a controller is necessarily connected, or that in-game client actions were initialized)
+ bool m_bSteamControllerActive; // true if our action sets and handles were successfully initialized *and* that at least one controller is actually connected and switched on.
+
+#if defined( WIN32 ) && !defined ( _X360 )
+ // NVNT Novint device info
+ int m_nNovintDeviceCount;
+ bool m_bNovintDevices;
+#endif
+
+ // Xbox controller info
+ appKey_t m_appXKeys[ XUSER_MAX_COUNT ][ XK_MAX_KEYS ];
+ xdevice_t m_XDevices[ XUSER_MAX_COUNT ];
+ int m_PrimaryUserId;
+
+ // raw mouse input
+ bool m_bRawInputSupported;
+ int m_mouseRawAccumX, m_mouseRawAccumY;
+
+ // For the 'SleepUntilInput' feature
+ HANDLE m_hEvent;
+
+ CSysModule *m_pXInputDLL;
+ CSysModule *m_pRawInputDLL;
+
+#if defined( WIN32 ) && !defined ( _X360 )
+ // NVNT falcon module
+ CSysModule *m_pNovintDLL;
+#endif
+
+ bool m_bConsoleTextMode;
+
+public:
+ // Steam API context for use by input system for access to steam controllers.
+ CSteamAPIContext& SteamAPIContext() { return m_SteamAPIContext; }
+
+private:
+ CSteamAPIContext m_SteamAPIContext;
+ bool m_bSkipControllerInitialization;
+};
+
+#endif // INPUTSYSTEM_H
diff --git a/inputsystem/inputsystem.vpc b/inputsystem/inputsystem.vpc
new file mode 100644
index 0000000..a9c2956
--- /dev/null
+++ b/inputsystem/inputsystem.vpc
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------------
+// INPUTSYSTEM.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$macro SRCDIR ".."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\thirdparty\SDL2" [!$SDL] // If $SDL, then we already have this from source_video_base.vpc.
+ $PreprocessorDefinitions "$BASE;VERSION_SAFE_STEAM_API_INTERFACES"
+ }
+ $Linker
+ {
+ $SystemLibraries "iconv" [$OSXALL]
+ $SystemFrameworks "IOKit;Carbon;ForceFeedback"
+ }
+}
+
+$Project "inputsystem"
+{
+ $Folder "Source Files"
+ {
+ $File "inputsystem.cpp"
+ $File "inputsystem.h"
+ $File "joystick_sdl.cpp"
+ $File "novint.cpp" [$WIN32]
+ $File "key_translation.cpp"
+ $File "key_translation.h"
+ $File "steamcontroller.cpp"
+ }
+
+ $Folder "Public Headers"
+ {
+ $File "$SRCDIR\public\inputsystem\AnalogCode.h"
+ $File "$SRCDIR\public\inputsystem\ButtonCode.h"
+ $File "$SRCDIR\public\inputsystem\iinputsystem.h"
+ $File "$SRCDIR\public\inputsystem\InputEnums.h"
+ $File "$SRCDIR\dx9sdk\include\xinput.h" [$WIN32]
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib tier1
+ $Lib tier2
+ $ImpLib SDL2
+ $Implib "steam_api" [($WIN32||$WIN64||$POSIX||$PS3)&&!$NO_STEAM]
+ }
+}
diff --git a/inputsystem/joystick_sdl.cpp b/inputsystem/joystick_sdl.cpp
new file mode 100644
index 0000000..aef5c48
--- /dev/null
+++ b/inputsystem/joystick_sdl.cpp
@@ -0,0 +1,615 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Linux Joystick implementation for inputsystem.dll
+//
+//===========================================================================//
+
+/* For force feedback testing. */
+#include "inputsystem.h"
+#include "tier1/convar.h"
+#include "tier0/icommandline.h"
+
+#include "SDL.h"
+#include "SDL_gamecontroller.h"
+#include "SDL_haptic.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+static ButtonCode_t ControllerButtonToButtonCode( SDL_GameControllerButton button );
+static AnalogCode_t ControllerAxisToAnalogCode( SDL_GameControllerAxis axis );
+static int JoystickSDLWatcher( void *userInfo, SDL_Event *event );
+
+ConVar joy_axisbutton_threshold( "joy_axisbutton_threshold", "0.3", FCVAR_ARCHIVE, "Analog axis range before a button press is registered." );
+ConVar joy_axis_deadzone( "joy_axis_deadzone", "0.2", FCVAR_ARCHIVE, "Dead zone near the zero point to not report movement." );
+
+static void joy_active_changed_f( IConVar *var, const char *pOldValue, float flOldValue );
+ConVar joy_active( "joy_active", "-1", FCVAR_NONE, "Which of the connected joysticks / gamepads to use (-1 means first found)", &joy_active_changed_f);
+
+static void joy_gamecontroller_config_changed_f( IConVar *var, const char *pOldValue, float flOldValue );
+ConVar joy_gamecontroller_config( "joy_gamecontroller_config", "", FCVAR_ARCHIVE, "Game controller mapping (passed to SDL with SDL_HINT_GAMECONTROLLERCONFIG), can also be configured in Steam Big Picture mode.", &joy_gamecontroller_config_changed_f );
+
+void SearchForDevice()
+{
+ int newJoystickId = joy_active.GetInt();
+ CInputSystem *pInputSystem = (CInputSystem *)g_pInputSystem;
+
+ if ( !pInputSystem )
+ {
+ return;
+ }
+ // -1 means "first available."
+ if ( newJoystickId < 0 )
+ {
+ pInputSystem->JoystickHotplugAdded(0);
+ return;
+ }
+
+ for ( int device_index = 0; device_index < SDL_NumJoysticks(); ++device_index )
+ {
+ SDL_Joystick *joystick = SDL_JoystickOpen(device_index);
+ if ( joystick == NULL )
+ {
+ continue;
+ }
+
+ int joystickId = SDL_JoystickInstanceID(joystick);
+ SDL_JoystickClose(joystick);
+
+ if ( joystickId == newJoystickId )
+ {
+ pInputSystem->JoystickHotplugAdded(device_index);
+ break;
+ }
+ }
+}
+
+//---------------------------------------------------------------------------------------
+// Switch our active joystick to another device
+//---------------------------------------------------------------------------------------
+void joy_active_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
+{
+ SearchForDevice();
+}
+
+//---------------------------------------------------------------------------------------
+// Reinitialize the game controller layer when the joy_gamecontroller_config is updated.
+//---------------------------------------------------------------------------------------
+void joy_gamecontroller_config_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
+{
+ CInputSystem *pInputSystem = (CInputSystem *)g_pInputSystem;
+ if ( pInputSystem && SDL_WasInit(SDL_INIT_GAMECONTROLLER) )
+ {
+ bool oldValuePresent = pOldValue && ( strlen( pOldValue ) > 0 );
+ bool newValuePresent = ( strlen( joy_gamecontroller_config.GetString() ) > 0 );
+ if ( !oldValuePresent && !newValuePresent )
+ {
+ return;
+ }
+
+ // We need to reinitialize the whole thing (i.e. undo CInputSystem::InitializeJoysticks and then call it again)
+ // due to SDL_GameController only reading the SDL_HINT_GAMECONTROLLERCONFIG on init.
+ pInputSystem->ShutdownJoysticks();
+ pInputSystem->InitializeJoysticks();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Handle the events coming from the GameController SDL subsystem.
+//-----------------------------------------------------------------------------
+int JoystickSDLWatcher( void *userInfo, SDL_Event *event )
+{
+ CInputSystem *pInputSystem = (CInputSystem *)userInfo;
+ Assert(pInputSystem != NULL);
+ Assert(event != NULL);
+
+ if ( event == NULL || pInputSystem == NULL )
+ {
+ Warning("No input system\n");
+ return 1;
+ }
+
+ switch ( event->type )
+ {
+ case SDL_CONTROLLERAXISMOTION:
+ case SDL_CONTROLLERBUTTONDOWN:
+ case SDL_CONTROLLERBUTTONUP:
+ case SDL_CONTROLLERDEVICEADDED:
+ case SDL_CONTROLLERDEVICEREMOVED:
+ break;
+ default:
+ return 1;
+ }
+
+ // This is executed on the same thread as SDL_PollEvent, as PollEvent
+ // updates the joystick subsystem, which then calls SDL_PushEvent for
+ // the various events below. PushEvent invokes this callback.
+ // SDL_PollEvent is called in PumpWindowsMessageLoop which is coming
+ // from PollInputState_Linux, so there's no worry about calling
+ // PostEvent (which doesn't seem to be thread safe) from other threads.
+ Assert(ThreadInMainThread());
+
+ switch ( event->type )
+ {
+ case SDL_CONTROLLERAXISMOTION:
+ {
+ pInputSystem->JoystickAxisMotion(event->caxis.which, event->caxis.axis, event->caxis.value);
+ break;
+ }
+
+ case SDL_CONTROLLERBUTTONDOWN:
+ pInputSystem->JoystickButtonPress(event->cbutton.which, event->cbutton.button);
+ break;
+ case SDL_CONTROLLERBUTTONUP:
+ pInputSystem->JoystickButtonRelease(event->cbutton.which, event->cbutton.button);
+ break;
+
+ case SDL_CONTROLLERDEVICEADDED:
+ pInputSystem->JoystickHotplugAdded(event->cdevice.which);
+ break;
+ case SDL_CONTROLLERDEVICEREMOVED:
+ pInputSystem->JoystickHotplugRemoved(event->cdevice.which);
+ SearchForDevice();
+ break;
+ }
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Initialize all joysticks
+//-----------------------------------------------------------------------------
+void CInputSystem::InitializeJoysticks( void )
+{
+ if ( m_bJoystickInitialized )
+ {
+ ShutdownJoysticks();
+ }
+
+ // assume no joystick
+ m_nJoystickCount = 0;
+ memset( m_pJoystickInfo, 0, sizeof( m_pJoystickInfo ) );
+ for ( int i = 0; i < MAX_JOYSTICKS; ++i )
+ {
+ m_pJoystickInfo[ i ].m_nDeviceId = -1;
+ }
+
+ // abort startup if user requests no joystick
+ if ( CommandLine()->FindParm("-nojoy") ) return;
+
+ const char *controllerConfig = joy_gamecontroller_config.GetString();
+ if ( strlen(controllerConfig) > 0 )
+ {
+ DevMsg("Passing joy_gamecontroller_config to SDL ('%s').\n", controllerConfig);
+ // We need to pass this hint to SDL *before* we init the gamecontroller subsystem, otherwise it gets ignored.
+ SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG, controllerConfig);
+ }
+
+ if ( SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC ) == -1 )
+ {
+ Warning("Joystick init failed -- SDL_Init(SDL_INIT_GAMECONTROLLER|SDL_INIT_HAPTIC) failed: %s.\n", SDL_GetError());
+ return;
+ }
+
+ m_bJoystickInitialized = true;
+
+ SDL_AddEventWatch(JoystickSDLWatcher, this);
+
+ const int totalSticks = SDL_NumJoysticks();
+ for ( int i = 0; i < totalSticks; i++ )
+ {
+ if ( SDL_IsGameController(i) )
+ {
+ JoystickHotplugAdded(i);
+ }
+ else
+ {
+ SDL_JoystickGUID joyGUID = SDL_JoystickGetDeviceGUID(i);
+ char szGUID[sizeof(joyGUID.data)*2 + 1];
+ SDL_JoystickGetGUIDString(joyGUID, szGUID, sizeof(szGUID));
+
+ Msg("Found joystick '%s' (%s), but no recognized controller configuration for it.\n", SDL_JoystickNameForIndex(i), szGUID);
+ }
+ }
+
+ if ( totalSticks < 1 )
+ {
+ Msg("Did not detect any valid joysticks.\n");
+ }
+}
+
+void CInputSystem::ShutdownJoysticks()
+{
+ if ( !m_bJoystickInitialized )
+ {
+ return;
+ }
+
+ SDL_DelEventWatch( JoystickSDLWatcher, this );
+ if ( m_pJoystickInfo[ 0 ].m_pDevice != NULL )
+ {
+ JoystickHotplugRemoved( m_pJoystickInfo[ 0 ].m_nDeviceId );
+ }
+ SDL_QuitSubSystem( SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC );
+
+ m_bJoystickInitialized = false;
+}
+
+// Update the joy_xcontroller_found convar to force CInput::JoyStickMove to re-exec 360controller-linux.cfg
+static void SetJoyXControllerFound( bool found )
+{
+ static ConVarRef xcontrollerVar( "joy_xcontroller_found" );
+ static ConVarRef joystickVar( "joystick" );
+ if ( xcontrollerVar.IsValid() )
+ {
+ xcontrollerVar.SetValue(found);
+ }
+
+ if ( found && joystickVar.IsValid() )
+ {
+ joystickVar.SetValue(true);
+ }
+}
+
+void CInputSystem::JoystickHotplugAdded( int joystickIndex )
+{
+ // SDL_IsGameController doesn't bounds check its inputs.
+ if ( joystickIndex < 0 || joystickIndex >= SDL_NumJoysticks() )
+ {
+ return;
+ }
+
+ if ( !SDL_IsGameController(joystickIndex) )
+ {
+ Warning("Joystick is not recognized by the game controller system. You can configure the controller in Steam Big Picture mode.\n");
+ return;
+ }
+
+ SDL_Joystick *joystick = SDL_JoystickOpen(joystickIndex);
+ if ( joystick == NULL )
+ {
+ Warning("Could not open joystick %i: %s", joystickIndex, SDL_GetError());
+ return;
+ }
+
+ int joystickId = SDL_JoystickInstanceID(joystick);
+ SDL_JoystickClose(joystick);
+
+ int activeJoystick = joy_active.GetInt();
+ JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
+ if ( activeJoystick < 0 )
+ {
+ // Only opportunistically open devices if we don't have one open already.
+ if ( info.m_nDeviceId != -1 )
+ {
+ Msg("Detected supported joystick #%i '%s'. Currently active joystick is #%i.\n", joystickId, SDL_JoystickNameForIndex(joystickIndex), info.m_nDeviceId);
+ return;
+ }
+ }
+ else if ( activeJoystick != joystickId )
+ {
+ Msg("Detected supported joystick #%i '%s'. Currently active joystick is #%i.\n", joystickId, SDL_JoystickNameForIndex(joystickIndex), activeJoystick);
+ return;
+ }
+
+ if ( info.m_nDeviceId != -1 )
+ {
+ // Don't try to open the device we already have open.
+ if ( info.m_nDeviceId == joystickId )
+ {
+ return;
+ }
+
+ DevMsg("Joystick #%i already initialized, removing it first.\n", info.m_nDeviceId);
+ JoystickHotplugRemoved(info.m_nDeviceId);
+ }
+
+ Msg("Initializing joystick #%i and making it active.\n", joystickId);
+
+ SDL_GameController *controller = SDL_GameControllerOpen(joystickIndex);
+ if ( controller == NULL )
+ {
+ Warning("Failed to open joystick %i: %s\n", joystickId, SDL_GetError());
+ return;
+ }
+
+ // XXX: This will fail if this is a *real* hotplug event (and not coming from the initial InitializeJoysticks call).
+ // That's because the SDL haptic subsystem currently doesn't do hotplugging. Everything but haptics will work fine.
+ SDL_Haptic *haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller));
+ if ( haptic == NULL || SDL_HapticRumbleInit(haptic) != 0 )
+ {
+ Warning("Unable to initialize rumble for joystick #%i: %s\n", joystickId, SDL_GetError());
+ haptic = NULL;
+ }
+
+ info.m_pDevice = controller;
+ info.m_pHaptic = haptic;
+ info.m_nDeviceId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
+ info.m_nButtonCount = SDL_CONTROLLER_BUTTON_MAX;
+ info.m_bRumbleEnabled = false;
+
+ SetJoyXControllerFound(true);
+ EnableJoystickInput(0, true);
+ m_nJoystickCount = 1;
+ m_bXController = true;
+
+ // We reset joy_active to -1 because joystick ids are never reused - until you restart.
+ // Setting it to -1 means that you get expected hotplugging behavior if you disconnect the current joystick.
+ joy_active.SetValue(-1);
+}
+
+void CInputSystem::JoystickHotplugRemoved( int joystickId )
+{
+ JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
+ if ( info.m_nDeviceId != joystickId )
+ {
+ DevMsg("Ignoring hotplug remove for #%i, active joystick is #%i.\n", joystickId, info.m_nDeviceId);
+ return;
+ }
+
+ if ( info.m_pDevice == NULL )
+ {
+ info.m_nDeviceId = -1;
+ DevMsg("Got hotplug remove event for removed joystick #%i, ignoring.\n", joystickId);
+ return;
+ }
+
+ m_nJoystickCount = 0;
+ m_bXController = false;
+ EnableJoystickInput(0, false);
+ SetJoyXControllerFound(false);
+
+ SDL_HapticClose((SDL_Haptic *)info.m_pHaptic);
+ SDL_GameControllerClose((SDL_GameController *)info.m_pDevice);
+
+ info.m_pHaptic = NULL;
+ info.m_pDevice = NULL;
+ info.m_nButtonCount = 0;
+ info.m_nDeviceId = -1;
+ info.m_bRumbleEnabled = false;
+
+ Msg("Joystick %i removed.\n", joystickId);
+}
+
+void CInputSystem::JoystickButtonPress( int joystickId, int button )
+{
+ JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
+ if ( info.m_nDeviceId != joystickId )
+ {
+ Warning("Not active device input system (%i x %i)\n", info.m_nDeviceId, joystickId);
+ return;
+ }
+
+ ButtonCode_t buttonCode = ControllerButtonToButtonCode((SDL_GameControllerButton)button);
+ PostButtonPressedEvent(IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode);
+}
+
+void CInputSystem::JoystickButtonRelease( int joystickId, int button )
+{
+ JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
+ if ( info.m_nDeviceId != joystickId )
+ {
+ return;
+ }
+
+ ButtonCode_t buttonCode = ControllerButtonToButtonCode((SDL_GameControllerButton)button);
+ PostButtonReleasedEvent(IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode);
+}
+
+
+void CInputSystem::JoystickAxisMotion( int joystickId, int axis, int value )
+{
+ JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
+ if ( info.m_nDeviceId != joystickId )
+ {
+ return;
+ }
+
+ AnalogCode_t code = ControllerAxisToAnalogCode((SDL_GameControllerAxis)axis);
+ if ( code == ANALOG_CODE_INVALID )
+ {
+ Warning("Invalid code for axis %i\n", axis);
+ return;
+ }
+
+ ButtonCode_t buttonCode = BUTTON_CODE_NONE;
+ switch ( axis )
+ {
+ case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+ buttonCode = KEY_XBUTTON_RTRIGGER;
+ break;
+ case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
+ buttonCode = KEY_XBUTTON_LTRIGGER;
+ break;
+ }
+
+ if ( buttonCode != BUTTON_CODE_NONE )
+ {
+ int pressThreshold = joy_axisbutton_threshold.GetFloat() * 32767;
+ int keyIndex = buttonCode - KEY_XBUTTON_LTRIGGER;
+ Assert( keyIndex < ARRAYSIZE( m_appXKeys[0] ) && keyIndex >= 0 );
+
+ appKey_t &key = m_appXKeys[0][keyIndex];
+ if ( value > pressThreshold )
+ {
+ if ( key.repeats < 1 )
+ {
+ PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
+ }
+ key.repeats++;
+ }
+ else
+ {
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
+ key.repeats = 0;
+ }
+ }
+
+ int minValue = joy_axis_deadzone.GetFloat() * 32767;
+ if ( abs(value) < minValue )
+ {
+ value = 0;
+ }
+
+ InputState_t& state = m_InputState[ m_bIsPolling ];
+ state.m_pAnalogDelta[ code ] = value - state.m_pAnalogValue[ code ];
+ state.m_pAnalogValue[ code ] = value;
+ if ( state.m_pAnalogDelta[ code ] != 0 )
+ {
+ PostEvent(IE_AnalogValueChanged, m_nLastSampleTick, code, value, 0);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Process the event
+//-----------------------------------------------------------------------------
+void CInputSystem::JoystickButtonEvent( ButtonCode_t button, int sample )
+{
+ // Not used - we post button events from JoystickButtonPress/Release.
+}
+
+
+//-----------------------------------------------------------------------------
+// Update the joystick button state
+//-----------------------------------------------------------------------------
+void CInputSystem::UpdateJoystickButtonState( int nJoystick )
+{
+ // We don't sample - we get events posted by SDL_GameController in JoystickSDLWatcher
+}
+
+
+//-----------------------------------------------------------------------------
+// Update the joystick POV control
+//-----------------------------------------------------------------------------
+void CInputSystem::UpdateJoystickPOVControl( int nJoystick )
+{
+ // SDL GameController does not support joystick POV. Should we poll?
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sample the joystick
+//-----------------------------------------------------------------------------
+void CInputSystem::PollJoystick( void )
+{
+ // We only pump the SDL event loop if we're not an SDL app, since otherwise PollInputState_Platform calls into CSDLMgr to pump it.
+ // Our state updates happen in events posted by SDL_GameController in JoystickSDLWatcher, so the loop is empty.
+#if !defined( USE_SDL )
+ SDL_Event event;
+ int nEventsProcessed = 0;
+
+ SDL_PumpEvents();
+ while ( SDL_PollEvent( &event ) && nEventsProcessed < 100 )
+ {
+ nEventsProcessed++;
+ }
+#endif
+}
+
+void CInputSystem::SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId )
+{
+ JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
+ if ( info.m_nDeviceId < 0 || info.m_pHaptic == NULL )
+ {
+ return;
+ }
+
+ float strength = (fLeftMotor + fRightMotor) / 2.f;
+ static ConVarRef joystickVar( "joystick" );
+
+ // 0f means "stop".
+ bool shouldStop = ( strength < 0.01f );
+ // If they've disabled the gamecontroller in settings, never rumble.
+ if ( !joystickVar.IsValid() || !joystickVar.GetBool() )
+ {
+ shouldStop = true;
+ }
+
+ if ( shouldStop )
+ {
+ if ( info.m_bRumbleEnabled )
+ {
+ SDL_HapticRumbleStop( (SDL_Haptic *)info.m_pHaptic );
+ info.m_bRumbleEnabled = false;
+ info.m_fCurrentRumble = 0.0f;
+ }
+
+ return;
+ }
+
+ // If there's little change, then don't change the rumble strength.
+ if ( info.m_bRumbleEnabled && abs(info.m_fCurrentRumble - strength) < 0.01f )
+ {
+ return;
+ }
+
+ info.m_bRumbleEnabled = true;
+ info.m_fCurrentRumble = strength;
+
+ if ( SDL_HapticRumblePlay((SDL_Haptic *)info.m_pHaptic, strength, SDL_HAPTIC_INFINITY) != 0 )
+ {
+ Warning("Couldn't play rumble (strength %.1f): %s\n", strength, SDL_GetError());
+ }
+}
+
+ButtonCode_t ControllerButtonToButtonCode( SDL_GameControllerButton button )
+{
+ switch ( button )
+ {
+ case SDL_CONTROLLER_BUTTON_A: // KEY_XBUTTON_A
+ case SDL_CONTROLLER_BUTTON_B: // KEY_XBUTTON_B
+ case SDL_CONTROLLER_BUTTON_X: // KEY_XBUTTON_X
+ case SDL_CONTROLLER_BUTTON_Y: // KEY_XBUTTON_Y
+ return JOYSTICK_BUTTON(0, button);
+
+ case SDL_CONTROLLER_BUTTON_BACK:
+ return KEY_XBUTTON_BACK;
+ case SDL_CONTROLLER_BUTTON_START:
+ return KEY_XBUTTON_START;
+
+ case SDL_CONTROLLER_BUTTON_GUIDE:
+ return KEY_XBUTTON_BACK; // XXX: How are we supposed to handle this? Steam overlay etc.
+
+ case SDL_CONTROLLER_BUTTON_LEFTSTICK:
+ return KEY_XBUTTON_STICK1;
+ case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
+ return KEY_XBUTTON_STICK2;
+ case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
+ return KEY_XBUTTON_LEFT_SHOULDER;
+ case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
+ return KEY_XBUTTON_RIGHT_SHOULDER;
+
+ case SDL_CONTROLLER_BUTTON_DPAD_UP:
+ return KEY_XBUTTON_UP;
+ case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
+ return KEY_XBUTTON_DOWN;
+ case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
+ return KEY_XBUTTON_LEFT;
+ case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
+ return KEY_XBUTTON_RIGHT;
+ }
+
+ return BUTTON_CODE_NONE;
+}
+
+AnalogCode_t ControllerAxisToAnalogCode( SDL_GameControllerAxis axis )
+{
+ switch ( axis )
+ {
+ case SDL_CONTROLLER_AXIS_LEFTX:
+ return JOYSTICK_AXIS(0, JOY_AXIS_X);
+ case SDL_CONTROLLER_AXIS_LEFTY:
+ return JOYSTICK_AXIS(0, JOY_AXIS_Y);
+
+ case SDL_CONTROLLER_AXIS_RIGHTX:
+ return JOYSTICK_AXIS(0, JOY_AXIS_U);
+ case SDL_CONTROLLER_AXIS_RIGHTY:
+ return JOYSTICK_AXIS(0, JOY_AXIS_R);
+
+ case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
+ case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
+ return JOYSTICK_AXIS(0, JOY_AXIS_Z);
+ }
+
+ return ANALOG_CODE_INVALID;
+}
diff --git a/inputsystem/key_translation.cpp b/inputsystem/key_translation.cpp
new file mode 100644
index 0000000..f8474fb
--- /dev/null
+++ b/inputsystem/key_translation.cpp
@@ -0,0 +1,911 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+
+#if defined( WIN32 )
+#if !defined( _X360 )
+#include <wtypes.h>
+#include <winuser.h>
+#include "xbox/xboxstubs.h"
+#else
+#include "xbox/xbox_win32stubs.h"
+#endif
+#endif // WIN32
+
+#include "key_translation.h"
+#include "tier1/convar.h"
+#include "tier1/strtools.h"
+#include "tier0/dbg.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#if defined(__clang__)
+ #pragma GCC diagnostic ignored "-Wchar-subscripts"
+#endif
+
+static ButtonCode_t s_pVirtualKeyToButtonCode[256];
+
+static ButtonCode_t s_pSKeytoButtonCode[SK_MAX_KEYS];
+
+#if !defined( POSIX )
+static ButtonCode_t s_pXKeyTrans[XK_MAX_KEYS];
+#endif
+
+#define SCONTROLLERBUTTONS_BUTTONS( x ) \
+ "SC_A",\
+ "SC_B",\
+ "SC_X",\
+ "SC_Y",\
+ "SC_DPAD_UP",\
+ "SC_DPAD_RIGHT",\
+ "SC_DPAD_DOWN",\
+ "SC_DPAD_LEFT",\
+ "SC_LEFT_BUMPER",\
+ "SC_RIGHT_BUMPER",\
+ "SC_LEFT_TRIGGER",\
+ "SC_RIGHT_TRIGGER",\
+ "SC_LEFT_GRIP",\
+ "SC_RIGHT_GRIP",\
+ "SC_LEFT_PAD_TOUCH",\
+ "SC_RIGHT_PAD_TOUCH",\
+ "SC_LEFT_PAD_CLICK",\
+ "SC_RIGHT_PAD_CLICK",\
+ "SC_LPAD_UP",\
+ "SC_LPAD_RIGHT",\
+ "SC_LPAD_DOWN",\
+ "SC_LPAD_LEFT",\
+ "SC_RPAD_UP",\
+ "SC_RPAD_RIGHT",\
+ "SC_RPAD_DOWN",\
+ "SC_RPAD_LEFT",\
+ "SC_SELECT",\
+ "SC_START",\
+ "SC_STEAM",\
+ "SC_NULL"
+
+#define SCONTROLLERBUTTONS_AXIS( x ) \
+ "SC_LPAD_AXIS_RIGHT",\
+ "SC_LPAD_AXIS_LEFT",\
+ "SC_LPAD_AXIS_DOWN",\
+ "SC_LPAD_AXIS_UP",\
+ "SC_AXIS_L_TRIGGER",\
+ "SC_AXIS_R_TRIGGER",\
+ "SC_RPAD_AXIS_RIGHT",\
+ "SC_RPAD_AXIS_LEFT",\
+ "SC_RPAD_AXIS_DOWN",\
+ "SC_RPAD_AXIS_UP",\
+ "SC_GYRO_AXIS_PITCH_POSITIVE",\
+ "SC_GYRO_AXIS_PITCH_NEGATIVE",\
+ "SC_GYRO_AXIS_ROLL_POSITIVE",\
+ "SC_GYRO_AXIS_ROLL_NEGATIVE",\
+ "SC_GYRO_AXIS_YAW_POSITIVE",\
+ "SC_GYRO_AXIS_YAW_NEGATIVE"
+
+#define SCONTROLLERBUTTONS_VBUTTONS( x ) \
+ "SC_F1",\
+ "SC_F2",\
+ "SC_F3",\
+ "SC_F4",\
+ "SC_F5",\
+ "SC_F6",\
+ "SC_F7",\
+ "SC_F8",\
+ "SC_F9",\
+ "SC_F10",\
+ "SC_F11",\
+ "SC_F12"
+
+static int s_pButtonCodeToVirtual[BUTTON_CODE_LAST];
+
+static const char *s_pButtonCodeName[ ] =
+{
+ "", // KEY_NONE
+ "0", // KEY_0,
+ "1", // KEY_1,
+ "2", // KEY_2,
+ "3", // KEY_3,
+ "4", // KEY_4,
+ "5", // KEY_5,
+ "6", // KEY_6,
+ "7", // KEY_7,
+ "8", // KEY_8,
+ "9", // KEY_9,
+ "a", // KEY_A,
+ "b", // KEY_B,
+ "c", // KEY_C,
+ "d", // KEY_D,
+ "e", // KEY_E,
+ "f", // KEY_F,
+ "g", // KEY_G,
+ "h", // KEY_H,
+ "i", // KEY_I,
+ "j", // KEY_J,
+ "k", // KEY_K,
+ "l", // KEY_L,
+ "m", // KEY_M,
+ "n", // KEY_N,
+ "o", // KEY_O,
+ "p", // KEY_P,
+ "q", // KEY_Q,
+ "r", // KEY_R,
+ "s", // KEY_S,
+ "t", // KEY_T,
+ "u", // KEY_U,
+ "v", // KEY_V,
+ "w", // KEY_W,
+ "x", // KEY_X,
+ "y", // KEY_Y,
+ "z", // KEY_Z,
+ "KP_INS", // KEY_PAD_0,
+ "KP_END", // KEY_PAD_1,
+ "KP_DOWNARROW", // KEY_PAD_2,
+ "KP_PGDN", // KEY_PAD_3,
+ "KP_LEFTARROW", // KEY_PAD_4,
+ "KP_5", // KEY_PAD_5,
+ "KP_RIGHTARROW",// KEY_PAD_6,
+ "KP_HOME", // KEY_PAD_7,
+ "KP_UPARROW", // KEY_PAD_8,
+ "KP_PGUP", // KEY_PAD_9,
+ "KP_SLASH", // KEY_PAD_DIVIDE,
+ "KP_MULTIPLY", // KEY_PAD_MULTIPLY,
+ "KP_MINUS", // KEY_PAD_MINUS,
+ "KP_PLUS", // KEY_PAD_PLUS,
+ "KP_ENTER", // KEY_PAD_ENTER,
+ "KP_DEL", // KEY_PAD_DECIMAL,
+ "[", // KEY_LBRACKET,
+ "]", // KEY_RBRACKET,
+ "SEMICOLON", // KEY_SEMICOLON,
+ "'", // KEY_APOSTROPHE,
+ "`", // KEY_BACKQUOTE,
+ ",", // KEY_COMMA,
+ ".", // KEY_PERIOD,
+ "/", // KEY_SLASH,
+ "\\", // KEY_BACKSLASH,
+ "-", // KEY_MINUS,
+ "=", // KEY_EQUAL,
+ "ENTER", // KEY_ENTER,
+ "SPACE", // KEY_SPACE,
+ "BACKSPACE", // KEY_BACKSPACE,
+ "TAB", // KEY_TAB,
+ "CAPSLOCK", // KEY_CAPSLOCK,
+ "NUMLOCK", // KEY_NUMLOCK,
+ "ESCAPE", // KEY_ESCAPE,
+ "SCROLLLOCK", // KEY_SCROLLLOCK,
+ "INS", // KEY_INSERT,
+ "DEL", // KEY_DELETE,
+ "HOME", // KEY_HOME,
+ "END", // KEY_END,
+ "PGUP", // KEY_PAGEUP,
+ "PGDN", // KEY_PAGEDOWN,
+ "PAUSE", // KEY_BREAK,
+ "SHIFT", // KEY_LSHIFT,
+ "RSHIFT", // KEY_RSHIFT,
+ "ALT", // KEY_LALT,
+ "RALT", // KEY_RALT,
+ "CTRL", // KEY_LCONTROL,
+ "RCTRL", // KEY_RCONTROL,
+ "LWIN", // KEY_LWIN,
+ "RWIN", // KEY_RWIN,
+ "APP", // KEY_APP,
+ "UPARROW", // KEY_UP,
+ "LEFTARROW", // KEY_LEFT,
+ "DOWNARROW", // KEY_DOWN,
+ "RIGHTARROW", // KEY_RIGHT,
+ "F1", // KEY_F1,
+ "F2", // KEY_F2,
+ "F3", // KEY_F3,
+ "F4", // KEY_F4,
+ "F5", // KEY_F5,
+ "F6", // KEY_F6,
+ "F7", // KEY_F7,
+ "F8", // KEY_F8,
+ "F9", // KEY_F9,
+ "F10", // KEY_F10,
+ "F11", // KEY_F11,
+ "F12", // KEY_F12,
+
+ // FIXME: CAPSLOCK/NUMLOCK/SCROLLLOCK all appear above. What are these for?!
+ // They only appear in CInputWin32::UpdateToggleButtonState in vgui2
+ "CAPSLOCKTOGGLE", // KEY_CAPSLOCKTOGGLE,
+ "NUMLOCKTOGGLE", // KEY_NUMLOCKTOGGLE,
+ "SCROLLLOCKTOGGLE", // KEY_SCROLLLOCKTOGGLE,
+
+ // Mouse
+ "MOUSE1", // MOUSE_LEFT,
+ "MOUSE2", // MOUSE_RIGHT,
+ "MOUSE3", // MOUSE_MIDDLE,
+ "MOUSE4", // MOUSE_4,
+ "MOUSE5", // MOUSE_5,
+
+ "MWHEELUP", // MOUSE_WHEEL_UP
+ "MWHEELDOWN", // MOUSE_WHEEL_DOWN
+
+#if defined ( _X360 ) || defined ( _LINUX )
+ "A_BUTTON", // JOYSTICK_FIRST_BUTTON
+ "B_BUTTON",
+ "X_BUTTON",
+ "Y_BUTTON",
+ "L_SHOULDER",
+ "R_SHOULDER",
+ "BACK",
+ "START",
+ "STICK1",
+ "STICK2",
+#else
+ "JOY1", // JOYSTICK_FIRST_BUTTON
+ "JOY2",
+ "JOY3",
+ "JOY4",
+ "JOY5",
+ "JOY6",
+ "JOY7",
+ "JOY8",
+ "JOY9",
+ "JOY10",
+#endif
+
+ "JOY11",
+ "JOY12",
+ "JOY13",
+ "JOY14",
+ "JOY15",
+ "JOY16",
+ "JOY17",
+ "JOY18",
+ "JOY19",
+ "JOY20",
+ "JOY21",
+ "JOY22",
+ "JOY23",
+ "JOY24",
+ "JOY25",
+ "JOY26",
+ "JOY27",
+ "JOY28",
+ "JOY29",
+ "JOY30",
+ "JOY31",
+ "JOY32", // JOYSTICK_LAST_BUTTON
+
+#if defined ( _X360 )
+ "UP", // JOYSTICK_FIRST_POV_BUTTON
+ "RIGHT",
+ "DOWN",
+ "LEFT", // JOYSTICK_LAST_POV_BUTTON
+
+ "S1_RIGHT", // JOYSTICK_FIRST_AXIS_BUTTON
+ "S1_LEFT",
+ "S1_DOWN",
+ "S1_UP",
+ "L_TRIGGER",
+ "R_TRIGGER",
+ "S2_RIGHT",
+ "S2_LEFT",
+ "S2_DOWN",
+ "S2_UP", // JOYSTICK_LAST_AXIS_BUTTON
+ "V AXIS POS",
+ "V AXIS NEG",
+#else
+ "POV_UP", // JOYSTICK_FIRST_POV_BUTTON
+ "POV_RIGHT",
+ "POV_DOWN",
+ "POV_LEFT", // JOYSTICK_LAST_POV_BUTTON
+
+ "X AXIS POS", // JOYSTICK_FIRST_AXIS_BUTTON
+ "X AXIS NEG",
+ "Y AXIS POS",
+ "Y AXIS NEG",
+ "Z AXIS POS",
+ "Z AXIS NEG",
+ "R AXIS POS",
+ "R AXIS NEG",
+ "U AXIS POS",
+ "U AXIS NEG",
+ "V AXIS POS",
+ "V AXIS NEG", // JOYSTICK_LAST_AXIS_BUTTON
+ "FALCON_NULL", // NVNT temp Fix for unaligned joystick enumeration
+ "FALCON_1", // NOVINT_FIRST
+ "FALCON_2",
+ "FALCON_3",
+ "FALCON_4",
+ "FALCON2_1",
+ "FALCON2_2",
+ "FALCON2_3",
+ "FALCON2_4", // NOVINT_LAST
+#endif
+
+ SCONTROLLERBUTTONS_BUTTONS( 0 ),
+ SCONTROLLERBUTTONS_BUTTONS( 1 ),
+ SCONTROLLERBUTTONS_BUTTONS( 2 ),
+ SCONTROLLERBUTTONS_BUTTONS( 3 ),
+ SCONTROLLERBUTTONS_BUTTONS( 4 ),
+ SCONTROLLERBUTTONS_BUTTONS( 5 ),
+ SCONTROLLERBUTTONS_BUTTONS( 6 ),
+ SCONTROLLERBUTTONS_BUTTONS( 7 ),
+
+ SCONTROLLERBUTTONS_AXIS( 0 ),
+ SCONTROLLERBUTTONS_AXIS( 1 ),
+ SCONTROLLERBUTTONS_AXIS( 2 ),
+ SCONTROLLERBUTTONS_AXIS( 3 ),
+ SCONTROLLERBUTTONS_AXIS( 4 ),
+ SCONTROLLERBUTTONS_AXIS( 5 ),
+ SCONTROLLERBUTTONS_AXIS( 6 ),
+ SCONTROLLERBUTTONS_AXIS( 7 ),
+
+ SCONTROLLERBUTTONS_VBUTTONS( 0 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 1 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 2 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 3 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 4 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 5 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 6 ),
+ SCONTROLLERBUTTONS_VBUTTONS( 7 ),
+};
+
+static const char *s_pAnalogCodeName[ ] =
+{
+ "MOUSE_X", // MOUSE_X = 0,
+ "MOUSE_Y", // MOUSE_Y,
+ "MOUSE_XY", // MOUSE_XY, // Invoked when either x or y changes
+ "MOUSE_WHEEL", // MOUSE_WHEEL,
+
+ "X AXIS", // JOY_AXIS_X
+ "Y AXIS", // JOY_AXIS_Y
+ "Z AXIS", // JOY_AXIS_Z
+ "R AXIS", // JOY_AXIS_R
+ "U AXIS", // JOY_AXIS_U
+ "V AXIS", // JOY_AXIS_V
+};
+
+#if !defined ( _X360 )
+static const char *s_pXControllerButtonCodeNames[ ] =
+{
+ "A_BUTTON", // JOYSTICK_FIRST_BUTTON
+ "B_BUTTON",
+ "X_BUTTON",
+ "Y_BUTTON",
+ "L_SHOULDER",
+ "R_SHOULDER",
+ "BACK",
+ "START",
+ "STICK1",
+ "STICK2",
+ "JOY11",
+ "JOY12",
+ "JOY13",
+ "JOY14",
+ "JOY15",
+ "JOY16",
+ "JOY17",
+ "JOY18",
+ "JOY19",
+ "JOY20",
+ "JOY21",
+ "JOY22",
+ "JOY23",
+ "JOY24",
+ "JOY25",
+ "JOY26",
+ "JOY27",
+ "JOY28",
+ "JOY29",
+ "JOY30",
+ "JOY31",
+ "JOY32", // JOYSTICK_LAST_BUTTON
+
+ "UP", // JOYSTICK_FIRST_POV_BUTTON
+ "RIGHT",
+ "DOWN",
+ "LEFT", // JOYSTICK_LAST_POV_BUTTON
+
+ "S1_RIGHT", // JOYSTICK_FIRST_AXIS_BUTTON
+ "S1_LEFT",
+ "S1_DOWN",
+ "S1_UP",
+ "L_TRIGGER",
+ "R_TRIGGER",
+ "S2_RIGHT",
+ "S2_LEFT",
+ "S2_DOWN",
+ "S2_UP", // JOYSTICK_LAST_AXIS_BUTTON
+ "V AXIS POS",
+ "V AXIS NEG",
+};
+#endif
+
+// this maps non-translated keyboard scan codes to engine key codes
+// Google for 'Keyboard Scan Code Specification'
+static ButtonCode_t s_pScanToButtonCode_QWERTY[128] =
+{
+ // 0 1 2 3 4 5 6 7
+ // 8 9 A B C D E F
+ KEY_NONE, KEY_ESCAPE, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, // 0
+ KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB, // 0
+
+ KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, // 1
+ KEY_O, KEY_P, KEY_LBRACKET, KEY_RBRACKET, KEY_ENTER, KEY_LCONTROL, KEY_A, KEY_S, // 1
+
+ KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON, // 2
+ KEY_APOSTROPHE, KEY_BACKQUOTE, KEY_LSHIFT, KEY_BACKSLASH, KEY_Z, KEY_X, KEY_C, KEY_V, // 2
+
+ KEY_B, KEY_N, KEY_M, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_RSHIFT, KEY_PAD_MULTIPLY,// 3
+ KEY_LALT, KEY_SPACE, KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, // 3
+
+ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_HOME, // 4
+ KEY_UP, KEY_PAGEUP, KEY_PAD_MINUS, KEY_LEFT, KEY_PAD_5, KEY_RIGHT, KEY_PAD_PLUS, KEY_END, // 4
+
+ KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_F11, // 5
+ KEY_F12, KEY_BREAK, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 5
+
+ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 6
+ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 6
+
+ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, // 7
+ KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE, KEY_NONE // 7
+};
+
+static ButtonCode_t s_pScanToButtonCode[128];
+
+
+void ButtonCode_InitKeyTranslationTable()
+{
+ COMPILE_TIME_ASSERT( sizeof(s_pButtonCodeName) / sizeof( const char * ) == BUTTON_CODE_LAST );
+ COMPILE_TIME_ASSERT( sizeof(s_pAnalogCodeName) / sizeof( const char * ) == ANALOG_CODE_LAST );
+
+ // set virtual key translation table
+ memset( s_pVirtualKeyToButtonCode, KEY_NONE, sizeof(s_pVirtualKeyToButtonCode) );
+
+ s_pVirtualKeyToButtonCode['0'] =KEY_0;
+ s_pVirtualKeyToButtonCode['1'] =KEY_1;
+ s_pVirtualKeyToButtonCode['2'] =KEY_2;
+ s_pVirtualKeyToButtonCode['3'] =KEY_3;
+ s_pVirtualKeyToButtonCode['4'] =KEY_4;
+ s_pVirtualKeyToButtonCode['5'] =KEY_5;
+ s_pVirtualKeyToButtonCode['6'] =KEY_6;
+ s_pVirtualKeyToButtonCode['7'] =KEY_7;
+ s_pVirtualKeyToButtonCode['8'] =KEY_8;
+ s_pVirtualKeyToButtonCode['9'] =KEY_9;
+ s_pVirtualKeyToButtonCode['A'] =KEY_A;
+ s_pVirtualKeyToButtonCode['B'] =KEY_B;
+ s_pVirtualKeyToButtonCode['C'] =KEY_C;
+ s_pVirtualKeyToButtonCode['D'] =KEY_D;
+ s_pVirtualKeyToButtonCode['E'] =KEY_E;
+ s_pVirtualKeyToButtonCode['F'] =KEY_F;
+ s_pVirtualKeyToButtonCode['G'] =KEY_G;
+ s_pVirtualKeyToButtonCode['H'] =KEY_H;
+ s_pVirtualKeyToButtonCode['I'] =KEY_I;
+ s_pVirtualKeyToButtonCode['J'] =KEY_J;
+ s_pVirtualKeyToButtonCode['K'] =KEY_K;
+ s_pVirtualKeyToButtonCode['L'] =KEY_L;
+ s_pVirtualKeyToButtonCode['M'] =KEY_M;
+ s_pVirtualKeyToButtonCode['N'] =KEY_N;
+ s_pVirtualKeyToButtonCode['O'] =KEY_O;
+ s_pVirtualKeyToButtonCode['P'] =KEY_P;
+ s_pVirtualKeyToButtonCode['Q'] =KEY_Q;
+ s_pVirtualKeyToButtonCode['R'] =KEY_R;
+ s_pVirtualKeyToButtonCode['S'] =KEY_S;
+ s_pVirtualKeyToButtonCode['T'] =KEY_T;
+ s_pVirtualKeyToButtonCode['U'] =KEY_U;
+ s_pVirtualKeyToButtonCode['V'] =KEY_V;
+ s_pVirtualKeyToButtonCode['W'] =KEY_W;
+ s_pVirtualKeyToButtonCode['X'] =KEY_X;
+ s_pVirtualKeyToButtonCode['Y'] =KEY_Y;
+ s_pVirtualKeyToButtonCode['Z'] =KEY_Z;
+#if !defined( POSIX )
+ s_pVirtualKeyToButtonCode[VK_NUMPAD0] =KEY_PAD_0;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD1] =KEY_PAD_1;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD2] =KEY_PAD_2;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD3] =KEY_PAD_3;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD4] =KEY_PAD_4;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD5] =KEY_PAD_5;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD6] =KEY_PAD_6;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD7] =KEY_PAD_7;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD8] =KEY_PAD_8;
+ s_pVirtualKeyToButtonCode[VK_NUMPAD9] =KEY_PAD_9;
+ s_pVirtualKeyToButtonCode[VK_DIVIDE] =KEY_PAD_DIVIDE;
+ s_pVirtualKeyToButtonCode[VK_MULTIPLY] =KEY_PAD_MULTIPLY;
+ s_pVirtualKeyToButtonCode[VK_SUBTRACT] =KEY_PAD_MINUS;
+ s_pVirtualKeyToButtonCode[VK_ADD] =KEY_PAD_PLUS;
+ s_pVirtualKeyToButtonCode[VK_RETURN] =KEY_PAD_ENTER;
+ s_pVirtualKeyToButtonCode[VK_DECIMAL] =KEY_PAD_DECIMAL;
+#endif
+ s_pVirtualKeyToButtonCode[0xdb] =KEY_LBRACKET;
+ s_pVirtualKeyToButtonCode[0xdd] =KEY_RBRACKET;
+ s_pVirtualKeyToButtonCode[0xba] =KEY_SEMICOLON;
+ s_pVirtualKeyToButtonCode[0xde] =KEY_APOSTROPHE;
+ s_pVirtualKeyToButtonCode[0xc0] =KEY_BACKQUOTE;
+ s_pVirtualKeyToButtonCode[0xbc] =KEY_COMMA;
+ s_pVirtualKeyToButtonCode[0xbe] =KEY_PERIOD;
+ s_pVirtualKeyToButtonCode[0xbf] =KEY_SLASH;
+ s_pVirtualKeyToButtonCode[0xdc] =KEY_BACKSLASH;
+ s_pVirtualKeyToButtonCode[0xbd] =KEY_MINUS;
+ s_pVirtualKeyToButtonCode[0xbb] =KEY_EQUAL;
+#if !defined( POSIX )
+ s_pVirtualKeyToButtonCode[VK_RETURN] =KEY_ENTER;
+ s_pVirtualKeyToButtonCode[VK_SPACE] =KEY_SPACE;
+ s_pVirtualKeyToButtonCode[VK_BACK] =KEY_BACKSPACE;
+ s_pVirtualKeyToButtonCode[VK_TAB] =KEY_TAB;
+ s_pVirtualKeyToButtonCode[VK_CAPITAL] =KEY_CAPSLOCK;
+ s_pVirtualKeyToButtonCode[VK_NUMLOCK] =KEY_NUMLOCK;
+ s_pVirtualKeyToButtonCode[VK_ESCAPE] =KEY_ESCAPE;
+ s_pVirtualKeyToButtonCode[VK_SCROLL] =KEY_SCROLLLOCK;
+ s_pVirtualKeyToButtonCode[VK_INSERT] =KEY_INSERT;
+ s_pVirtualKeyToButtonCode[VK_DELETE] =KEY_DELETE;
+ s_pVirtualKeyToButtonCode[VK_HOME] =KEY_HOME;
+ s_pVirtualKeyToButtonCode[VK_END] =KEY_END;
+ s_pVirtualKeyToButtonCode[VK_PRIOR] =KEY_PAGEUP;
+ s_pVirtualKeyToButtonCode[VK_NEXT] =KEY_PAGEDOWN;
+ s_pVirtualKeyToButtonCode[VK_PAUSE] =KEY_BREAK;
+ s_pVirtualKeyToButtonCode[VK_SHIFT] =KEY_RSHIFT;
+ s_pVirtualKeyToButtonCode[VK_SHIFT] =KEY_LSHIFT; // SHIFT -> left SHIFT
+ s_pVirtualKeyToButtonCode[VK_MENU] =KEY_RALT;
+ s_pVirtualKeyToButtonCode[VK_MENU] =KEY_LALT; // ALT -> left ALT
+ s_pVirtualKeyToButtonCode[VK_CONTROL] =KEY_RCONTROL;
+ s_pVirtualKeyToButtonCode[VK_CONTROL] =KEY_LCONTROL; // CTRL -> left CTRL
+ s_pVirtualKeyToButtonCode[VK_LWIN] =KEY_LWIN;
+ s_pVirtualKeyToButtonCode[VK_RWIN] =KEY_RWIN;
+ s_pVirtualKeyToButtonCode[VK_APPS] =KEY_APP;
+ s_pVirtualKeyToButtonCode[VK_UP] =KEY_UP;
+ s_pVirtualKeyToButtonCode[VK_LEFT] =KEY_LEFT;
+ s_pVirtualKeyToButtonCode[VK_DOWN] =KEY_DOWN;
+ s_pVirtualKeyToButtonCode[VK_RIGHT] =KEY_RIGHT;
+ s_pVirtualKeyToButtonCode[VK_F1] =KEY_F1;
+ s_pVirtualKeyToButtonCode[VK_F2] =KEY_F2;
+ s_pVirtualKeyToButtonCode[VK_F3] =KEY_F3;
+ s_pVirtualKeyToButtonCode[VK_F4] =KEY_F4;
+ s_pVirtualKeyToButtonCode[VK_F5] =KEY_F5;
+ s_pVirtualKeyToButtonCode[VK_F6] =KEY_F6;
+ s_pVirtualKeyToButtonCode[VK_F7] =KEY_F7;
+ s_pVirtualKeyToButtonCode[VK_F8] =KEY_F8;
+ s_pVirtualKeyToButtonCode[VK_F9] =KEY_F9;
+ s_pVirtualKeyToButtonCode[VK_F10] =KEY_F10;
+ s_pVirtualKeyToButtonCode[VK_F11] =KEY_F11;
+ s_pVirtualKeyToButtonCode[VK_F12] =KEY_F12;
+#endif
+
+ // init the xkey translation table
+#if !defined( POSIX )
+ s_pXKeyTrans[XK_NULL] = KEY_NONE;
+ s_pXKeyTrans[XK_BUTTON_UP] = KEY_XBUTTON_UP;
+ s_pXKeyTrans[XK_BUTTON_DOWN] = KEY_XBUTTON_DOWN;
+ s_pXKeyTrans[XK_BUTTON_LEFT] = KEY_XBUTTON_LEFT;
+ s_pXKeyTrans[XK_BUTTON_RIGHT] = KEY_XBUTTON_RIGHT;
+ s_pXKeyTrans[XK_BUTTON_START] = KEY_XBUTTON_START;
+ s_pXKeyTrans[XK_BUTTON_BACK] = KEY_XBUTTON_BACK;
+ s_pXKeyTrans[XK_BUTTON_STICK1] = KEY_XBUTTON_STICK1;
+ s_pXKeyTrans[XK_BUTTON_STICK2] = KEY_XBUTTON_STICK2;
+ s_pXKeyTrans[XK_BUTTON_A] = KEY_XBUTTON_A;
+ s_pXKeyTrans[XK_BUTTON_B] = KEY_XBUTTON_B;
+ s_pXKeyTrans[XK_BUTTON_X] = KEY_XBUTTON_X;
+ s_pXKeyTrans[XK_BUTTON_Y] = KEY_XBUTTON_Y;
+ s_pXKeyTrans[XK_BUTTON_LEFT_SHOULDER] = KEY_XBUTTON_LEFT_SHOULDER;
+ s_pXKeyTrans[XK_BUTTON_RIGHT_SHOULDER] = KEY_XBUTTON_RIGHT_SHOULDER;
+ s_pXKeyTrans[XK_BUTTON_LTRIGGER] = KEY_XBUTTON_LTRIGGER;
+ s_pXKeyTrans[XK_BUTTON_RTRIGGER] = KEY_XBUTTON_RTRIGGER;
+ s_pXKeyTrans[XK_STICK1_UP] = KEY_XSTICK1_UP;
+ s_pXKeyTrans[XK_STICK1_DOWN] = KEY_XSTICK1_DOWN;
+ s_pXKeyTrans[XK_STICK1_LEFT] = KEY_XSTICK1_LEFT;
+ s_pXKeyTrans[XK_STICK1_RIGHT] = KEY_XSTICK1_RIGHT;
+ s_pXKeyTrans[XK_STICK2_UP] = KEY_XSTICK2_UP;
+ s_pXKeyTrans[XK_STICK2_DOWN] = KEY_XSTICK2_DOWN;
+ s_pXKeyTrans[XK_STICK2_LEFT] = KEY_XSTICK2_LEFT;
+ s_pXKeyTrans[XK_STICK2_RIGHT] = KEY_XSTICK2_RIGHT;
+#endif // PLATFORM_POSIX
+
+ // create reverse table engine to virtual
+ for ( int i = 0; i < ARRAYSIZE( s_pVirtualKeyToButtonCode ); i++ )
+ {
+ s_pButtonCodeToVirtual[ s_pVirtualKeyToButtonCode[i] ] = i;
+ }
+
+ s_pButtonCodeToVirtual[0] = 0;
+
+ s_pSKeytoButtonCode[SK_NULL] = KEY_NONE;
+ s_pSKeytoButtonCode[SK_BUTTON_A] = STEAMCONTROLLER_A;
+ s_pSKeytoButtonCode[SK_BUTTON_B] = STEAMCONTROLLER_B;
+ s_pSKeytoButtonCode[SK_BUTTON_X] = STEAMCONTROLLER_X;
+ s_pSKeytoButtonCode[SK_BUTTON_Y] = STEAMCONTROLLER_Y;
+ s_pSKeytoButtonCode[SK_BUTTON_UP] = STEAMCONTROLLER_DPAD_UP;
+ s_pSKeytoButtonCode[SK_BUTTON_RIGHT] = STEAMCONTROLLER_DPAD_RIGHT;
+ s_pSKeytoButtonCode[SK_BUTTON_DOWN] = STEAMCONTROLLER_DPAD_DOWN;
+ s_pSKeytoButtonCode[SK_BUTTON_LEFT] = STEAMCONTROLLER_DPAD_LEFT;
+ s_pSKeytoButtonCode[SK_BUTTON_LEFT_BUMPER] = STEAMCONTROLLER_LEFT_BUMPER;
+ s_pSKeytoButtonCode[SK_BUTTON_RIGHT_BUMPER] = STEAMCONTROLLER_RIGHT_BUMPER;
+ s_pSKeytoButtonCode[SK_BUTTON_LEFT_TRIGGER] = STEAMCONTROLLER_LEFT_TRIGGER;
+ s_pSKeytoButtonCode[SK_BUTTON_RIGHT_TRIGGER] = STEAMCONTROLLER_RIGHT_TRIGGER;
+ s_pSKeytoButtonCode[SK_BUTTON_LEFT_GRIP] = STEAMCONTROLLER_LEFT_GRIP;
+ s_pSKeytoButtonCode[SK_BUTTON_RIGHT_GRIP] = STEAMCONTROLLER_RIGHT_GRIP;
+ s_pSKeytoButtonCode[SK_BUTTON_LPAD_TOUCH] = STEAMCONTROLLER_LEFT_PAD_FINGERDOWN;
+ s_pSKeytoButtonCode[SK_BUTTON_RPAD_TOUCH] = STEAMCONTROLLER_RIGHT_PAD_FINGERDOWN;
+ s_pSKeytoButtonCode[SK_BUTTON_LPAD_CLICK] = STEAMCONTROLLER_LEFT_PAD_CLICK;
+ s_pSKeytoButtonCode[SK_BUTTON_RPAD_CLICK] = STEAMCONTROLLER_RIGHT_PAD_CLICK;
+ s_pSKeytoButtonCode[SK_BUTTON_LPAD_UP] = STEAMCONTROLLER_LEFT_PAD_UP;
+ s_pSKeytoButtonCode[SK_BUTTON_LPAD_RIGHT] = STEAMCONTROLLER_LEFT_PAD_RIGHT;
+ s_pSKeytoButtonCode[SK_BUTTON_LPAD_DOWN] = STEAMCONTROLLER_LEFT_PAD_DOWN;
+ s_pSKeytoButtonCode[SK_BUTTON_LPAD_LEFT] = STEAMCONTROLLER_LEFT_PAD_LEFT;
+ s_pSKeytoButtonCode[SK_BUTTON_RPAD_UP] = STEAMCONTROLLER_RIGHT_PAD_UP;
+ s_pSKeytoButtonCode[SK_BUTTON_RPAD_RIGHT] = STEAMCONTROLLER_RIGHT_PAD_RIGHT;
+ s_pSKeytoButtonCode[SK_BUTTON_RPAD_DOWN] = STEAMCONTROLLER_RIGHT_PAD_DOWN;
+ s_pSKeytoButtonCode[SK_BUTTON_RPAD_LEFT] = STEAMCONTROLLER_RIGHT_PAD_LEFT;
+ s_pSKeytoButtonCode[SK_BUTTON_SELECT] = STEAMCONTROLLER_SELECT;
+ s_pSKeytoButtonCode[SK_BUTTON_START] = STEAMCONTROLLER_START;
+ s_pSKeytoButtonCode[SK_BUTTON_STEAM] = STEAMCONTROLLER_STEAM;
+ s_pSKeytoButtonCode[SK_BUTTON_INACTIVE_START] = STEAMCONTROLLER_INACTIVE_START;
+
+ // These are fake ("virtual") steam controller buttons that don't physically exist, but we can manufacture to make internal routing
+ // to old school UI (which is expecting button code rather than actions) without clashing with other butt
+ s_pSKeytoButtonCode[SK_VBUTTON_F1] = STEAMCONTROLLER_F1;
+ s_pSKeytoButtonCode[SK_VBUTTON_F2] = STEAMCONTROLLER_F2;
+ s_pSKeytoButtonCode[SK_VBUTTON_F3] = STEAMCONTROLLER_F3;
+ s_pSKeytoButtonCode[SK_VBUTTON_F4] = STEAMCONTROLLER_F4;
+ s_pSKeytoButtonCode[SK_VBUTTON_F5] = STEAMCONTROLLER_F5;
+ s_pSKeytoButtonCode[SK_VBUTTON_F6] = STEAMCONTROLLER_F6;
+ s_pSKeytoButtonCode[SK_VBUTTON_F7] = STEAMCONTROLLER_F7;
+ s_pSKeytoButtonCode[SK_VBUTTON_F8] = STEAMCONTROLLER_F8;
+ s_pSKeytoButtonCode[SK_VBUTTON_F9] = STEAMCONTROLLER_F9;
+ s_pSKeytoButtonCode[SK_VBUTTON_F10] = STEAMCONTROLLER_F10;
+ s_pSKeytoButtonCode[SK_VBUTTON_F11] = STEAMCONTROLLER_F11;
+ s_pSKeytoButtonCode[SK_VBUTTON_F12] = STEAMCONTROLLER_F12;
+}
+
+ButtonCode_t ButtonCode_VirtualKeyToButtonCode( int keyCode )
+{
+ if ( keyCode < 0 || keyCode >= sizeof( s_pVirtualKeyToButtonCode ) / sizeof( s_pVirtualKeyToButtonCode[0] ) )
+ {
+ Assert( false );
+ return KEY_NONE;
+ }
+ return s_pVirtualKeyToButtonCode[keyCode];
+}
+
+int ButtonCode_ButtonCodeToVirtualKey( ButtonCode_t code )
+{
+ return s_pButtonCodeToVirtual[code];
+}
+
+ButtonCode_t ButtonCode_XKeyToButtonCode( int nPort, int keyCode )
+{
+#if !defined( POSIX )
+ if ( keyCode < 0 || keyCode >= sizeof( s_pXKeyTrans ) / sizeof( s_pXKeyTrans[0] ) )
+ {
+ Assert( false );
+ return KEY_NONE;
+ }
+
+ ButtonCode_t code = s_pXKeyTrans[keyCode];
+ if ( IsJoystickButtonCode( code ) )
+ {
+ int nOffset = code - JOYSTICK_FIRST_BUTTON;
+ return JOYSTICK_BUTTON( nPort, nOffset );
+ }
+
+ if ( IsJoystickPOVCode( code ) )
+ {
+ int nOffset = code - JOYSTICK_FIRST_POV_BUTTON;
+ return JOYSTICK_POV_BUTTON( nPort, nOffset );
+ }
+
+ if ( IsJoystickAxisCode( code ) )
+ {
+ int nOffset = code - JOYSTICK_FIRST_AXIS_BUTTON;
+ return JOYSTICK_AXIS_BUTTON( nPort, nOffset );
+ }
+
+ return code;
+#else // POSIX
+ return KEY_NONE;
+#endif // POSIX
+}
+
+// Convert back + forth between ButtonCode/AnalogCode + strings
+const char *ButtonCode_ButtonCodeToString( ButtonCode_t code, bool bXController )
+{
+#if !defined ( _X360 )
+ if ( bXController && code >= JOYSTICK_FIRST_BUTTON && code <= JOYSTICK_LAST_AXIS_BUTTON )
+ return s_pXControllerButtonCodeNames[ code - JOYSTICK_FIRST_BUTTON ];
+#endif
+
+ return s_pButtonCodeName[ code ];
+}
+
+const char *AnalogCode_AnalogCodeToString( AnalogCode_t code )
+{
+ return s_pAnalogCodeName[ code ];
+}
+
+ButtonCode_t ButtonCode_StringToButtonCode( const char *pString, bool bXController )
+{
+ if ( !pString || !pString[0] )
+ return BUTTON_CODE_INVALID;
+
+ // Backward compat for screwed up previous joystick button names
+ if ( !Q_strnicmp( pString, "aux", 3 ) )
+ {
+ int nIndex = atoi( &pString[3] );
+ if ( nIndex < 29 )
+ return JOYSTICK_BUTTON( 0, nIndex );
+ if ( ( nIndex >= 29 ) && ( nIndex <= 32 ) )
+ return JOYSTICK_POV_BUTTON( 0, nIndex - 29 );
+ return BUTTON_CODE_INVALID;
+ }
+
+ for ( int i = 0; i < BUTTON_CODE_LAST; ++i )
+ {
+ if ( !Q_stricmp( s_pButtonCodeName[i], pString ) )
+ return (ButtonCode_t)i;
+ }
+
+#if !defined ( _X360 )
+ if ( bXController )
+ {
+ for ( int i = 0; i < ARRAYSIZE(s_pXControllerButtonCodeNames); ++i )
+ {
+ if ( !Q_stricmp( s_pXControllerButtonCodeNames[i], pString ) )
+ return (ButtonCode_t)(JOYSTICK_FIRST_BUTTON + i);
+ }
+ }
+#endif
+
+ return BUTTON_CODE_INVALID;
+}
+
+ButtonCode_t ButtonCode_SKeyToButtonCode( int nPort, int keyCode )
+{
+#if !defined( _GAMECONSOLE )
+ if ( keyCode < 0 || keyCode >= sizeof( s_pSKeytoButtonCode ) / sizeof( s_pSKeytoButtonCode[0] ) )
+ {
+ Assert( false );
+ return KEY_NONE;
+ }
+
+ ButtonCode_t code = s_pSKeytoButtonCode[keyCode];
+ // if ( IsSteamControllerCode( code ) )
+ // {
+ // // Need Per Controller Offset here.
+ // return code;
+ // }
+
+ if ( IsSteamControllerButtonCode( code ) )
+ {
+ int nOffset = code - STEAMCONTROLLER_FIRST_BUTTON;
+ return STEAMCONTROLLER_BUTTON( nPort, nOffset );
+ }
+
+ if ( IsSteamControllerAxisCode( code ) )
+ {
+ int nOffset = code - STEAMCONTROLLER_FIRST_AXIS_BUTTON;
+ return STEAMCONTROLLER_AXIS_BUTTON( nPort, nOffset );
+ }
+
+ return code;
+#else // _GAMECONSOLE
+ return KEY_NONE;
+#endif // _GAMECONSOLE
+}
+
+AnalogCode_t AnalogCode_StringToAnalogCode( const char *pString )
+{
+ if ( !pString || !pString[0] )
+ return ANALOG_CODE_INVALID;
+
+ for ( int i = 0; i < ANALOG_CODE_LAST; ++i )
+ {
+ if ( !Q_stricmp( s_pAnalogCodeName[i], pString ) )
+ return (AnalogCode_t)i;
+ }
+
+ return ANALOG_CODE_INVALID;
+}
+
+ButtonCode_t ButtonCode_ScanCodeToButtonCode( int lParam )
+{
+ int nScanCode = ( lParam >> 16 ) & 0xFF;
+ if ( nScanCode > 127 )
+ return KEY_NONE;
+
+ ButtonCode_t result = s_pScanToButtonCode[nScanCode];
+
+ bool bIsExtended = ( lParam & ( 1 << 24 ) ) != 0;
+ if ( !bIsExtended )
+ {
+ switch ( result )
+ {
+ case KEY_HOME:
+ return KEY_PAD_7;
+ case KEY_UP:
+ return KEY_PAD_8;
+ case KEY_PAGEUP:
+ return KEY_PAD_9;
+ case KEY_LEFT:
+ return KEY_PAD_4;
+ case KEY_RIGHT:
+ return KEY_PAD_6;
+ case KEY_END:
+ return KEY_PAD_1;
+ case KEY_DOWN:
+ return KEY_PAD_2;
+ case KEY_PAGEDOWN:
+ return KEY_PAD_3;
+ case KEY_INSERT:
+ return KEY_PAD_0;
+ case KEY_DELETE:
+ return KEY_PAD_DECIMAL;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch ( result )
+ {
+ case KEY_ENTER:
+ return KEY_PAD_ENTER;
+ case KEY_LALT:
+ return KEY_RALT;
+ case KEY_LCONTROL:
+ return KEY_RCONTROL;
+ case KEY_SLASH:
+ return KEY_PAD_DIVIDE;
+ case KEY_CAPSLOCK:
+ return KEY_PAD_PLUS;
+ }
+ }
+
+ return result;
+}
+
+
+//-----------------------------------------------------------------------------
+// Update scan codes for foreign keyboards
+//-----------------------------------------------------------------------------
+void ButtonCode_UpdateScanCodeLayout( )
+{
+ // reset the keyboard
+ memcpy( s_pScanToButtonCode, s_pScanToButtonCode_QWERTY, sizeof(s_pScanToButtonCode) );
+
+#if !defined( _X360 ) && !defined( POSIX )
+ // fix up keyboard layout for other languages
+ HKL currentKb = ::GetKeyboardLayout( 0 );
+ HKL englishKb = ::LoadKeyboardLayout("00000409", 0);
+
+ if (englishKb && englishKb != currentKb)
+ {
+ for ( int i = 0; i < ARRAYSIZE(s_pScanToButtonCode); i++ )
+ {
+ // take the english/QWERTY
+ ButtonCode_t code = s_pScanToButtonCode_QWERTY[ i ];
+
+ // only remap printable keys
+ if ( code != KEY_NONE && code != KEY_BACKQUOTE && ( IsAlphaNumeric( code ) || IsPunctuation( code ) ) )
+ {
+ // get it's virtual key based on the old layout
+ int vk = ::MapVirtualKeyEx( i, 1, englishKb );
+
+ // turn in into a scancode on the new layout
+ int newScanCode = ::MapVirtualKeyEx( vk, 0, currentKb );
+
+ // strip off any high bits
+ newScanCode &= 0x0000007F;
+
+ // set in the new layout
+ s_pScanToButtonCode[newScanCode] = code;
+ }
+ }
+ }
+
+ s_pScanToButtonCode[0] = KEY_NONE;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Updates the current keyboard layout
+//-----------------------------------------------------------------------------
+CON_COMMAND( key_updatelayout, "Updates game keyboard layout to current windows keyboard setting." )
+{
+ ButtonCode_UpdateScanCodeLayout();
+}
diff --git a/inputsystem/key_translation.h b/inputsystem/key_translation.h
new file mode 100644
index 0000000..97ee7dd
--- /dev/null
+++ b/inputsystem/key_translation.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#ifndef KEY_TRANSLATION_H
+#define KEY_TRANSLATION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "inputsystem/ButtonCode.h"
+#include "inputsystem/AnalogCode.h"
+
+// Call this to initialize the system
+void ButtonCode_InitKeyTranslationTable();
+
+// Convert from Windows scan codes to Button codes.
+ButtonCode_t ButtonCode_ScanCodeToButtonCode( int lParam );
+
+// Update scan codes for foreign keyboards
+void ButtonCode_UpdateScanCodeLayout( );
+
+// Convert from Windows virtual key codes to Button codes.
+ButtonCode_t ButtonCode_VirtualKeyToButtonCode( int keyCode );
+int ButtonCode_ButtonCodeToVirtualKey( ButtonCode_t code );
+
+ButtonCode_t ButtonCode_XKeyToButtonCode( int nPort, int keyCode );
+
+// Convert back + forth between ButtonCode/AnalogCode + strings
+const char *ButtonCode_ButtonCodeToString( ButtonCode_t code, bool bXController );
+const char *AnalogCode_AnalogCodeToString( AnalogCode_t code );
+ButtonCode_t ButtonCode_StringToButtonCode( const char *pString, bool bXController );
+AnalogCode_t AnalogCode_StringToAnalogCode( const char *pString );
+ButtonCode_t ButtonCode_SKeyToButtonCode( int nPort, int keyCode );
+
+#endif // KEY_TRANSLATION_H
diff --git a/inputsystem/novint.cpp b/inputsystem/novint.cpp
new file mode 100644
index 0000000..32bd481
--- /dev/null
+++ b/inputsystem/novint.cpp
@@ -0,0 +1,138 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "inputsystem.h"
+#include "key_translation.h"
+#include "inputsystem/ButtonCode.h"
+#include "inputsystem/AnalogCode.h"
+#include "tier1/convar.h"
+
+typedef void*(*NovintGetIHaptics_t)(void);
+typedef bool (*NovintAttemptHWND_t)(void *hWnd);
+typedef void (*NovintDisableHWND_t)(void);
+typedef void (*NovintPollDevices_t)(void);
+typedef bool (*NovintButtonState_t)(int nDevice,
+ int &down,
+ int &pressed,
+ int &released);
+typedef int (*NovintDeviceCount_t)();
+typedef int (*NovintButtonCount_t)(int nDevice);
+typedef bool (__cdecl *NovintMouseModeCallback_t) (void);
+typedef void (*NovintInputActive_t)(bool bGameInput);
+typedef __int64 (*NovintGetDeviceID_t)(int nDevice);
+
+NovintGetIHaptics_t NovintGetIHaptics = NULL;
+NovintAttemptHWND_t NovintAttemptHWND = NULL;
+NovintDisableHWND_t NovintDisableHWND = NULL;
+NovintPollDevices_t NovintPollDevices = NULL;
+NovintButtonState_t NovintButtonState = NULL;
+NovintDeviceCount_t NovintDeviceCount = NULL;
+NovintButtonCount_t NovintButtonCount = NULL;
+NovintInputActive_t NovintInputActive = NULL;
+NovintGetDeviceID_t NovintGetDeviceID = NULL;
+
+static CInputSystem *pNovintInputSystem = 0;
+
+static bool bNovintPure = false;
+
+bool __cdecl NovintDevicesInputMode(void)
+{
+ if(pNovintInputSystem)
+ {
+ return !bNovintPure;
+ }
+ return false;
+}
+
+void *CInputSystem::GetHapticsInterfaceAddress(void)const
+{
+ if( NovintGetIHaptics )
+ return NovintGetIHaptics();
+
+ return NULL;
+}
+
+void CInputSystem::AttachWindowToNovintDevices( void * hWnd )
+{
+ if( NovintAttemptHWND )
+ NovintAttemptHWND( hWnd );
+}
+
+void CInputSystem::DetachWindowFromNovintDevices(void)
+{
+ if( NovintDisableHWND )
+ NovintDisableHWND();
+}
+
+void CInputSystem::InitializeNovintDevices()
+{
+ pNovintInputSystem = this;
+ // assume no novint devices
+ m_bNovintDevices = false;
+ m_nNovintDeviceCount = 0;
+
+ if(!m_pNovintDLL)
+ return;
+
+ NovintGetIHaptics = (NovintGetIHaptics_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintGetIHaptics" );
+ NovintAttemptHWND = (NovintAttemptHWND_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintAttemptHWND" );
+ NovintDisableHWND = (NovintDisableHWND_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintDisableHWND" );
+ NovintPollDevices = (NovintPollDevices_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintPollDevices" );
+ NovintButtonState = (NovintButtonState_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintButtonState" );
+ NovintDeviceCount = (NovintDeviceCount_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintDeviceCount" );
+ NovintButtonCount = (NovintButtonCount_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintButtonCount" );
+ NovintGetDeviceID = (NovintGetDeviceID_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintGetDeviceID" );
+ NovintInputActive = (NovintInputActive_t)GetProcAddress( (HMODULE)m_pNovintDLL, "NovintInputActive" );
+
+
+ m_nNovintDeviceCount = NovintDeviceCount();
+
+ if( m_nNovintDeviceCount > 0 )
+ m_bNovintDevices = true;
+}
+
+void CInputSystem::PollNovintDevices()
+{
+ if( NovintPollDevices )
+ NovintPollDevices();
+
+ for ( int i = 0; i < m_nNovintDeviceCount; i++ )
+ {
+ UpdateNovintDeviceButtonState(i);
+ }
+}
+
+void CInputSystem::UpdateNovintDeviceButtonState(int nDevice)
+{
+ int nDown;
+ int nPressed;
+ int nReleased;
+ if(NovintButtonState(nDevice, nDown, nPressed, nReleased))
+ {
+ for ( int i = 0; i < 4; i++ )
+ {
+ ButtonCode_t code = (ButtonCode_t)( NOVINT_FIRST + ( nDevice * 4 ) + i );
+
+ if( nPressed & (1 << i) )
+ {
+ PostButtonPressedEvent(IE_ButtonPressed, m_nLastSampleTick, code, KEY_NONE);
+ }
+
+ if( nReleased & (1 << i) )
+ {
+ PostButtonReleasedEvent(IE_ButtonReleased, m_nLastSampleTick, code, KEY_NONE);
+ }
+ }
+ }
+}
+
+void CInputSystem::SetNovintPure( bool bPure )
+{
+ bNovintPure = bPure;
+ if(NovintInputActive)
+ NovintInputActive(bNovintPure);
+
+} \ No newline at end of file
diff --git a/inputsystem/posix_stubs.h b/inputsystem/posix_stubs.h
new file mode 100644
index 0000000..776c230
--- /dev/null
+++ b/inputsystem/posix_stubs.h
@@ -0,0 +1,75 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//
+//
+//==================================================================================================
+
+#ifndef POSIX_WIN32STUBS_H
+#define POSIX_WIN32STUBS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tier0/basetypes.h"
+#include "tier0/platform.h"
+
+typedef int32 LRESULT;
+typedef void* HWND;
+typedef uint32 UINT;
+typedef uintp WPARAM;
+typedef uintp LPARAM;
+
+typedef uint8 BYTE;
+typedef int16 SHORT;
+
+typedef void* WNDPROC;
+typedef void* HANDLE;
+
+typedef char xKey_t;
+
+#define XUSER_MAX_COUNT 2
+#define XK_MAX_KEYS 5
+
+typedef struct joyinfoex_tag
+{
+ DWORD dwSize;
+ DWORD dwFlags;
+ DWORD dwXpos;
+ DWORD dwYpos;
+ DWORD dwZpos;
+ DWORD dwRpos;
+ DWORD dwUpos;
+ DWORD dwVpos;
+ DWORD dwButtons;
+ DWORD dwButtonNumber;
+ DWORD dwPOV;
+ DWORD dwReserved1;
+ DWORD dwReserved2;
+} JOYINFOEX, *LPJOYINFOEX;
+
+
+typedef struct _XINPUT_GAMEPAD
+{
+ WORD wButtons;
+ BYTE bLeftTrigger;
+ BYTE bRightTrigger;
+ SHORT sThumbLX;
+ SHORT sThumbLY;
+ SHORT sThumbRX;
+ SHORT sThumbRY;
+} XINPUT_GAMEPAD, *PXINPUT_GAMEPAD;
+
+typedef struct _XINPUT_STATE
+{
+ DWORD dwPacketNumber;
+ XINPUT_GAMEPAD Gamepad;
+} XINPUT_STATE, *PXINPUT_STATE;
+
+typedef struct _XINPUT_VIBRATION
+{
+ WORD wLeftMotorSpeed;
+ WORD wRightMotorSpeed;
+} XINPUT_VIBRATION, *PXINPUT_VIBRATION;
+
+
+#endif // POSIX_WIN32STUBS_H
diff --git a/inputsystem/steamcontroller.cpp b/inputsystem/steamcontroller.cpp
new file mode 100644
index 0000000..007ccec
--- /dev/null
+++ b/inputsystem/steamcontroller.cpp
@@ -0,0 +1,695 @@
+//=========== Copyright Valve Corporation, All rights reserved. ===============//
+//
+// Purpose: Native Steam Controller Interface
+//=============================================================================//
+#include "inputsystem.h"
+#include "key_translation.h"
+#include "filesystem.h"
+#include "steam/isteamcontroller.h"
+#include "math.h"
+
+#ifndef UINT64_MAX
+const uint64 UINT64_MAX = 0xffffffffffffffff;
+#endif
+
+#if !defined( NO_STEAM )
+#include "steam/steam_api.h"
+#endif //NO_STEAM
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+ConVar sc_joystick_map( "sc_joystick_map", "1", FCVAR_ARCHIVE, "How to map the analog joystick deadzone and extents 0 = Scaled Cross, 1 = Concentric Mapping to Square." );
+
+#define STEAMPAD_MAX_ANALOGSAMPLE_GYRO 32768
+#define STEAMPAD_MAX_ANALOGSAMPLE_TRIGGER 32768
+#define STEAMPAD_MAX_ANALOGSAMPLE_LEFT 32768
+#define STEAMPAD_MAX_ANALOGSAMPLE_RIGHT 32767
+#define STEAMPAD_MAX_ANALOGSAMPLE_DOWN 32768
+#define STEAMPAD_MAX_ANALOGSAMPLE_UP 32767
+#define STEAMPAD_MAX_ANALOGSAMPLE_MAX 32768
+#define STEAMPAD_ANALOG_SCALE_LEFT(x) ( ( float )STEAMPAD_MAX_ANALOGSAMPLE_LEFT/( float )( STEAMPAD_MAX_ANALOGSAMPLE_LEFT-(x) ) )
+#define STEAMPAD_ANALOG_SCALE_RIGHT(x) ( ( float )STEAMPAD_MAX_ANALOGSAMPLE_RIGHT/( float )( STEAMPAD_MAX_ANALOGSAMPLE_RIGHT-(x) ) )
+#define STEAMPAD_ANALOG_SCALE_DOWN(x) ( ( float )STEAMPAD_MAX_ANALOGSAMPLE_DOWN/( float )( STEAMPAD_MAX_ANALOGSAMPLE_DOWN-(x) ) )
+#define STEAMPAD_ANALOG_SCALE_UP(x) ( ( float )STEAMPAD_MAX_ANALOGSAMPLE_UP/( float )( STEAMPAD_MAX_ANALOGSAMPLE_UP-(x) ) )
+
+
+#define STEAMPAD_ANALOG_TRIGGER_THRESHOLD ( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_TRIGGER * 0.5f ) )
+#define STEAMPAD_ANALOG_PAD_THRESHOLD ( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.25f ) )
+#define STEAMPAD_ANALOG_GYRO_THRESHOLD ( ( int ) ( STEAMPAD_MAX_ANALOGSAMPLE_GYRO * 0.3f ) )
+#define STEAMPAD_DIGITAL_PAD_THRESHOLD ( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.52f ) )
+
+#define STEAMPAD_AXIS_REPEAT_INTERVAL_START 0.6f
+#define STEAMPAD_AXIS_REPEAT_INTERVAL_END 0.05f
+#define STEAMPAD_AXIS_REPEAT_CURVE_TIME 1.75f
+
+#define PAD_ANALOG_BUTTON_THRESHOLD ( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.285f ) )
+#define PAD_ANALOG_BUTTON_THRESHOLD_STRONG ( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.68f ) )
+
+#define SQRT2 1.414213562
+
+// key translation
+typedef struct
+{
+ uint64 steampadinput;
+ ButtonCode_t steampadkey;
+} steampadInputTosteampadKey_t;
+
+static const int s_nSteamPadDeadZoneTable[] =
+{
+ STEAMPAD_ANALOG_PAD_THRESHOLD, // LEFTPAD_AXIS_X
+ STEAMPAD_ANALOG_PAD_THRESHOLD, // LEFTPAD_AXIS_Y
+ STEAMPAD_ANALOG_PAD_THRESHOLD, // RIGHTPAD_AXIS_X
+ STEAMPAD_ANALOG_PAD_THRESHOLD, // RIGHTPAD_AXIS_Y
+ STEAMPAD_ANALOG_TRIGGER_THRESHOLD, //LEFT_TRIGGER_AXIS
+ STEAMPAD_ANALOG_TRIGGER_THRESHOLD, //RIGHT_TRIGGER_AXIS
+ STEAMPAD_ANALOG_GYRO_THRESHOLD, //GYRO_AXIS_PITCH
+ STEAMPAD_ANALOG_GYRO_THRESHOLD, //GYRO_AXIS_ROLL
+ STEAMPAD_ANALOG_GYRO_THRESHOLD, //GYRO_AXIS_YAW
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Counts the number of active gamepads connected
+//-----------------------------------------------------------------------------
+uint32 CInputSystem::GetNumSteamControllersConnected()
+{
+ return m_unNumConnected;
+}
+
+struct SDigitalMenuAction
+{
+ const char *strName;
+ ButtonCode_t buttonCode;
+ ControllerDigitalActionHandle_t handle;
+ bool bState[STEAM_CONTROLLER_MAX_COUNT];
+ bool bAwaitingDebounce[STEAM_CONTROLLER_MAX_COUNT];
+};
+
+static SDigitalMenuAction g_DigitalMenuActions[] = {
+ { "menu_left", STEAMCONTROLLER_DPAD_LEFT, 0, { false }, { false } },
+ { "menu_right", STEAMCONTROLLER_DPAD_RIGHT, 0, { false }, { false } },
+ { "menu_up", STEAMCONTROLLER_DPAD_UP, 0, { false }, { false } },
+ { "menu_down", STEAMCONTROLLER_DPAD_DOWN, 0, { false }, { false } },
+ { "menu_cancel", STEAMCONTROLLER_B, 0, { false }, { false } },
+ { "menu_select", STEAMCONTROLLER_A, 0, { false }, { false } },
+ { "resume_esc", STEAMCONTROLLER_START, 0, { false }, { false } },
+ { "cl_trigger_first_notification", STEAMCONTROLLER_F1, 0, { false }, { false } }, // Command is in the in-game action set (use for hitting accept/ok on dialogs)
+ { "cl_decline_first_notification", STEAMCONTROLLER_F2, 0, { false }, { false } }, // Command is in the in-game action set (use for hitting cancel/decline on dialogs)
+ { "menu_toggle_function", STEAMCONTROLLER_Y, 0, { false }, { false }, }, // Command is in the in-game HUD action set
+ { "menu_alt_function", STEAMCONTROLLER_X, 0, { false }, { false } }, // Command is in the in-game HUD action set
+ { "cancelselect", STEAMCONTROLLER_START, 0, { false }, { false } }, // Command is in the in-game action set
+ { "toggleready", STEAMCONTROLLER_F4, 0, { false }, { false } }, // Command is in the in-game action set
+};
+
+struct SAnalogAction
+{
+ const char *strName;
+ JoystickAxis_t joystickAxisX, joystickAxisY;
+ ControllerAnalogActionHandle_t handle;
+};
+
+struct SGameActionSet
+{
+ const char *strName;
+ GameActionSet_t eGameActionSet;
+ ControllerActionSetHandle_t handle;
+};
+static SGameActionSet g_GameActionSets[] = {
+ { "MenuControls", GAME_ACTION_SET_MENUCONTROLS, 0 },
+ { "FPSControls", GAME_ACTION_SET_FPSCONTROLS, 0 },
+ { "InGameHUDControls", GAME_ACTION_SET_IN_GAME_HUD, 0 },
+ { "SpectatorControls", GAME_ACTION_SET_SPECTATOR, 0 },
+};
+
+
+// Table that maps a physical steam controller origin to the corresponding character in our icon font.
+// If the EControllerActionOrigin enum or the font changes, this table must be changed to match.
+static const wchar_t* g_MapSteamControllerOriginToIconFont[] = {
+ L"", // k_EControllerActionOrigin_None
+ L"A", // k_EControllerActionOrigin_A
+ L"B", // k_EControllerActionOrigin_B
+ L"X", // k_EControllerActionOrigin_X
+ L"Y", // k_EControllerActionOrigin_Y
+ L"2", // k_EControllerActionOrigin_LeftBumper
+ L"3", // k_EControllerActionOrigin_RightBumper
+ L"(", // k_EControllerActionOrigin_LeftGrip
+ L")", // k_EControllerActionOrigin_RightGrip
+ L"5", // k_EControllerActionOrigin_Start
+ L"4", // k_EControllerActionOrigin_Back
+ L"q", // k_EControllerActionOrigin_LeftPad_Touch
+ L"w", // k_EControllerActionOrigin_LeftPad_Swipe
+ L"e", // k_EControllerActionOrigin_LeftPad_Click
+ L"a", // k_EControllerActionOrigin_LeftPad_DPadNorth
+ L"s", // k_EControllerActionOrigin_LeftPad_DPadSouth
+ L"d", // k_EControllerActionOrigin_LeftPad_DPadWest
+ L"f", // k_EControllerActionOrigin_LeftPad_DPadEast
+ L"y", // k_EControllerActionOrigin_RightPad_Touch
+ L"u", // k_EControllerActionOrigin_RightPad_Swipe
+ L"i", // k_EControllerActionOrigin_RightPad_Click
+ L"h", // k_EControllerActionOrigin_RightPad_DPadNorth
+ L"j", // k_EControllerActionOrigin_RightPad_DPadSouth
+ L"k", // k_EControllerActionOrigin_RightPad_DPadWest
+ L"l", // k_EControllerActionOrigin_RightPad_DEast
+ L"z", // k_EControllerActionOrigin_LeftTrigger_Pull
+ L"x", // k_EControllerActionOrigin_LeftTrigger_Click
+ L"n", // k_EControllerActionOrigin_RightTrigger_Pull
+ L"m", // k_EControllerActionOrigin_RightTrigger_Click
+ L"C", // k_EControllerActionOrigin_LeftStick_Move
+ L"V", // k_EControllerActionOrigin_LeftStick_Click
+ L"7", // k_EControllerActionOrigin_LeftStick_DPadNorth
+ L"8", // k_EControllerActionOrigin_LeftStick_DPadSouth
+ L"9", // k_EControllerActionOrigin_LeftStick_DPadWest
+ L"0", // k_EControllerActionOrigin_LeftStick_DPadEast
+ L"6", // k_EControllerActionOrigin_Gyro_Move
+ L"6", // k_EControllerActionOrigin_Gyro_Pitch
+ L"6", // k_EControllerActionOrigin_Gyro_Yaw
+ L"6", // k_EControllerActionOrigin_Gyro_Roll
+};
+
+// Table that maps a physical steam controller origin to a short description string for user display (only use this
+// if it's impractical to use the icon font).
+// If the EControllerActionOrigin enum changes, this table must be changed to match.
+static const wchar_t* g_MapSteamControllerOriginToDescription[] = {
+ L"", // k_EControllerActionOrigin_None
+ L"A", // k_EControllerActionOrigin_A
+ L"B", // k_EControllerActionOrigin_B
+ L"X", // k_EControllerActionOrigin_X
+ L"Y", // k_EControllerActionOrigin_Y
+ L"LB", // k_EControllerActionOrigin_LeftBumper
+ L"RB", // k_EControllerActionOrigin_RightBumper
+ L"LG", // k_EControllerActionOrigin_LeftGrip
+ L"RG", // k_EControllerActionOrigin_RightGrip
+ L"START", // k_EControllerActionOrigin_Start
+ L"BACK", // k_EControllerActionOrigin_Back
+ L"LPTOUCH", // k_EControllerActionOrigin_LeftPad_Touch
+ L"LPSWIPE", // k_EControllerActionOrigin_LeftPad_Swipe
+ L"LPCLICK", // k_EControllerActionOrigin_LeftPad_Click
+ L"LPUP", // k_EControllerActionOrigin_LeftPad_DPadNorth
+ L"LPDOWN", // k_EControllerActionOrigin_LeftPad_DPadSouth
+ L"LPLEFT", // k_EControllerActionOrigin_LeftPad_DPadWest
+ L"LPRIGHT", // k_EControllerActionOrigin_LeftPad_DPadEast
+ L"RPTOUCH", // k_EControllerActionOrigin_RightPad_Touch
+ L"RPSWIPE", // k_EControllerActionOrigin_RightPad_Swipe
+ L"RPCLICK", // k_EControllerActionOrigin_RightPad_Click
+ L"RPUP", // k_EControllerActionOrigin_RightPad_DPadNorth
+ L"RPDOWN", // k_EControllerActionOrigin_RightPad_DPadSouth
+ L"RPLEFT", // k_EControllerActionOrigin_RightPad_DPadWest
+ L"RPRIGHT", // k_EControllerActionOrigin_RightPad_DEast
+ L"LT", // k_EControllerActionOrigin_LeftTrigger_Pull
+ L"LT", // k_EControllerActionOrigin_LeftTrigger_Click
+ L"LT", // k_EControllerActionOrigin_RightTrigger_Pull
+ L"LT", // k_EControllerActionOrigin_RightTrigger_Click
+ L"LS", // k_EControllerActionOrigin_LeftStick_Move
+ L"LSCLICK", // k_EControllerActionOrigin_LeftStick_Click
+ L"LSUP", // k_EControllerActionOrigin_LeftStick_DPadNorth
+ L"LSDOWN", // k_EControllerActionOrigin_LeftStick_DPadSouth
+ L"LSLEFT", // k_EControllerActionOrigin_LeftStick_DPadWest
+ L"LSRIGHT", // k_EControllerActionOrigin_LeftStick_DPadEast
+ L"GYRO", // k_EControllerActionOrigin_Gyro_Move
+ L"GYRO", // k_EControllerActionOrigin_Gyro_Pitch
+ L"GYRO", // k_EControllerActionOrigin_Gyro_Yaw
+ L"GYRO", // k_EControllerActionOrigin_Gyro_Roll
+};
+
+
+bool CInputSystem::InitializeSteamControllerActionSets()
+{
+ auto psteamcontroller = g_pInputSystem->SteamControllerInterface();
+ if ( !psteamcontroller )
+ {
+ return false;
+ }
+
+ bool bGotHandles = true;
+
+ for ( int i = 0; i != ARRAYSIZE( g_DigitalMenuActions ); ++i )
+ {
+ g_DigitalMenuActions[i].handle = psteamcontroller->GetDigitalActionHandle( g_DigitalMenuActions[i].strName );
+ bGotHandles = bGotHandles && ( g_DigitalMenuActions[i].handle != 0 );
+
+ // Init the button state array
+ for( int j = 0; j < STEAM_CONTROLLER_MAX_COUNT; j++ )
+ {
+ g_DigitalMenuActions[i].bState[j] = false;
+ }
+ }
+
+ for ( int i = 0; i != ARRAYSIZE( g_GameActionSets ); ++i )
+ {
+ g_GameActionSets[i].handle = psteamcontroller->GetActionSetHandle( g_GameActionSets[i].strName );
+ bGotHandles = bGotHandles && g_GameActionSets[i].handle;
+ }
+
+ return bGotHandles;
+}
+
+ConVar sc_debug_sets( "sc_debug_sets", "0", FCVAR_ARCHIVE, "Debugging" );
+
+void CInputSystem::PollSteamControllers( void )
+{
+ // Make sure we've got the appropriate connections to Steam
+ auto steamcontroller = SteamControllerInterface();
+ if ( !steamcontroller )
+ {
+ return;
+ }
+
+ steamcontroller->RunFrame();
+
+ uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
+ m_unNumConnected = steamcontroller->GetConnectedControllers( nControllerHandles );
+
+ if ( m_unNumConnected > 0 )
+ {
+ if ( !m_bSteamControllerActionsInitialized )
+ {
+ // Retry initialization of controller actions if we didn't acquire them all before for some reason.
+ m_bSteamControllerActionsInitialized = m_bSteamController && InitializeSteamControllerActionSets();
+ g_pInputSystem->ActivateSteamControllerActionSet( GAME_ACTION_SET_MENUCONTROLS );
+ }
+
+ if ( m_bSteamControllerActionsInitialized )
+ {
+ // If we successfully initialized all the actions, and we have a connected controller, then we're considered "active".
+ m_bSteamControllerActive = true;
+
+ // For each digital action
+ for ( int i = 0; i != ARRAYSIZE( g_DigitalMenuActions ); ++i )
+ {
+ SDigitalMenuAction& action = g_DigitalMenuActions[i];
+
+ // and for each controller
+ for ( uint64 j = 0; j < m_unNumConnected; ++j )
+ {
+
+ // Get the action's current state
+ ControllerDigitalActionData_t data = steamcontroller->GetDigitalActionData( nControllerHandles[j], action.handle );
+
+ // We only care if the action is active
+ if ( data.bActive )
+ {
+ action.bAwaitingDebounce[j] = action.bAwaitingDebounce[j] && data.bState;
+
+ // If the action's state has changed
+ if ( data.bState != action.bState[j] )
+ {
+ action.bState[j] = data.bState;
+
+ // Press the key for the correct controller
+ ButtonCode_t buttonCode = ButtonCodeToJoystickButtonCode( action.buttonCode, j );
+
+ if ( action.bState[j] )
+ {
+ if ( !action.bAwaitingDebounce[j] )
+ {
+ PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
+ }
+ }
+ else
+ {
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // If no controllers are connected, unflag the active state.
+ m_bSteamControllerActive = false;
+ }
+}
+
+bool CInputSystem::GetRadialMenuStickValues( int nSlot, float &fX, float &fY )
+{
+ fX = m_pRadialMenuStickVal[nSlot][0];
+ fY = m_pRadialMenuStickVal[nSlot][1];
+
+ return true;
+}
+
+bool CInputSystem::IsSteamControllerActive( void )
+{
+ return m_bSteamControllerActive;
+}
+
+bool CInputSystem::InitializeSteamControllers()
+{
+ m_flLastSteamControllerInput = -FLT_MAX;
+ auto steamcontroller = SteamControllerInterface();
+ if ( steamcontroller )
+ {
+ if ( !steamcontroller->Init() )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ for( int i=0; i<STEAM_CONTROLLER_MAX_COUNT; i++ )
+ {
+ m_pRadialMenuStickVal[i][0] = 0.0f;
+ m_pRadialMenuStickVal[i][1] = 0.0f;
+ }
+
+ // We have to account for other joysticks prior to adding steam controllers
+ // So we get the baseline number here when first initializing
+ m_nJoystickBaseline = m_nJoystickCount;
+ if ( steamcontroller )
+ {
+ uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
+ m_unNumConnected = steamcontroller->GetConnectedControllers( nControllerHandles );
+ steamcontroller->RunFrame();
+
+ if ( m_unNumConnected > 0 )
+ {
+ for ( uint32 i = 0; i < m_unNumConnected; i++ )
+ {
+ if ( m_Device[i].m_nJoystickIndex == INVALID_USER_ID )
+ {
+ int nJoystickIndex = i;
+ m_Device[i].m_nJoystickIndex = nJoystickIndex;
+ m_Device[i].m_nHardwareIndex = i;
+ }
+ m_nControllerType[m_Device[i].m_nJoystickIndex] = INPUT_TYPE_STEAMCONTROLLER;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+ControllerActionSetHandle_t CInputSystem::GetActionSetHandle( GameActionSet_t eActionSet )
+{
+ return g_GameActionSets[eActionSet].handle;
+}
+
+ControllerActionSetHandle_t CInputSystem::GetActionSetHandle( const char* szActionSet )
+{
+ for ( int i = 0; i != ARRAYSIZE( g_GameActionSets ); ++i )
+ {
+ if ( !Q_strcmp( szActionSet, g_GameActionSets[i].strName ) )
+ {
+ return g_GameActionSets[i].handle;
+ }
+ }
+
+ return 0;
+}
+
+
+void CInputSystem::ActivateSteamControllerActionSetForSlot( uint64 nSlot, GameActionSet_t eActionSet )
+{
+ auto steamcontroller = SteamControllerInterface();
+ bool bChangedActionSet = false;
+
+ if ( steamcontroller )
+ {
+ if ( nSlot == STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS )
+ {
+ for ( int i = 0; i < STEAM_CONTROLLER_MAX_COUNT; i++ )
+ {
+ if ( m_currentActionSet[i] != eActionSet )
+ {
+ bChangedActionSet = true;
+ m_currentActionSet[i] = eActionSet;
+ }
+ }
+
+ steamcontroller->ActivateActionSet( STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS, g_GameActionSets[eActionSet].handle );
+ }
+ else
+ {
+ uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
+ int unNumConnected = steamcontroller->GetConnectedControllers( nControllerHandles );
+
+ if( nSlot < unNumConnected )
+ {
+ if ( m_currentActionSet[nSlot] != eActionSet )
+ {
+ bChangedActionSet = true;
+ m_currentActionSet[nSlot] = eActionSet;
+ }
+
+ steamcontroller->ActivateActionSet( nControllerHandles[nSlot], g_GameActionSets[eActionSet].handle );
+ }
+ }
+ }
+
+ if ( bChangedActionSet )
+ {
+ // If we changed action set, then flag everything for a debounce (meaning we demand to see an unpressed state before we'll register a pressed one)
+ for ( int i = 0; i < STEAM_CONTROLLER_MAX_COUNT; i++ )
+ {
+ for ( int j = 0; j != ARRAYSIZE( g_DigitalMenuActions ); ++j )
+ {
+ g_DigitalMenuActions[j].bAwaitingDebounce[i] = true;
+ }
+ }
+ }
+}
+
+const int CInputSystem::GetSteamPadDeadZone( ESteamPadAxis axis )
+{
+ int nDeadzone = s_nSteamPadDeadZoneTable[ axis ];
+
+ // Do modifications if required here?
+
+ return nDeadzone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Processes data for controller
+//-----------------------------------------------------------------------------
+void CInputSystem::ReadSteamController( int iIndex )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Pulse haptic feedback
+//-----------------------------------------------------------------------------
+/* void CInputSystem::PulseHapticOnSteamController( uint32 nControllerIndex, ESteamControllerPad ePad, unsigned short durationMicroSec )
+{
+ auto steamcontroller = SteamControllerInterface();
+ if ( steamcontroller )
+ {
+ steamcontroller->TriggerHapticPulse( nControllerIndex, ePad, durationMicroSec );
+ }
+}*/
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the controller State for a particular joystick slot
+//-----------------------------------------------------------------------------
+bool CInputSystem::GetControllerStateForSlot( int nSlot )
+{
+ return false;
+}
+
+int CInputSystem::GetSteamControllerIndexForSlot( int nSlot )
+{
+ for ( int i = 0; i < Q_ARRAYSIZE( m_Device ); i++ )
+ {
+ if ( m_Device[i].active && (int)m_Device[i].m_nJoystickIndex == nSlot )
+ {
+ return m_Device[i].m_nHardwareIndex;
+ }
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Post events, ignoring key repeats
+//-----------------------------------------------------------------------------
+void CInputSystem::PostKeyEvent( int iIndex, sKey_t sKey, int nSample )
+{
+ // Rework of xbox code here :
+
+ AnalogCode_t code = ANALOG_CODE_LAST;
+ float value = 0.f;
+ //int nMsgSlot = iIndex;
+ int nMsgSlot = m_Device[iIndex].m_nJoystickIndex;
+ int nSampleThreshold = 1;
+
+ // Look for changes on the analog axes
+ switch( sKey )
+ {
+ case SK_BUTTON_LPAD_LEFT:
+ case SK_BUTTON_LPAD_RIGHT:
+ {
+ code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_X );
+ value = ( sKey == SK_BUTTON_LPAD_LEFT ) ? -nSample : nSample;
+ // Kind of a hack to horizontal values for menu selection items in Portal 2
+ // The additional 5k helps accidental horizontals in menus.
+ nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD ) + 5000;
+ }
+ break;
+
+ case SK_BUTTON_LPAD_UP:
+ case SK_BUTTON_LPAD_DOWN:
+ {
+ code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_Y );
+ value = ( sKey == SK_BUTTON_LPAD_UP ) ? -nSample : nSample;
+ nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD );
+ }
+ break;
+
+ case SK_BUTTON_RPAD_LEFT:
+ case SK_BUTTON_RPAD_RIGHT:
+ {
+ code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_U );
+ value = ( sKey == SK_BUTTON_RPAD_LEFT ) ? -nSample : nSample;
+ // Kind of a hack to horizontal values for menu selection items in Portal 2
+ // The additional 5k helps accidental horizontals in menus.
+ nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD ) + 5000;;
+ }
+ break;
+
+ case SK_BUTTON_RPAD_UP:
+ case SK_BUTTON_RPAD_DOWN:
+ {
+ code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_R );
+ value = ( sKey == SK_BUTTON_RPAD_UP ) ? -nSample : nSample;
+ nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD );
+ }
+ break;
+ }
+
+ // Store the analog event
+ if ( ANALOG_CODE_LAST != code )
+ {
+ InputState_t &state = m_InputState[ m_bIsPolling ];
+ state.m_pAnalogDelta[ code ] = ( int )( value - state.m_pAnalogValue[ code ] );
+ state.m_pAnalogValue[ code ] = ( int )value;
+ if ( state.m_pAnalogDelta[ code ] != 0 )
+ {
+ PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, ( int )value, state.m_pAnalogDelta[ code ] );
+ }
+ }
+
+ // store the key
+ m_Device[iIndex].m_appSKeys[sKey].sample = nSample;
+ if ( nSample > nSampleThreshold )
+ {
+ m_Device[iIndex].m_appSKeys[sKey].repeats++;
+ }
+ else
+ {
+ m_Device[iIndex].m_appSKeys[sKey].repeats = 0;
+ nSample = 0;
+ }
+
+ if ( m_Device[iIndex].m_appSKeys[sKey].repeats > 1 )
+ {
+ // application cannot handle streaming keys
+ // first keypress is the only edge trigger
+ return;
+ }
+
+ // package the key
+ ButtonCode_t buttonCode = SKeyToButtonCode( nMsgSlot, sKey );
+ if ( nSample )
+ {
+ PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
+ }
+ else
+ {
+ PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
+ }
+}
+
+// Gets the action origin (i.e. which physical input) maps to the given virtual button for the given action set
+EControllerActionOrigin CInputSystem::GetSteamControllerActionOrigin( const char* action, GameActionSet_t action_set )
+{
+ auto pSC = SteamControllerInterface();
+ if ( pSC )
+ {
+ ControllerHandle_t hConnected[STEAM_CONTROLLER_MAX_COUNT];
+ auto nConnected = pSC->GetConnectedControllers( hConnected );
+ if ( nConnected == 0 )
+ {
+ return k_EControllerActionOrigin_None;
+ }
+
+ SGameActionSet* pActionSet = nullptr;
+ for ( int i = 0; i < ARRAYSIZE( g_GameActionSets ); i++ )
+ {
+ if ( g_GameActionSets[i].eGameActionSet == action_set )
+ {
+ pActionSet = &g_GameActionSets[i];
+ break;
+ }
+ }
+
+ if ( pActionSet )
+ {
+ auto actionHandle = pSC->GetDigitalActionHandle( action );
+ EControllerActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
+ int nOrigins = pSC->GetDigitalActionOrigins( hConnected[0], pActionSet->handle, actionHandle, origins );
+ if ( nOrigins > 0 )
+ {
+ return origins[0];
+ }
+ }
+ }
+
+ return k_EControllerActionOrigin_None;
+}
+
+// Gets the action origin (i.e. which physical input) maps to the given virtual button for the given action set
+EControllerActionOrigin CInputSystem::GetSteamControllerActionOrigin( const char* action, ControllerActionSetHandle_t action_set_handle )
+{
+ auto pSC = SteamControllerInterface();
+ if ( pSC && action_set_handle )
+ {
+ ControllerHandle_t hConnected[STEAM_CONTROLLER_MAX_COUNT];
+ auto nConnected = pSC->GetConnectedControllers( hConnected );
+ if ( nConnected == 0 )
+ {
+ return k_EControllerActionOrigin_None;
+ }
+
+ auto actionHandle = pSC->GetDigitalActionHandle( action );
+ EControllerActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
+ int nOrigins = pSC->GetDigitalActionOrigins( hConnected[0], action_set_handle, actionHandle, origins );
+ if ( nOrigins > 0 )
+ {
+ return origins[0];
+ }
+ }
+
+ return k_EControllerActionOrigin_None;
+}
+
+// Maps a Steam Controller action origin to a string (consisting of a single character) in our SC icon font
+const wchar_t* CInputSystem::GetSteamControllerFontCharacterForActionOrigin( EControllerActionOrigin origin )
+{
+ if ( origin >= 0 && origin < ARRAYSIZE( g_MapSteamControllerOriginToIconFont ) )
+ {
+ return g_MapSteamControllerOriginToIconFont[origin];
+ }
+ else
+ {
+ return L"";
+ }
+}
+
+// Maps a Steam Controller action origin to a short text string (e.g. "X", "LB", "LDOWN") describing the control.
+// Prefer to actually use the icon font wherever possible.
+const wchar_t* CInputSystem::GetSteamControllerDescriptionForActionOrigin( EControllerActionOrigin origin )
+{
+ if ( origin >= 0 && origin < ARRAYSIZE( g_MapSteamControllerOriginToDescription ) )
+ {
+ return g_MapSteamControllerOriginToDescription[origin];
+ }
+ else
+ {
+ return L"";
+ }
+}