mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-23 06:36:54 +00:00
618 lines
13 KiB
C++
618 lines
13 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
// vmpi_service_ui.cpp : Defines the entry point for the application.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "consolewnd.h"
|
|
#include "resource.h"
|
|
#include "tier0/dbg.h"
|
|
#include "tier1/strtools.h"
|
|
#include "shell_icon_mgr.h"
|
|
#include "vmpi.h"
|
|
#include "service_conn_mgr.h"
|
|
#include <io.h>
|
|
#include <time.h>
|
|
|
|
|
|
|
|
void UpdatePopupMenuState();
|
|
|
|
|
|
|
|
const char *g_pIconTooltip = VMPI_SERVICE_NAME;
|
|
|
|
IConsoleWnd *g_pConsoleWnd = NULL;
|
|
HINSTANCE g_hInstance = NULL;
|
|
|
|
|
|
#define MYWM_NOTIFYICON (WM_APP+100)
|
|
CShellIconMgr g_ShellIconMgr;
|
|
|
|
bool g_bHighlightIconWhenBusy = false;
|
|
|
|
|
|
// STATE THE SERVICE OWNS.
|
|
int g_iCurState = 0; // One of the VMPI_SERVICE_STATE_ defines.
|
|
char *g_pPassword = NULL;
|
|
bool g_bScreensaverMode = false;
|
|
|
|
|
|
void LogString( const char *pStr, ... )
|
|
{
|
|
#ifdef VMPI_SERVICE_LOGS
|
|
char str[4096];
|
|
va_list marker;
|
|
va_start( marker, pStr );
|
|
_vsnprintf( str, sizeof( str ), pStr, marker );
|
|
va_end( marker );
|
|
|
|
static FILE *fp = fopen( "c:\\vmpi_service_ui.log", "wt" );
|
|
if ( fp )
|
|
{
|
|
fprintf( fp, "%s", str );
|
|
fflush( fp );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
char* GetLastErrorString()
|
|
{
|
|
static char err[2048];
|
|
|
|
LPVOID lpMsgBuf;
|
|
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL );
|
|
strncpy( err, (char*)lpMsgBuf, sizeof( err ) );
|
|
LocalFree( lpMsgBuf );
|
|
|
|
err[ sizeof( err ) - 1 ] = 0;
|
|
return err;
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------ //
|
|
// Persistent state in the registry.
|
|
// ------------------------------------------------------------------------------------------ //
|
|
void LoadStateFromRegistry()
|
|
{
|
|
HKEY hKey = NULL;
|
|
RegCreateKey( HKEY_LOCAL_MACHINE, VMPI_SERVICE_KEY, &hKey );
|
|
if ( hKey )
|
|
{
|
|
DWORD val = 0;
|
|
DWORD type = REG_DWORD;
|
|
DWORD size = sizeof( val );
|
|
if ( RegQueryValueEx(
|
|
hKey,
|
|
"HighlightIconWhenBusy",
|
|
0,
|
|
&type,
|
|
(unsigned char*)&val,
|
|
&size ) == ERROR_SUCCESS &&
|
|
type == REG_DWORD &&
|
|
size == sizeof( val ) )
|
|
{
|
|
g_bHighlightIconWhenBusy = (val != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SaveStateToRegistry()
|
|
{
|
|
HKEY hKey = NULL;
|
|
RegCreateKey( HKEY_LOCAL_MACHINE, VMPI_SERVICE_KEY, &hKey );
|
|
if ( hKey )
|
|
{
|
|
DWORD val = g_bHighlightIconWhenBusy;
|
|
RegSetValueEx(
|
|
hKey,
|
|
"HighlightIconWhenBusy",
|
|
0,
|
|
REG_DWORD,
|
|
(unsigned char*)&val,
|
|
sizeof( val ) );
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------ //
|
|
// Our CServiceConnMgr packet handler.
|
|
// ------------------------------------------------------------------------------------------ //
|
|
|
|
void UpdateAppIcon()
|
|
{
|
|
if ( g_iCurState == VMPI_SERVICE_STATE_IDLE )
|
|
{
|
|
g_ShellIconMgr.ChangeIcon( IDI_WAITING_ICON );
|
|
}
|
|
else if ( g_iCurState == VMPI_SERVICE_STATE_BUSY )
|
|
{
|
|
if ( g_bHighlightIconWhenBusy )
|
|
g_ShellIconMgr.ChangeIcon( IDI_BUSY_ICON );
|
|
else
|
|
g_ShellIconMgr.ChangeIcon( IDI_WAITING_ICON );
|
|
}
|
|
else
|
|
{
|
|
g_ShellIconMgr.ChangeIcon( IDI_DISABLED_ICON );
|
|
}
|
|
}
|
|
|
|
|
|
class CUIConnMgr : public CServiceConnMgr
|
|
{
|
|
public:
|
|
virtual void HandlePacket( const char *pData, int len );
|
|
};
|
|
|
|
|
|
void CUIConnMgr::HandlePacket( const char *pData, int len )
|
|
{
|
|
if ( pData[0] != VMPI_SERVICE_UI_PROTOCOL_VERSION )
|
|
return;
|
|
|
|
int packetID = pData[1];
|
|
|
|
int offset = 2;
|
|
if ( packetID == VMPI_SERVICE_TO_UI_CONSOLE_TEXT )
|
|
{
|
|
Msg( &pData[offset] );
|
|
}
|
|
else if ( packetID == VMPI_SERVICE_TO_UI_STATE )
|
|
{
|
|
// Get the new state out..
|
|
g_iCurState = *((int*)&pData[offset]);
|
|
offset += 4;
|
|
|
|
LogString( "New UI state: %d.\n", g_iCurState );
|
|
|
|
// Update our icon.
|
|
UpdateAppIcon();
|
|
|
|
g_bScreensaverMode = (*((char*)&pData[offset]) != 0);
|
|
++offset;
|
|
|
|
// Store the current password.
|
|
if ( g_pPassword )
|
|
delete [] g_pPassword;
|
|
|
|
const char *pStr = &pData[offset];
|
|
g_pPassword = new char[ strlen( pStr ) + 1 ];
|
|
strcpy( g_pPassword, pStr );
|
|
offset += strlen( pStr ) + 1;
|
|
|
|
UpdatePopupMenuState();
|
|
}
|
|
else if ( packetID == VMPI_SERVICE_TO_UI_PATCHING )
|
|
{
|
|
LogString( "Got a VMPI_SERVICE_TO_UI_PATCHING packet.\n" );
|
|
|
|
int bExitAfter = pData[offset];
|
|
++offset;
|
|
|
|
char workingDir[MAX_PATH], commandLine[4096];
|
|
V_strncpy( workingDir, &pData[offset], sizeof( workingDir ) );
|
|
offset += V_strlen( workingDir ) + 1;
|
|
|
|
V_strncpy( commandLine, &pData[offset], sizeof( commandLine ) );
|
|
offset += V_strlen( commandLine ) + 1;
|
|
|
|
// Run whatever they said to run.
|
|
STARTUPINFO si;
|
|
memset( &si, 0, sizeof( si ) );
|
|
si.cb = sizeof( si );
|
|
|
|
PROCESS_INFORMATION pi;
|
|
memset( &pi, 0, sizeof( pi ) );
|
|
if ( CreateProcess( NULL, commandLine, NULL, NULL, false, CREATE_NO_WINDOW, NULL, workingDir, &si, &pi ) )
|
|
{
|
|
LogString( "CreateProcess succeeded:\n%s\n", commandLine );
|
|
|
|
CloseHandle( pi.hProcess );
|
|
CloseHandle( pi.hThread );
|
|
|
|
if ( bExitAfter )
|
|
PostQuitMessage( 0 );
|
|
}
|
|
else
|
|
{
|
|
LogString( "CreateProcess failed: %s", GetLastErrorString() );
|
|
}
|
|
}
|
|
else if ( packetID == VMPI_SERVICE_TO_UI_EXIT )
|
|
{
|
|
// Exit.
|
|
PostQuitMessage( 0 );
|
|
}
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------ //
|
|
// Helpers.
|
|
// ------------------------------------------------------------------------------------------ //
|
|
|
|
void InternalSpew( const char *pMsg )
|
|
{
|
|
if ( g_pConsoleWnd )
|
|
{
|
|
g_pConsoleWnd->PrintToConsole( pMsg );
|
|
}
|
|
|
|
OutputDebugString( pMsg );
|
|
}
|
|
|
|
|
|
SpewRetval_t MySpewOutputFunc( SpewType_t spewType, const char *pMsg )
|
|
{
|
|
// Prepend the time.
|
|
time_t aclock;
|
|
time( &aclock );
|
|
struct tm *newtime = localtime( &aclock );
|
|
|
|
// Get rid of the \n.
|
|
char timeString[512];
|
|
Q_strncpy( timeString, asctime( newtime ), sizeof( timeString ) );
|
|
char *pEnd = strstr( timeString, "\n" );
|
|
if ( pEnd )
|
|
*pEnd = 0;
|
|
InternalSpew( timeString );
|
|
|
|
InternalSpew( " - " );
|
|
InternalSpew( pMsg );
|
|
|
|
if ( spewType == SPEW_ASSERT )
|
|
return SPEW_DEBUGGER;
|
|
else if( spewType == SPEW_ERROR )
|
|
return SPEW_ABORT;
|
|
else
|
|
return SPEW_CONTINUE;
|
|
}
|
|
|
|
|
|
|
|
CUIConnMgr g_ConnMgr;
|
|
|
|
void InitConsoleWindow()
|
|
{
|
|
// Only initialize it once.
|
|
if ( g_pConsoleWnd )
|
|
return;
|
|
|
|
g_pConsoleWnd = CreateConsoleWnd(
|
|
g_hInstance,
|
|
IDD_VMPI_SERVICE,
|
|
IDC_DEBUG_OUTPUT,
|
|
false );
|
|
}
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------ //
|
|
// Implementation of IShellIconMgrHelper.
|
|
// ------------------------------------------------------------------------------------------ //
|
|
|
|
HMENU g_hMenu = NULL;
|
|
HMENU g_hPopupMenu = NULL; // This is just a submenu of g_hMenu.
|
|
|
|
|
|
bool LoadPopupMenu()
|
|
{
|
|
g_hMenu = LoadMenu( g_hInstance, MAKEINTRESOURCE( IDR_POPUP_MENU ) );
|
|
if ( !g_hMenu )
|
|
{
|
|
Assert( false );
|
|
Warning( "LoadMenu failed.\n" );
|
|
return false;
|
|
}
|
|
|
|
g_hPopupMenu = GetSubMenu( g_hMenu, 0 );
|
|
return true;
|
|
}
|
|
|
|
|
|
void TermPopupMenu()
|
|
{
|
|
if ( g_hMenu )
|
|
{
|
|
DestroyMenu( g_hMenu );
|
|
g_hMenu = NULL;
|
|
}
|
|
g_hPopupMenu = NULL;
|
|
}
|
|
|
|
|
|
void UpdatePopupMenuState()
|
|
{
|
|
bool bEnabled = (g_iCurState == VMPI_SERVICE_STATE_IDLE || g_iCurState == VMPI_SERVICE_STATE_BUSY);
|
|
EnableMenuItem( g_hPopupMenu, ID_ENABLE_WORKER, bEnabled ? MF_GRAYED : MF_ENABLED );
|
|
EnableMenuItem( g_hPopupMenu, ID_DISABLE_WORKER, !bEnabled ? MF_GRAYED : MF_ENABLED );
|
|
|
|
// Enable or disable console items.
|
|
EnableMenuItem( g_hPopupMenu, ID_SHOW_CONSOLE_WINDOW, g_pConsoleWnd->IsVisible() ? MF_GRAYED : MF_ENABLED );
|
|
EnableMenuItem( g_hPopupMenu, ID_HIDE_CONSOLE_WINDOW, !g_pConsoleWnd->IsVisible() ? MF_GRAYED : MF_ENABLED );
|
|
|
|
CheckMenuItem( g_hPopupMenu, ID_SCREENSAVER_MODE, g_bScreensaverMode ? MF_CHECKED : MF_UNCHECKED );
|
|
CheckMenuItem( g_hPopupMenu, ID_HIGHLIGHT_ICON_WHEN_BUSY, g_bHighlightIconWhenBusy ? MF_CHECKED : MF_UNCHECKED );
|
|
}
|
|
|
|
|
|
int CALLBACK SetPasswordDlgProc(
|
|
HWND hwndDlg, // handle to dialog box
|
|
UINT uMsg, // message
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam // second message parameter
|
|
)
|
|
{
|
|
switch( uMsg )
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
if ( g_pPassword )
|
|
{
|
|
HWND hWnd = GetDlgItem( hwndDlg, IDC_PASSWORD );
|
|
SetWindowText( hWnd, g_pPassword );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch( wParam )
|
|
{
|
|
case IDOK:
|
|
{
|
|
// Set our new password.
|
|
HWND hWnd = GetDlgItem( hwndDlg, IDC_PASSWORD );
|
|
if ( hWnd )
|
|
{
|
|
char tempBuf[512];
|
|
GetWindowText( hWnd, tempBuf, sizeof( tempBuf ) );
|
|
|
|
// Send it to the service.
|
|
CUtlVector<char> data;
|
|
data.AddToTail( VMPI_SERVICE_UPDATE_PASSWORD );
|
|
data.AddMultipleToTail( strlen( tempBuf ) + 1, tempBuf );
|
|
g_ConnMgr.SendPacket( -1, data.Base(), data.Count() );
|
|
}
|
|
EndDialog( hwndDlg, 0 );
|
|
}
|
|
break;
|
|
|
|
case IDCANCEL:
|
|
{
|
|
EndDialog( hwndDlg, 0 );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
class CShellIconMgrHelper : public IShellIconMgrHelper
|
|
{
|
|
public:
|
|
virtual HINSTANCE GetHInstance()
|
|
{
|
|
return g_hInstance;
|
|
}
|
|
|
|
virtual int WindowProc( void *pWnd, int uMsg, long wParam, long lParam )
|
|
{
|
|
HWND hWnd = (HWND)pWnd;
|
|
|
|
switch( uMsg )
|
|
{
|
|
// Right button brings up the popup menu.
|
|
case MYWM_NOTIFYICON:
|
|
{
|
|
if ( lParam == WM_RBUTTONDOWN )
|
|
{
|
|
POINT cursorPos;
|
|
GetCursorPos( &cursorPos );
|
|
|
|
UpdatePopupMenuState();
|
|
|
|
// Make a popup menu.
|
|
SetForegroundWindow( hWnd );
|
|
TrackPopupMenu( g_hPopupMenu, TPM_RIGHTALIGN | TPM_BOTTOMALIGN, cursorPos.x, cursorPos.y, 0, hWnd, NULL );
|
|
return 0;
|
|
}
|
|
else if ( lParam == WM_LBUTTONDOWN )
|
|
{
|
|
// Left button brings up the console.
|
|
g_pConsoleWnd->SetVisible( true );
|
|
UpdatePopupMenuState();
|
|
return 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
{
|
|
switch( wParam )
|
|
{
|
|
case ID_ENABLE_WORKER:
|
|
{
|
|
char cPacket = VMPI_SERVICE_ENABLE;
|
|
g_ConnMgr.SendPacket( -1, &cPacket, 1 );
|
|
}
|
|
break;
|
|
|
|
case ID_DISABLE_WORKER:
|
|
{
|
|
char cPacket = VMPI_SERVICE_DISABLE;
|
|
g_ConnMgr.SendPacket( -1, &cPacket, 1 );
|
|
}
|
|
break;
|
|
|
|
case ID_KILLCURRENTJOB:
|
|
{
|
|
char cPacket = VMPI_KILL_PROCESS;
|
|
g_ConnMgr.SendPacket( -1, &cPacket, 1 );
|
|
}
|
|
break;
|
|
|
|
case ID_HIGHLIGHT_ICON_WHEN_BUSY:
|
|
{
|
|
g_bHighlightIconWhenBusy = !g_bHighlightIconWhenBusy;
|
|
SaveStateToRegistry();
|
|
UpdateAppIcon();
|
|
UpdatePopupMenuState();
|
|
}
|
|
break;
|
|
|
|
case ID_SCREENSAVER_MODE:
|
|
{
|
|
g_bScreensaverMode = !g_bScreensaverMode;
|
|
char cPacket[2] = { VMPI_SERVICE_SCREENSAVER_MODE, g_bScreensaverMode };
|
|
g_ConnMgr.SendPacket( -1, cPacket, sizeof( cPacket ) );
|
|
}
|
|
break;
|
|
|
|
|
|
case ID_SHOW_CONSOLE_WINDOW:
|
|
{
|
|
g_pConsoleWnd->SetVisible( true );
|
|
UpdatePopupMenuState();
|
|
}
|
|
break;
|
|
|
|
case ID_HIDE_CONSOLE_WINDOW:
|
|
{
|
|
g_pConsoleWnd->SetVisible( false );
|
|
UpdatePopupMenuState();
|
|
}
|
|
break;
|
|
|
|
case ID_SET_PASSWORD:
|
|
{
|
|
DialogBox( g_hInstance, MAKEINTRESOURCE( IDD_SET_PASSWORD ), NULL, SetPasswordDlgProc );
|
|
}
|
|
break;
|
|
|
|
case ID_EXIT_SERVICE:
|
|
{
|
|
// Quit the service app..
|
|
char cPacket = VMPI_SERVICE_EXIT;
|
|
g_ConnMgr.SendPacket( -1, &cPacket, 1 );
|
|
|
|
// Stop showing the icon.
|
|
g_ShellIconMgr.Term();
|
|
|
|
// Wait for a bit for the connection to go away.
|
|
DWORD startTime = GetTickCount();
|
|
while ( GetTickCount()-startTime < 2000 )
|
|
{
|
|
g_ConnMgr.Update();
|
|
if ( !g_ConnMgr.IsConnected() )
|
|
break;
|
|
else
|
|
Sleep( 10 );
|
|
}
|
|
|
|
// Quit the UI app.
|
|
PostQuitMessage( 0 );
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc( (HWND)hWnd, uMsg, wParam, lParam );
|
|
}
|
|
};
|
|
|
|
CShellIconMgrHelper g_ShellIconMgrHelper;
|
|
|
|
|
|
|
|
int APIENTRY WinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
g_hInstance = hInstance;
|
|
|
|
|
|
LogString( "vmpi_service_ui startup.\n" );
|
|
|
|
// Don't run multiple instances.
|
|
HANDLE hMutex = CreateMutex( NULL, FALSE, "vmpi_service_ui_mutex" );
|
|
if ( hMutex && GetLastError() == ERROR_ALREADY_EXISTS )
|
|
return 1;
|
|
|
|
|
|
// Hook spew output.
|
|
SpewOutputFunc( MySpewOutputFunc );
|
|
InitConsoleWindow();
|
|
LogString( "Setup console window.\n" );
|
|
|
|
LoadStateFromRegistry();
|
|
|
|
// Setup the popup menu.
|
|
if( !LoadPopupMenu() )
|
|
{
|
|
return false;
|
|
}
|
|
UpdatePopupMenuState();
|
|
|
|
|
|
// Setup the tray icon.
|
|
Msg( "Waiting for jobs...\n" );
|
|
if ( !g_ShellIconMgr.Init( &g_ShellIconMgrHelper, g_pIconTooltip, MYWM_NOTIFYICON, IDI_WAITING_ICON ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
// Connect to the VMPI service.
|
|
g_ConnMgr.InitClient();
|
|
|
|
LogString( "Entering main loop.\n" );
|
|
|
|
while ( 1 )
|
|
{
|
|
MSG msg;
|
|
msg.message = !WM_QUIT; // So it doesn't accidentally exit.
|
|
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
|
|
{
|
|
if ( msg.message == WM_QUIT )
|
|
break;
|
|
|
|
TranslateMessage( &msg );
|
|
DispatchMessage( &msg );
|
|
}
|
|
if ( msg.message == WM_QUIT )
|
|
break;
|
|
|
|
g_ConnMgr.Update();
|
|
if ( !g_ConnMgr.IsConnected() )
|
|
{
|
|
g_ShellIconMgr.ChangeIcon( IDI_UNCONNECTED );
|
|
}
|
|
|
|
Sleep( 30 );
|
|
}
|
|
|
|
// Important that we call this instead of letting the destructor do it because it deletes its
|
|
// socket and it needs to cleanup some threads.
|
|
g_ConnMgr.Term();
|
|
|
|
g_ShellIconMgr.Term();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|