rallyunlimited-engine/code/win32/win_syscon.c
2024-02-02 19:46:17 +03:00

1161 lines
28 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// win_syscon.h
#include "../client/client.h"
#include "win_local.h"
#include "resource.h"
#define COPY_ID 1
#define QUIT_ID 2
#define CLEAR_ID 3
#define ERROR_TIMER_ID 4
#define CON_TIMER_ID 5
#define BUF_TIMER_ID 6
#define TEX_TIMER_ID 7
#define ERRORBOX_ID 10
#define ERRORTEXT_ID 11
#define EDIT_ID 100
#define INPUT_ID 101
#define STATUS_ID 102
#define DEFAULT_WIDTH 600
#define DEFAULT_HEIGHT 434
#define BORDERW 1
#define BORDERH 2
#define INPUT_HEIGHT 16
#define ERROR_HEIGHT 27
#define MAX_CONSIZE 65536
#define T TEXT
#define EDIT_COLOR RGB(0x00,0x00,0x10)
#define TEXT_COLOR RGB(0x40,0xEE,0x20)
#define ERROR_BG_COLOR RGB(0x90,0x80,0x80)
#define ERROR_COLOR_1 RGB(0xFF,0xFF,0x00)
#define ERROR_COLOR_2 RGB(0xF0,0x00,0x00)
static field_t console;
typedef struct
{
HWND hWnd;
HWND hwndBuffer;
HWND hwndInputLine;
HWND hwndStatusBar;
HWND hwndButtonClear;
HWND hwndButtonCopy;
HWND hwndErrorBox;
HBRUSH hbrEditBackground;
HBRUSH hbrErrorBackground;
HFONT hfBufferFont;
HFONT hfStatusFont;
char consoleText[512];
char returnedText[512];
int visLevel;
qboolean quitOnClose;
int windowWidth, windowHeight;
LONG_PTR SysInputLineWndProc;
LONG_PTR SysStatusWndProc;
LONG_PTR SysBufferWndProc;
qboolean newline;
} WinConData;
static WinConData s_wcd;
static int maxConSize; // up to MAX_CONSIZE
static int curConSize; // up to MAX_CONSIZE
static UINT texTimerID; // for flushing text in buffer
static char conBuffer[ MAXPRINTMSG ];
static int conBufPos;
static void AddBufferText( const char *text, int textLength );
static void ConClear( void )
{
//SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
//SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" );
SetWindowText( s_wcd.hwndBuffer, T("") );
UpdateWindow( s_wcd.hwndBuffer );
s_wcd.newline = qfalse;
curConSize = 0;
conBufPos = 0;
}
static int GetStatusBarHeight( void )
{
RECT rect;
if ( !s_wcd.hwndStatusBar )
return 22;
GetClientRect( s_wcd.hwndStatusBar, &rect );
return (rect.bottom-rect.top+1);
}
static int GetTimerMsec( void )
{
int msec;
if ( !com_sv_running || !com_sv_running->integer ) {
msec = 50; // 20fps
} else {
msec = 1000 / Cvar_VariableIntegerValue( "sv_fps" );
}
#ifndef DEDICATED
if ( com_cl_running && com_cl_running->integer ) {
if ( com_maxfps->integer ) {
msec = 1000 / com_maxfps->integer;
}
if ( Cvar_VariableIntegerValue( "com_maxfpsUnfocused" ) ) {
msec = 1000 / Cvar_VariableIntegerValue( "com_maxfpsUnfocused" );
}
if ( gw_minimized || CL_VideoRecording() ) {
return 0;
}
}
#endif
return msec;
}
static LRESULT WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
char *cmdString;
static qboolean s_timePolarity;
static UINT conTimerID;
int v;
switch ( uMsg )
{
case WM_SETFOCUS:
if ( s_wcd.hwndInputLine )
{
SetFocus( s_wcd.hwndInputLine );
}
break;
case WM_ACTIVATE:
if ( com_viewlog && ( com_dedicated && !com_dedicated->integer ) )
{
// if the viewlog is open, check to see if it's being minimized
if ( com_viewlog->integer == 1 )
{
if ( HIWORD( wParam ) ) // minimized flag
{
Cvar_Set( "viewlog", "2" );
}
}
else if ( com_viewlog->integer == 2 )
{
if ( !HIWORD( wParam ) ) // minimized flag
{
Cvar_Set( "viewlog", "1" );
}
}
}
break;
case WM_QUERYENDSESSION:
if ( com_dedicated && com_dedicated->integer && !com_errorEntered )
{
cmdString = CopyString( "quit" );
Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString );
}
else
{
PostQuitMessage( 0 );
}
return TRUE;
case WM_CLOSE:
if ( com_dedicated && com_dedicated->integer && !com_errorEntered )
{
cmdString = CopyString( "quit" );
Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString );
}
else if ( s_wcd.quitOnClose )
{
PostQuitMessage( 0 );
}
else
{
Sys_ShowConsole( 0, qfalse );
Cvar_Set( "viewlog", "0" );
}
return 0;
case WM_CTLCOLORSTATIC:
if ( ( HWND ) lParam == s_wcd.hwndBuffer )
{
SetBkColor( ( HDC ) wParam, EDIT_COLOR );
SetTextColor( ( HDC ) wParam, TEXT_COLOR );
return ( LRESULT ) s_wcd.hbrEditBackground;
}
else if ( ( HWND ) lParam == s_wcd.hwndErrorBox )
{
if ( s_timePolarity & 1 )
{
SetBkColor( ( HDC ) wParam, ERROR_BG_COLOR );
SetTextColor( ( HDC ) wParam, ERROR_COLOR_1 );
}
else
{
SetBkColor( ( HDC ) wParam, ERROR_BG_COLOR );
SetTextColor( ( HDC ) wParam, ERROR_COLOR_2 );
}
return ( LRESULT ) s_wcd.hbrErrorBackground;
}
break;
case WM_CREATE:
s_wcd.hbrEditBackground = CreateSolidBrush( EDIT_COLOR );
GetWindowRect( hWnd, &g_wv.conRect );
break;
case WM_MOVE:
GetWindowRect( hWnd, &g_wv.conRect );
break;
case WM_SIZE:
{
RECT rect;
int sth;
sth = GetStatusBarHeight();
GetClientRect( hWnd, &rect );
s_wcd.windowWidth = rect.right - rect.left + 1;
s_wcd.windowHeight = rect.bottom - rect.top + 1;
if ( s_wcd.hwndErrorBox ) {
SetWindowPos( s_wcd.hwndBuffer, HWND_TOP, BORDERW, ERROR_HEIGHT + BORDERH*2, rect.right - BORDERW*2, rect.bottom - sth - ERROR_HEIGHT - BORDERH*3 + 1, SWP_NOZORDER );
} else {
SetWindowPos( s_wcd.hwndBuffer, HWND_TOP, BORDERW, BORDERH, rect.right - BORDERW*2, rect.bottom - sth - INPUT_HEIGHT - BORDERH*3 - 2, SWP_NOZORDER );
}
if ( s_wcd.hwndErrorBox ) {
SetWindowPos( s_wcd.hwndErrorBox, HWND_TOP, BORDERW, BORDERH, rect.right - BORDERW*2, ERROR_HEIGHT, SWP_NOZORDER );
InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE );
}
if ( s_wcd.hwndInputLine ) {
SetWindowPos( s_wcd.hwndInputLine, HWND_TOP, BORDERW, rect.bottom - sth - INPUT_HEIGHT - BORDERH, rect.right - BORDERW*2, INPUT_HEIGHT, SWP_NOZORDER );
InvalidateRect( s_wcd.hwndInputLine, NULL, FALSE );
}
if ( s_wcd.hwndStatusBar ) {
SetWindowPos( s_wcd.hwndStatusBar, HWND_TOP, BORDERW, rect.bottom, rect.right - BORDERW*2, 26, SWP_NOZORDER );
InvalidateRect( s_wcd.hwndStatusBar, NULL, FALSE );
}
GetWindowRect( hWnd, &g_wv.conRect );
return 0;
}
case WM_SIZING:
{
int w, h;
RECT *r;
r = (LPRECT) lParam;
w = r->right - r->left - 280+BORDERW*2 + 1;
h = r->bottom - r->top - 155+BORDERH*3 + 1;
if ( w < 0 ) {
if ( wParam == WMSZ_RIGHT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT ) {
r->right -= w;
}
if ( wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT ) {
r->left += w;
}
}
if ( h < 0 ) {
if ( wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_BOTTOMRIGHT ) {
r->bottom -= h;
}
if ( wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT ) {
r->top += h;
}
}
return TRUE;
}
case WM_SYSCOMMAND:
// Prevent Alt+Letter commands from hanging the application temporarily
if ( wParam == SC_KEYMENU || wParam == SC_MOUSEMENU + HTSYSMENU || wParam == SC_CLOSE + HTSYSMENU )
return 0;
// simulate drag move to avoid ~500ms delay between DefWindowProc() and further WM_ENTERSIZEMOVE
if ( wParam == SC_MOVE + HTCAPTION )
{
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN, 7, 0, 0, 0 );
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN, (DWORD)-7, 0, 0, 0 );
}
break;
case WM_ENTERSIZEMOVE:
if ( conTimerID == 0 && (v = GetTimerMsec()) > 0 ) {
conTimerID = SetTimer( s_wcd.hWnd, CON_TIMER_ID, v, NULL );
}
break;
case WM_EXITSIZEMOVE:
if ( conTimerID != 0 ) {
KillTimer( s_wcd.hWnd, conTimerID );
conTimerID = 0;
}
break;
case WM_TIMER:
if ( wParam == ERROR_TIMER_ID )
{
s_timePolarity = !s_timePolarity;
if ( s_wcd.hwndErrorBox )
{
InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE );
}
}
else if ( wParam == CON_TIMER_ID && conTimerID != 0 && !com_errorEntered )
{
#ifdef DEDICATED
Com_Frame( qfalse );
#else
//Com_Frame( CL_NoDelay() );
#endif
}
break;
case WM_CONTEXTMENU:
return 0;
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
static LRESULT WINAPI BufferWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
static UINT bufTimerID;
int v;
switch ( uMsg ) {
case WM_VSCROLL:
if ( (int)LOWORD(wParam) == SB_ENDSCROLL ) {
if ( bufTimerID != 0 ) {
KillTimer( hWnd, bufTimerID );
bufTimerID = 0;
}
} else {
if ( bufTimerID == 0 && (v = GetTimerMsec()) > 0 ) {
bufTimerID = SetTimer( hWnd, BUF_TIMER_ID, v, NULL );
}
}
break;
case WM_CAPTURECHANGED:
if ( (HWND)lParam == hWnd ) {
if ( bufTimerID == 0 && (v = GetTimerMsec()) > 0 )
bufTimerID = SetTimer( hWnd, BUF_TIMER_ID, v, NULL );
} else {
if ( bufTimerID != 0 ) {
KillTimer( hWnd, bufTimerID );
bufTimerID = 0;
}
}
return 0;
#if 0 // this is actually redundant except setting focus to s_wcd.hwndInputLine
case WM_COPY: {
DWORD selStart, selEnd;
SendMessage( hWnd, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd );
if ( selStart != selEnd ) {
if ( OpenClipboard( s_wcd.hWnd ) ) {
int len;
HGLOBAL hMem;
TCHAR *text, *tmp;
len = GetWindowTextLength( s_wcd.hwndBuffer ) + 1;
tmp = (TCHAR*) malloc( len * sizeof( TCHAR ) );
if ( tmp ) {
GetWindowText( s_wcd.hwndBuffer, tmp, len );
hMem = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT, ( selEnd - selStart + 1 ) * sizeof( TCHAR ) );
if ( hMem != NULL ) {
EmptyClipboard();
text = (TCHAR*) GlobalLock( hMem );
if ( text != NULL ) {
memcpy( text, tmp + selStart, ( selEnd - selStart ) * sizeof( text[0] ) );
}
GlobalUnlock( hMem );
#ifdef UNICODE
SetClipboardData( CF_UNICODETEXT, hMem );
#else
SetClipboardData( CF_TEXT, hMem );
#endif
}
free( tmp );
}
CloseClipboard();
}
if ( s_wcd.hwndInputLine ) {
SetFocus( s_wcd.hwndInputLine );
}
return 0;
}
}
break;
#endif
case WM_TIMER:
if ( wParam == BUF_TIMER_ID && bufTimerID != 0 && !com_errorEntered )
{
#ifdef DEDICATED
Com_Frame( qfalse );
#else
//Com_Frame( CL_NoDelay() );
#endif
}
if ( wParam == TEX_TIMER_ID && texTimerID != 0 ) {
if ( conBufPos ) {
// dump text
AddBufferText( conBuffer, conBufPos );
conBufPos = 0;
} else {
// kill timer
KillTimer( hWnd, texTimerID );
texTimerID = 0;
}
}
return 0;
case WM_CONTEXTMENU:
return 0;
case WM_CHAR: {
if ( wParam != VK_CANCEL ) {
// forward to input line
SetFocus( s_wcd.hwndInputLine );
SendMessage( s_wcd.hwndInputLine, WM_CHAR, wParam, lParam );
return 0;
}
}
break;
}
return CallWindowProc( (WNDPROC) s_wcd.SysBufferWndProc, hWnd, uMsg, wParam, lParam );
}
static LRESULT WINAPI StatusWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
HGLOBAL hMem;
TCHAR *text;
int len;
switch (uMsg)
{
case WM_COMMAND:
if ( wParam == COPY_ID )
{
if ( OpenClipboard( s_wcd.hWnd ) )
{
EmptyClipboard();
len = GetWindowTextLength( s_wcd.hwndBuffer );
if ( len > 0 ) {
hMem = GlobalAlloc( GMEM_MOVEABLE | GMEM_DDESHARE | GMEM_ZEROINIT,
(len + 1) * sizeof( TCHAR ) );
if ( hMem != NULL ) {
text = ( TCHAR* )GlobalLock( hMem );
if ( text != NULL ) {
GetWindowText( s_wcd.hwndBuffer, text, len + 1 );
}
GlobalUnlock( hMem );
#ifdef UNICODE
SetClipboardData( CF_UNICODETEXT, hMem );
#else
SetClipboardData( CF_TEXT, hMem );
#endif
}
}
CloseClipboard();
}
if ( s_wcd.hwndInputLine ) {
SetFocus( s_wcd.hwndInputLine );
}
}
else if ( wParam == CLEAR_ID )
{
ConClear();
if ( s_wcd.hwndInputLine ) {
SetFocus( s_wcd.hwndInputLine );
}
}
break;
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
if ( s_wcd.hwndInputLine ) {
SetFocus( s_wcd.hwndInputLine );
}
break;
}
return CallWindowProc( (WNDPROC)s_wcd.SysStatusWndProc, hWnd, uMsg, wParam, lParam );
}
static LRESULT WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
TCHAR inputBuffer[ MAX_EDIT_LINE ];
int zDelta, fwKeys, i;
WPARAM scrollMsg;
switch ( uMsg )
{
#if 0
case WM_KILLFOCUS:
if ( (HWND)wParam == s_wcd.hwndBuffer ) {
SetFocus( s_wcd.hwndInputLine );
return 0;
}
break;
#endif
case WM_MOUSEWHEEL:
zDelta = (short) HIWORD( wParam ) / WHEEL_DELTA;
if ( zDelta ) {
fwKeys = LOWORD( wParam );
if ( zDelta > 0 ) {
if ( fwKeys & MK_CONTROL )
scrollMsg = SB_PAGEUP;
else
scrollMsg = SB_LINEUP;
} else {
zDelta = -zDelta;
if ( fwKeys & MK_CONTROL )
scrollMsg = SB_PAGEDOWN;
else
scrollMsg = SB_LINEDOWN;
}
for ( i = 0; i < zDelta; i++ ) {
SendMessage( s_wcd.hwndBuffer, EM_SCROLL, scrollMsg, 0 );
}
return 0;
}
break;
case WM_KEYDOWN:
{
if ( wParam == 'L' && ( GetAsyncKeyState( VK_LCONTROL ) & 0x8000 || GetAsyncKeyState( VK_RCONTROL ) & 0x8000 ) ) {
ConClear();
return 0;
}
if ( wParam == VK_PRIOR ) {
if ( GetAsyncKeyState( VK_LCONTROL ) & 0x8000 || GetAsyncKeyState( VK_RCONTROL ) & 0x8000 )
SendMessage( s_wcd.hwndBuffer, EM_SCROLL, (WPARAM)SB_PAGEUP, 0 );
else
SendMessage( s_wcd.hwndBuffer, EM_SCROLL, (WPARAM)SB_LINEUP, 0 );
return 0;
}
if ( wParam == VK_NEXT ) {
if ( GetAsyncKeyState( VK_LCONTROL ) & 0x8000 || GetAsyncKeyState( VK_RCONTROL ) & 0x8000 )
SendMessage( s_wcd.hwndBuffer, EM_SCROLL, (WPARAM)SB_PAGEDOWN, 0 );
else
SendMessage( s_wcd.hwndBuffer, EM_SCROLL, (WPARAM)SB_LINEDOWN, 0 );
return 0;
}
if ( wParam == VK_UP ) {
Con_HistoryGetPrev( &console );
SetWindowText( hWnd, AtoW( console.buffer ) );
SendMessage( hWnd, EM_SETSEL, (WPARAM) console.cursor, console.cursor );
return 0;
}
if ( wParam == VK_DOWN ) {
Con_HistoryGetNext( &console );
SetWindowText( hWnd, AtoW( console.buffer ) );
SendMessage( hWnd, EM_SETSEL, (WPARAM) console.cursor, console.cursor );
return 0;
}
break;
}
case WM_CHAR:
if ( wParam > 255 )
return 0;
if ( wParam == VK_RETURN )
{
DWORD pos;
char *s;
GetWindowText( hWnd, inputBuffer, sizeof( inputBuffer ) );
Q_strncpyz( console.buffer, WtoA( inputBuffer ), sizeof( console.buffer ) );
SendMessage( hWnd, EM_GETSEL, (WPARAM) &pos, (LPARAM) 0 );
console.cursor = pos;
Con_SaveField( &console );
s = console.buffer;
while ( *s == '\\' || *s == '/' ) // skip leading slashes
s++;
strncat( s_wcd.consoleText, s, sizeof( s_wcd.consoleText ) - strlen( s_wcd.consoleText ) - 2 );
strcat( s_wcd.consoleText, "\n" );
SetWindowText( s_wcd.hwndInputLine, T("") );
Field_Clear( &console );
Sys_Print( va( "]%s\n", WtoA( inputBuffer ) ) );
return 0;
}
if ( wParam == VK_TAB ) {
DWORD pos;
GetWindowText( hWnd, inputBuffer, sizeof( inputBuffer ) );
Q_strncpyz( console.buffer, WtoA( inputBuffer ), sizeof( console.buffer ) );
SendMessage( hWnd, EM_GETSEL, (WPARAM) &pos, (LPARAM) 0 );
console.cursor = pos;
Field_AutoComplete( &console );
SetWindowText( hWnd, AtoW( console.buffer ) );
SendMessage( hWnd, EM_SETSEL, console.cursor, console.cursor );
return 0;
}
break;
case WM_CONTEXTMENU:
return 0;
}
return CallWindowProc( (WNDPROC)s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam );
}
/*
** Sys_CreateConsole
*/
void Sys_CreateConsole( const char *title, int xPos, int yPos, qboolean useXYpos )
{
HDC hDC;
WNDCLASS wc;
RECT rect;
const TCHAR *DEDCLASS = T("Q3 WinConsole");
int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX;
int fontWidth, fontHeight, statusFontHeight;
int widths[2] = { 140, -1 };
int borders[3];
int x, y, w, h, sth;
int con_x, con_y;
HMONITOR hMonitor;
MONITORINFO mInfo;
POINT p;
memset( &wc, 0, sizeof( wc ) );
wc.style = 0;
wc.lpfnWndProc = ConWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = g_wv.hInstance;
wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor = LoadCursor (NULL,IDC_ARROW);
wc.hbrBackground = (HBRUSH)(LRESULT)COLOR_WINDOW;
wc.lpszMenuName = 0;
wc.lpszClassName = DEDCLASS;
if ( !RegisterClass (&wc) )
return;
rect.left = 0;
rect.right = DEFAULT_WIDTH;
rect.top = 0;
rect.bottom = DEFAULT_HEIGHT;
AdjustWindowRect( &rect, DEDSTYLE, FALSE );
// try to use command line provided coodinates to locate primary monitor
if ( useXYpos ) {
p.x = xPos;
p.y = yPos;
} else {
GetCursorPos( &p );
}
memset( &mInfo, 0, sizeof( mInfo ) );
mInfo.cbSize = sizeof( MONITORINFO );
// Query display dimensions
hMonitor = MonitorFromPoint( p, MONITOR_DEFAULTTONEAREST );
if ( hMonitor && GetMonitorInfo( hMonitor, &mInfo ) ) {
// current monitor info
w = mInfo.rcMonitor.right - mInfo.rcMonitor.left;
h = mInfo.rcMonitor.bottom - mInfo.rcMonitor.top;
x = mInfo.rcMonitor.left;
y = mInfo.rcMonitor.top;
} else {
// primary display info
hDC = GetDC( GetDesktopWindow() );
w = GetDeviceCaps( hDC, HORZRES );
h = GetDeviceCaps( hDC, VERTRES );
x = 0;
y = 0;
ReleaseDC( GetDesktopWindow(), hDC );
}
fontWidth = -8;
fontHeight = -12;
statusFontHeight = -11;
s_wcd.windowWidth = rect.right - rect.left + 1;
s_wcd.windowHeight = rect.bottom - rect.top + 1;
#ifdef DEDICATED
if ( useXYpos )
{
con_x = xPos;
con_y = yPos;
}
else
#endif
{
con_x = x + ( w - s_wcd.windowWidth ) / 2;
con_y = y + ( h - s_wcd.windowHeight ) / 2;
}
s_wcd.hWnd = CreateWindowEx( 0, DEDCLASS,
T(CONSOLE_WINDOW_TITLE), DEDSTYLE, con_x, con_y,
s_wcd.windowWidth, s_wcd.windowHeight,
NULL, NULL, g_wv.hInstance, NULL );
if ( s_wcd.hWnd == NULL )
return;
InitCommonControls();
s_wcd.hfBufferFont = CreateFont( fontHeight, fontWidth,
0,
0,
FW_DONTCARE,
FALSE,
FALSE,
FALSE,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
FF_MODERN | FIXED_PITCH,
T("Terminal") );
s_wcd.hfStatusFont = CreateFont( statusFontHeight, 0,
0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
DEFAULT_PITCH,
T("Tahoma") );
s_wcd.hwndStatusBar = CreateWindow( STATUSCLASSNAME, NULL, WS_VISIBLE | WS_CHILD,
1,1,32,32, s_wcd.hWnd, NULL, g_wv.hInstance, NULL );
// split statusbar into parts and set styles
SendMessage( s_wcd.hwndStatusBar, WM_SETFONT, ( WPARAM ) s_wcd.hfStatusFont, 0 );
SendMessage( s_wcd.hwndStatusBar, SB_GETBORDERS, 0, (LPARAM)&borders );
widths[0] += borders[1]*2; // count vertical borders
SendMessage( s_wcd.hwndStatusBar, SB_SETPARTS, 2, (LPARAM)&widths );
SendMessage( s_wcd.hwndStatusBar, SB_SETTEXT, 0 | SBT_NOBORDERS, (LPARAM)"" );
SendMessage( s_wcd.hwndStatusBar, SB_GETRECT, 0, (LPARAM)&rect );
rect.left += borders[1];
rect.right -= borders[1];
x = rect.left;
h = rect.bottom - rect.top - 1;
w = (rect.right - rect.left - 4) / 2;
// create the buttons
s_wcd.hwndButtonCopy = CreateWindow( T("button"), T("copy"), WS_VISIBLE | WS_CHILD,
x, rect.top, w, h, s_wcd.hwndStatusBar, (HMENU)(LRESULT)COPY_ID, g_wv.hInstance, NULL );
x += w + 4;
s_wcd.hwndButtonClear = CreateWindow( T("button"), T("clear"), WS_VISIBLE | WS_CHILD,
x, rect.top, w, h, s_wcd.hwndStatusBar, (HMENU)(LRESULT)CLEAR_ID, g_wv.hInstance, NULL );
SendMessage( s_wcd.hwndButtonCopy, WM_SETFONT, ( WPARAM ) s_wcd.hfStatusFont, 0 );
SendMessage( s_wcd.hwndButtonClear, WM_SETFONT, ( WPARAM ) s_wcd.hfStatusFont, 0 );
sth = GetStatusBarHeight();
GetClientRect( s_wcd.hWnd, &rect );
// create fonts
//hDC = GetDC( s_wcd.hWnd );
//nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72);
//ReleaseDC( s_wcd.hWnd, hDC );
// create the input line
s_wcd.hwndInputLine = CreateWindow( T("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_BORDER |
ES_LEFT | ES_AUTOHSCROLL,
BORDERW, rect.bottom - sth - INPUT_HEIGHT - BORDERH, rect.right - BORDERW*2, INPUT_HEIGHT,
s_wcd.hWnd,
(HMENU)(LRESULT)INPUT_ID, // child window ID
g_wv.hInstance, NULL );
// create the scrollbuffer
s_wcd.hwndBuffer = CreateWindow( T("edit"), NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER |
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | ES_NOHIDESEL,
BORDERW, BORDERH, rect.right - BORDERW*2, rect.bottom - sth - INPUT_HEIGHT - BORDERH*3 - 2,
s_wcd.hWnd,
(HMENU)(LRESULT)EDIT_ID, // child window ID
g_wv.hInstance, NULL );
SendMessage( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
s_wcd.SysInputLineWndProc = SetWindowLongPtr( s_wcd.hwndInputLine, GWLP_WNDPROC, ( LONG_PTR ) InputLineWndProc );
s_wcd.SysStatusWndProc = SetWindowLongPtr( s_wcd.hwndStatusBar, GWLP_WNDPROC, ( LONG_PTR ) StatusWndProc );
s_wcd.SysBufferWndProc = SetWindowLongPtr( s_wcd.hwndBuffer, GWLP_WNDPROC, ( LONG_PTR ) BufferWndProc );
if ( title && *title ) {
SetWindowText( s_wcd.hWnd, AtoW( title ) );
}
ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT );
UpdateWindow( s_wcd.hWnd );
SetForegroundWindow( s_wcd.hWnd );
SendMessage( s_wcd.hwndBuffer, EM_SETLIMITTEXT, MAX_CONSIZE, 0 );
maxConSize = SendMessage( s_wcd.hwndBuffer, EM_GETLIMITTEXT, 0, 0 );
SendMessage( s_wcd.hwndInputLine, EM_SETLIMITTEXT, MAX_EDIT_LINE, 0 );
Field_Clear( &console );
ConClear();
Sys_SetStatus( "Server is not running" );
s_wcd.visLevel = 1;
}
/*
** Sys_DestroyConsole
*/
void Sys_DestroyConsole( void )
{
if ( s_wcd.hWnd )
{
ShowWindow( s_wcd.hWnd, SW_HIDE );
CloseWindow( s_wcd.hWnd );
DestroyWindow( s_wcd.hWnd );
s_wcd.hWnd = NULL;
}
}
/*
** Sys_ShowConsole
*/
void Sys_ShowConsole( int visLevel, qboolean quitOnClose )
{
s_wcd.quitOnClose = quitOnClose;
if ( visLevel == s_wcd.visLevel )
{
return;
}
s_wcd.visLevel = visLevel;
if ( !s_wcd.hWnd )
return;
switch ( visLevel )
{
case 0:
ShowWindow( s_wcd.hWnd, SW_HIDE );
break;
case 1:
ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL );
curConSize = GetWindowTextLength( s_wcd.hwndBuffer );
SendMessage( s_wcd.hwndBuffer, EM_SETSEL, curConSize, curConSize );
SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 );
//SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
break;
case 2:
ShowWindow( s_wcd.hWnd, SW_MINIMIZE );
break;
default:
Sys_Error( "Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel );
break;
}
}
/*
=============
Sys_SetStatus
=============
*/
void QDECL Sys_SetStatus( const char *format, ... )
{
va_list argptr;
char text[256];
if ( s_wcd.hwndStatusBar == NULL )
return;
text[0] = ' '; // add leading space for better look :P
va_start( argptr, format );
Q_vsnprintf( text + 1, sizeof( text ) - 1, format, argptr );
va_end( argptr );
SendMessage( s_wcd.hwndStatusBar, SB_SETTEXT, (WPARAM) 1 | 0, (LPARAM) AtoW( text ) );
}
/*
=================
Sys_ConsoleInput
=================
*/
char *Sys_ConsoleInput( void )
{
if ( s_wcd.consoleText[0] == '\0' )
{
return NULL;
}
strcpy( s_wcd.returnedText, s_wcd.consoleText );
s_wcd.consoleText[0] = '\0';
return s_wcd.returnedText;
}
/*
=================
Conbuf_AppendText
=================
*/
void Conbuf_AppendText( const char *msg )
{
char buffer[MAXPRINTMSG*2]; // reserve space for CR-LF expansion
char *b = buffer;
int bufLen, n;
n = strlen( msg );
// if the message is REALLY long, use just the last portion of it
if ( n > (MAXPRINTMSG - 1) ) {
msg += n - (MAXPRINTMSG - 1);
}
// insert skipped newline from previous message
if ( s_wcd.newline ) {
s_wcd.newline = qfalse;
*b++ = '\r';
*b++ = '\n';
}
// copy into an intermediate buffer
while ( *msg )
{
if ( *msg == '\n' )
{
*b++ = '\r';
*b++ = '\n';
msg++;
}
else if ( *msg == '\r' )
{
*b++ = '\r';
*b++ = '\n';
msg++;
if ( *msg == '\n' )
msg++;
}
else if ( Q_IsColorString( msg ) )
{
msg += 2;
}
else
{
*b++ = *msg++;
}
}
// try to skip ending newline to avoid inserting empty line in edit control
if ( b - buffer >= 2 && *(b-1) == '\n' && *(b-2) == '\r' ) {
s_wcd.newline = qtrue;
b -= 2;
}
*b = '\0';
bufLen = b - buffer;
// not enough space in buffer -> flush
if ( bufLen + conBufPos >= sizeof( conBuffer )-1 ) {
AddBufferText( conBuffer, conBufPos );
conBufPos = 0;
}
// new message is too long -> flush
if ( bufLen >= sizeof( conBuffer )-1 ) {
if ( conBufPos ) {
AddBufferText( conBuffer, conBufPos );
conBufPos = 0;
}
AddBufferText( buffer, bufLen );
return;
}
// accumulate
memcpy( conBuffer + conBufPos, buffer, bufLen + 1 );
conBufPos += bufLen;
// set flush timer
if ( texTimerID == 0 ) {
texTimerID = SetTimer( s_wcd.hwndBuffer, TEX_TIMER_ID,
s_wcd.visLevel == 1 ? 25 : 100, NULL );
}
}
static void AddBufferText( const char *text, int textLength )
{
int lineCount;
int pos, n;
if ( textLength + curConSize >= maxConSize ) {
lineCount = SendMessage( s_wcd.hwndBuffer, EM_GETLINECOUNT, 0, 0 );
// cut off half from total lines count
lineCount /= 2;
if ( lineCount <= 1 ) {
SetWindowText( s_wcd.hwndBuffer, T("") );
} else {
pos = SendMessage( s_wcd.hwndBuffer, EM_LINEINDEX, lineCount, 0 );
SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, pos );
SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, (LPARAM) TEXT("") );
}
curConSize = 0;
}
if ( !curConSize )
curConSize = GetWindowTextLength( s_wcd.hwndBuffer );
SendMessage( s_wcd.hwndBuffer, EM_GETSEL, (WPARAM)(LPDWORD)&pos, (LPARAM)(LPDWORD)&n );
if ( pos != curConSize || n != curConSize ) {
SendMessage( s_wcd.hwndBuffer, EM_SETSEL, curConSize, curConSize );
}
// put this text into the windows console
//SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 );
SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) AtoW( text ) );
curConSize += textLength;
}
/*
** Sys_SetErrorText
*/
void Sys_SetErrorText( const char *buf )
{
RECT rect;
int sth;
if ( s_wcd.hwndErrorBox ) // already created
return;
// remove input field
DestroyWindow( s_wcd.hwndInputLine );
s_wcd.hwndInputLine = NULL;
EnableWindow( s_wcd.hwndButtonClear, FALSE );
s_wcd.hbrErrorBackground = CreateSolidBrush( ERROR_BG_COLOR );
SetTimer( s_wcd.hWnd, ERROR_TIMER_ID, 1000, NULL );
sth = GetStatusBarHeight();
GetClientRect( s_wcd.hWnd, &rect );
// shift buffer position
SetWindowPos( s_wcd.hwndBuffer, HWND_TOP, BORDERW, ERROR_HEIGHT + BORDERH*2, rect.right - BORDERW*2, rect.bottom - sth - ERROR_HEIGHT - BORDERH*3+1, SWP_NOZORDER );
s_wcd.hwndErrorBox = CreateWindow( T("static"), NULL, WS_CHILD | WS_VISIBLE | SS_SUNKEN,
BORDERW, BORDERH, rect.right - BORDERW*2, ERROR_HEIGHT,
s_wcd.hWnd,
(HMENU)(LRESULT)ERRORBOX_ID, // child window ID
g_wv.hInstance, NULL );
SendMessage( s_wcd.hwndErrorBox, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
SetWindowText( s_wcd.hwndErrorBox, AtoW( buf ) );
Sys_SetStatus( "Fatal error occurred" );
}
void HandleConsoleEvents( void ) {
MSG msg;
// pump the message loop
while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) {
if ( GetMessage( &msg, NULL, 0, 0 ) <= 0 ) {
Cmd_Clear();
Com_Quit_f();
}
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}