// InputService.cpp : Implementation of cInputService #include "stdafx.h" #include "DecalInput.h" #include "InputService.h" #include "Timer.h" #include "Hotkey.h" #include "WinMsgHook.h" #include "InputBuffer.h" using cInputService::cCharNames; static cCharNames _charnames[] = { { _T( "BACKSPACE" ), VK_BACK }, { _T( "BS" ), VK_BACK }, { _T( "BKSP" ), VK_BACK }, { _T( "CAPSLOCK" ), VK_CAPITAL }, { _T( "DELETE" ), VK_DELETE }, { _T( "DEL" ), VK_DELETE }, { _T( "DOWN" ), VK_DOWN }, { _T( "END" ), VK_END }, { _T( "ENTER" ), VK_RETURN }, { _T( "ESC" ), VK_ESCAPE }, { _T( "HELP" ), VK_HELP }, { _T( "HOME" ), VK_HOME }, { _T( "INS" ), VK_INSERT }, { _T( "INSERT" ), VK_INSERT }, { _T( "LEFT" ), VK_LEFT }, { _T( "NUMLOCK" ), VK_NUMLOCK }, { _T( "PGDN" ), VK_NEXT }, { _T( "PGUP" ), VK_PRIOR }, { _T( "PRTSC" ), VK_SNAPSHOT }, { _T( "RIGHT" ), VK_RIGHT }, { _T( "SCROLLLOCK" ), VK_SCROLL }, { _T( "TAB" ), VK_TAB }, { _T( "UP" ), VK_UP }, { _T( "F1" ), VK_F1 }, { _T( "F2" ), VK_F2 }, { _T( "F3" ), VK_F3 }, { _T( "F4" ), VK_F4 }, { _T( "F5" ), VK_F5 }, { _T( "F6" ), VK_F6 }, { _T( "F7" ), VK_F7 }, { _T( "F8" ), VK_F8 }, { _T( "F9" ), VK_F9 }, { _T( "F10" ), VK_F10 }, { _T( "F11" ), VK_F11 }, { _T( "F12" ), VK_F12 }, { _T( "F13" ), VK_F13 }, { _T( "F14" ), VK_F14 }, { _T( "F15" ), VK_F15 }, { _T( "F16" ), VK_F16 }, { _T( "+" ), VK_ADD }, { _T( "SHIFT" ), VK_SHIFT }, { _T( "CTRL" ), VK_CONTROL }, { _T( "ALT" ), VK_MENU }, { _T( "LBUTTON" ), VK_LBUTTON }, { _T( "RBUTTON" ), VK_RBUTTON } }, *_end_charnames = _charnames + ( sizeof(_charnames ) / sizeof( cCharNames ) ); ///////////////////////////////////////////////////////////////////////////// // cInputService LRESULT cInputService::wndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { if( g_p == NULL || g_p->m_hWndHook != hWnd ) { // We have no idea how to continue - this hook has been orphaned _ASSERT( FALSE ); return 0; } if( g_p->m_msghooks.size() > 0 ) { // Fill out the message g_p->m_pMessage->loadMessage( hWnd, nMsg, wParam, lParam ); for( cWinMsgHookList::iterator i = g_p->m_msghooks.begin(); i != g_p->m_msghooks.end(); ++ i ) { ( *i )->Fire_Message( *i, g_p->m_pMessage ); if( g_p->m_pMessage->m_bEaten ) // This message was eaten, yummy return 0; } } // NOTE: The hotkeys are not dispatched while an input buffer is running - tough luck if( nMsg == WM_KEYUP && g_p->m_pActive == NULL ) { int nVK = static_cast< int >( wParam ); // Check if there's a matching hotkey for( cHotkeyList::iterator i = g_p->m_hotkeys.begin(); i != g_p->m_hotkeys.end(); ++ i ) { if( ( *i )->m_nVK == nVK ) { // Found a hotkey - fire the event and eat it ( *i )->Fire_Hotkey( *i ); break; } } } return g_p->m_pfnHook( hWnd, nMsg, wParam, lParam ); } cInputService *cInputService::g_p = NULL; HRESULT cInputService::onInitialize() { // ::DebugBreak(); USES_CONVERSION; if( g_p == NULL ) g_p = this; // Create the message object CComObject< cWndMsg >::CreateInstance( &m_pMessage ); m_pMessage->AddRef(); // Load the list of action types from the configuration data { static _bstr_t _strPrefix( _T( "Prefix" ) ); CComPtr< IDecalEnum > pActions; m_pDecal->get_Configuration( _bstr_t( _T( "InputActions" ) ), GUID_NULL, &pActions ); while( pActions->Next() == S_OK ) { VARIANT_BOOL bEnabled; HRESULT hRes = pActions->get_Enabled ( &bEnabled ); _ASSERTE ( SUCCEEDED ( hRes ) ); if ( !bEnabled ) continue; cActionType at; pActions->get_ComClass( &at.m_clsid ); CComVariant v; if( FAILED( pActions->get_Property( _strPrefix, &v ) ) ) continue; ::_tcscpy( at.szAction, OLE2T( v.bstrVal ) ); ::_tcslwr( at.szAction ); m_actiontypes.push_back( at ); } } // Get the OS version OSVERSIONINFO ovi; ovi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); ::GetVersionEx( &ovi ); if( ovi.dwPlatformId == VER_PLATFORM_WIN32_NT ) { m_eWindows = eWindowsNT; return S_OK; } // First check if there's version info in the registry RegKey key; key.Open( HKEY_LOCAL_MACHINE, _T( "Software\\Decal" ) ); DWORD dwRegMajor, dwRegMinor; if( key.QueryDWORDValue( _T( "ACMajorVersion" ), dwRegMajor ) != ERROR_SUCCESS || key.QueryDWORDValue( _T( "ACMinorVersion" ), dwRegMinor ) != ERROR_SUCCESS ) { m_eWindows = eWindows98NoMemLocs; return S_OK; } TCHAR szFilename[ MAX_PATH ]; DWORD dwVerSize; ::GetModuleFileName( NULL, szFilename, MAX_PATH ); if( ::GetFileVersionInfoSize( szFilename, &dwVerSize ) ) { m_eWindows = eWindows98NoMemLocs; return S_OK; } BYTE *pbVersionInfo = reinterpret_cast< BYTE * >( ::_alloca( dwVerSize ) ); ::GetFileVersionInfo( szFilename, 0, dwVerSize, pbVersionInfo ); VS_FIXEDFILEINFO *vffi; UINT nLength = sizeof( VS_FIXEDFILEINFO ); if( ::VerQueryValue( pbVersionInfo, _T( "\\" ), reinterpret_cast< LPVOID * >( &vffi ), &nLength ) ) { if( dwRegMajor == vffi->dwFileVersionMS && dwRegMinor == vffi->dwFileVersionLS ) { DWORD dwOffset1, dwOffset2; if( key.QueryDWORDValue( _T( "MouseOffset1" ), dwOffset1 ) == ERROR_SUCCESS && key.QueryDWORDValue( _T( "MouseOffset2" ), dwOffset2 ) == ERROR_SUCCESS ) { m_eWindows = eWindows98WithMemLocs; m_pnOffset1 = reinterpret_cast< long * >( dwOffset1 ); m_pnOffset2 = reinterpret_cast< long * >( dwOffset2 ); return S_OK; } } } m_eWindows = eWindows98NoMemLocs; return S_OK; } void cInputService::onTerminate() { // Create the message object m_pMessage->Release(); m_pMessage = NULL; if( g_p == this ) g_p = NULL; } cCharNames *cInputService::begin_chars() { return _charnames; } cCharNames *cInputService::end_chars() { return _end_charnames; } cCharNames *cInputService::charFromVK( int nVK ) { for( cCharNames *i_chars = _charnames; i_chars != _end_charnames; ++ i_chars ) { if( i_chars->m_nVKey == nVK ) break; } return i_chars; } cCharNames *cInputService::charFromName( LPCTSTR szName, int nLength ) { for( cCharNames *i_chars = _charnames; i_chars != _end_charnames; ++ i_chars ) { if( ::strlen( i_chars->szName ) == nLength && std::equal( szName, szName + nLength, i_chars->szName ) ) break; } return i_chars; } STDMETHODIMP cInputService::BeforePlugins() { // Hook the main plugin window long nWnd; m_pDecal->get_HWND( &nWnd ); if( nWnd != 0 ) { m_hWndHook = reinterpret_cast< HWND >( nWnd ); m_pfnHook = reinterpret_cast< WNDPROC >( ::SetWindowLong( m_hWndHook, GWL_WNDPROC, reinterpret_cast< LONG >( wndProc ) ) ); } // Load the keymap RegKey key; if(key.Open( HKEY_LOCAL_MACHINE, _T( "SOFTWARE\\Microsoft\\Microsoft Games\\Asheron's Call\\1.00" ), KEY_READ ) != ERROR_SUCCESS) return E_FAIL; TCHAR szFilename[ MAX_PATH ]; TCHAR *pszInsert = szFilename; DWORD dwSize = MAX_PATH; key.QueryStringValue( _T("Path"), szFilename, &dwSize ); pszInsert = &szFilename[dwSize-1]; //pszInsert += dwSize; ::_tcscat( pszInsert, _T( "\\" ) ); ++ pszInsert; dwSize = MAX_PATH - ( dwSize + 1 ); key.QueryStringValue( _T( "CurrentInputMap" ), pszInsert, &dwSize ); // Looking good, open up the file and load the keys std::ifstream is( szFilename ); if( is.bad() ) // Could not load the file return E_FAIL; int nCount = 0; is >> nCount; for( ; nCount > 0; -- nCount ) { int nInputType, nVK, nChording, nCommandType, nAnalogType; TCHAR szCommandName[ 64 ]; is >> nInputType >> nVK >> nChording >> szCommandName >> nCommandType >> nAnalogType; m_commands.insert( cCommandMap::value_type( szCommandName, static_cast< WORD >( nVK ) ) ); } return S_OK; } STDMETHODIMP cInputService::AfterPlugins() { m_commands.clear(); if( m_hWndHook != NULL ) { _ASSERTE( reinterpret_cast< LONG >( wndProc ) == ::GetWindowLong( m_hWndHook, GWL_WNDPROC ) ); ::SetWindowLong( m_hWndHook, GWL_WNDPROC, reinterpret_cast< LONG >( m_pfnHook ) ); } return S_OK; } STDMETHODIMP cInputService::get_Command(BSTR strCommandName, BSTR *pVal) { USES_CONVERSION; // First try to find the command in the list _bstr_t strCommand( strCommandName ); cCommandMap::iterator i = m_commands.find( strCommand ); if( i == m_commands.end() ) { *pVal = T2BSTR( _T( "" ) ); return S_FALSE; } WORD nCommand = i->second; // Scan to see if this command has a special name cCharNames *i_chars = charFromVK( nCommand ); if( i_chars != end_chars() ) { TCHAR szFormat[ 12 ]; ::_stprintf( szFormat, _T( "{%s}" ), i_chars->szName ); *pVal = T2BSTR( szFormat ); return S_OK; } // Ok, we'll assume the char is printable TCHAR szString[ 2 ] = { nCommand, _T( '\0' ) }; *pVal = T2BSTR( szString ); return S_OK; } STDMETHODIMP cInputService::Render2D() { long nTime = static_cast< long >( ::timeGetTime() ); cTimerList::iterator i = m_timers.begin(); while( i != m_timers.end() ) { cTimerList::iterator j = i++; // Make a copy of the iterator, increment it, then call the timeout. if( ( ( *j )->m_nStart + ( *j )->m_nInterval ) <= nTime ) { ( *j )->m_nStart = nTime; ( *j )->Fire_Timeout( *j ); } } // Check to see if there's a delay paused if( m_pActive != NULL && m_pActive->m_bWaiting && m_pActive->m_nUntilTime <= nTime ) // We've passed the timeout, continue processing actions m_pActive->runActions(); return S_OK; } STDMETHODIMP cInputService::ChangeHWND() { return S_OK; } STDMETHODIMP cInputService::get_Decal(IDecal **pVal) { return m_pDecal->QueryInterface( pVal ); } STDMETHODIMP cInputService::get_KeyByName(BSTR strName, long *pVal) { USES_CONVERSION; LPTSTR szName = OLE2T( strName ); cCharNames *i_c = charFromName( szName, ::_tcslen( szName ) ); if( i_c == end_chars() ) return E_INVALIDARG; *pVal = i_c->m_nVKey; return S_OK; } STDMETHODIMP cInputService::get_CommandKey(BSTR strCommandName, long *pVal) { _bstr_t strCommand( strCommandName ); cCommandMap::iterator i = m_commands.find( strCommand ); if( i == m_commands.end() ) return E_FAIL; *pVal = i->second; return S_OK; }