DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

win_syscon.cpp (15581B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../../idlib/precompiled.h"
     31 
     32 #include <errno.h>
     33 #include <float.h>
     34 #include <fcntl.h>
     35 #include <stdio.h>
     36 #include <direct.h>
     37 #include <io.h>
     38 #include <conio.h>
     39 
     40 #include "win_local.h"
     41 #include "rc/doom_resource.h"
     42 
     43 #define COPY_ID			1
     44 #define QUIT_ID			2
     45 #define CLEAR_ID		3
     46 
     47 #define ERRORBOX_ID		10
     48 #define ERRORTEXT_ID	11
     49 
     50 #define EDIT_ID			100
     51 #define INPUT_ID		101
     52 
     53 #define	COMMAND_HISTORY	64
     54 
     55 typedef struct {
     56 	HWND		hWnd;
     57 	HWND		hwndBuffer;
     58 
     59 	HWND		hwndButtonClear;
     60 	HWND		hwndButtonCopy;
     61 	HWND		hwndButtonQuit;
     62 
     63 	HWND		hwndErrorBox;
     64 	HWND		hwndErrorText;
     65 
     66 	HBITMAP		hbmLogo;
     67 	HBITMAP		hbmClearBitmap;
     68 
     69 	HBRUSH		hbrEditBackground;
     70 	HBRUSH		hbrErrorBackground;
     71 
     72 	HFONT		hfBufferFont;
     73 	HFONT		hfButtonFont;
     74 
     75 	HWND		hwndInputLine;
     76 
     77 	char		errorString[80];
     78 
     79 	char		consoleText[512], returnedText[512];
     80 	bool		quitOnClose;
     81 	int			windowWidth, windowHeight;
     82 	 
     83 	WNDPROC		SysInputLineWndProc;
     84 
     85 	idEditField	historyEditLines[COMMAND_HISTORY];
     86 
     87 	int			nextHistoryLine;// the last line in the history buffer, not masked
     88 	int			historyLine;	// the line being displayed from history buffer
     89 								// will be <= nextHistoryLine
     90 
     91 	idEditField	consoleField;
     92 
     93 } WinConData;
     94 
     95 static WinConData s_wcd;
     96 
     97 static LONG WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
     98 	char *cmdString;
     99 	static bool s_timePolarity;
    100 
    101 	switch (uMsg) {
    102 		case WM_ACTIVATE:
    103 			if ( LOWORD( wParam ) != WA_INACTIVE ) {
    104 				SetFocus( s_wcd.hwndInputLine );
    105 			}
    106 		break;
    107 		case WM_CLOSE:
    108 			if ( s_wcd.quitOnClose ) {
    109 				PostQuitMessage( 0 );
    110 			} else {
    111 				Sys_ShowConsole( 0, false );
    112 				win32.win_viewlog.SetBool( false );
    113 			}
    114 			return 0;
    115 		case WM_CTLCOLORSTATIC:
    116 			if ( ( HWND ) lParam == s_wcd.hwndBuffer ) {
    117 				SetBkColor( ( HDC ) wParam, RGB( 0x00, 0x00, 0x80 ) );
    118 				SetTextColor( ( HDC ) wParam, RGB( 0xff, 0xff, 0x00 ) );
    119 				return ( long ) s_wcd.hbrEditBackground;
    120 			} else if ( ( HWND ) lParam == s_wcd.hwndErrorBox ) {
    121 				if ( s_timePolarity & 1 ) {
    122 					SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) );
    123 					SetTextColor( ( HDC ) wParam, RGB( 0xff, 0x0, 0x00 ) );
    124 				} else {
    125 					SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) );
    126 					SetTextColor( ( HDC ) wParam, RGB( 0x00, 0x0, 0x00 ) );
    127 				}
    128 				return ( long ) s_wcd.hbrErrorBackground;
    129 			}
    130 			break;
    131 		case WM_SYSCOMMAND:
    132 			if ( wParam == SC_CLOSE ) {
    133 				PostQuitMessage( 0 );
    134 			}
    135 			break;
    136 		case WM_COMMAND:
    137 			if ( wParam == COPY_ID ) {
    138 				SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
    139 				SendMessage( s_wcd.hwndBuffer, WM_COPY, 0, 0 );
    140 			} else if ( wParam == QUIT_ID ) {
    141 				if ( s_wcd.quitOnClose ) {
    142 					PostQuitMessage( 0 );
    143 				} else {
    144 					cmdString = Mem_CopyString( "quit" );
    145 					Sys_QueEvent( SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString, 0 );
    146 				}
    147 			} else if ( wParam == CLEAR_ID ) {
    148 				SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
    149 				SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" );
    150 				UpdateWindow( s_wcd.hwndBuffer );
    151 			}
    152 			break;
    153 		case WM_CREATE:
    154 			s_wcd.hbrEditBackground = CreateSolidBrush( RGB( 0x00, 0x00, 0x80 ) );
    155 			s_wcd.hbrErrorBackground = CreateSolidBrush( RGB( 0x80, 0x80, 0x80 ) );
    156 			SetTimer( hWnd, 1, 1000, NULL );
    157 			break;
    158 /*
    159 		case WM_ERASEBKGND:
    160 			HGDIOBJ oldObject;
    161 			HDC hdcScaled;
    162 			hdcScaled = CreateCompatibleDC( ( HDC ) wParam );
    163 			assert( hdcScaled != 0 );
    164 			if ( hdcScaled ) {
    165 				oldObject = SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo );
    166 				assert( oldObject != 0 );
    167 				if ( oldObject )
    168 				{
    169 					StretchBlt( ( HDC ) wParam, 0, 0, s_wcd.windowWidth, s_wcd.windowHeight, 
    170 						hdcScaled, 0, 0, 512, 384,
    171 						SRCCOPY );
    172 				}
    173 				DeleteDC( hdcScaled );
    174 				hdcScaled = 0;
    175 			}
    176 			return 1;
    177 */
    178 		case WM_TIMER:
    179 			if ( wParam == 1 ) {
    180 				s_timePolarity = (bool)!s_timePolarity;
    181 				if ( s_wcd.hwndErrorBox ) {
    182 					InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE );
    183 				}
    184 			}
    185 			break;
    186     }
    187 
    188     return DefWindowProc( hWnd, uMsg, wParam, lParam );
    189 }
    190 
    191 LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    192 	int key, cursor;
    193 	switch ( uMsg ) {
    194 	case WM_KILLFOCUS:
    195 		if ( ( HWND ) wParam == s_wcd.hWnd || ( HWND ) wParam == s_wcd.hwndErrorBox ) {
    196 			SetFocus( hWnd );
    197 			return 0;
    198 		}
    199 		break;
    200 
    201 	case WM_KEYDOWN:
    202 		key = ( ( lParam >> 16 ) & 0xFF ) | ( ( ( lParam >> 24 ) & 1 ) << 7 );
    203 
    204 		// command history
    205 		if ( ( key == K_UPARROW ) || ( key == K_KP_8 ) ) {
    206 			if ( s_wcd.nextHistoryLine - s_wcd.historyLine < COMMAND_HISTORY && s_wcd.historyLine > 0 ) {
    207 				s_wcd.historyLine--;
    208 			}
    209 			s_wcd.consoleField = s_wcd.historyEditLines[ s_wcd.historyLine % COMMAND_HISTORY ];
    210 
    211 			SetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer() );
    212 			SendMessage( s_wcd.hwndInputLine, EM_SETSEL, s_wcd.consoleField.GetCursor(), s_wcd.consoleField.GetCursor() );
    213 			return 0;
    214 		}
    215 
    216 		if ( ( key == K_DOWNARROW ) || ( key == K_KP_2 ) ) {
    217 			if ( s_wcd.historyLine == s_wcd.nextHistoryLine ) {
    218 				return 0;
    219 			}
    220 			s_wcd.historyLine++;
    221 			s_wcd.consoleField = s_wcd.historyEditLines[ s_wcd.historyLine % COMMAND_HISTORY ];
    222 
    223 			SetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer() );
    224 			SendMessage( s_wcd.hwndInputLine, EM_SETSEL, s_wcd.consoleField.GetCursor(), s_wcd.consoleField.GetCursor() );
    225 			return 0;
    226 		}
    227 		break;
    228 
    229 	case WM_CHAR:
    230 		key = ( ( lParam >> 16 ) & 0xFF ) | ( ( ( lParam >> 24 ) & 1 ) << 7 );
    231 
    232 		GetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer(), MAX_EDIT_LINE );
    233 		SendMessage( s_wcd.hwndInputLine, EM_GETSEL, (WPARAM) NULL, (LPARAM) &cursor );
    234 		s_wcd.consoleField.SetCursor( cursor );
    235 
    236 		// enter the line
    237 		if ( key == K_ENTER || key == K_KP_ENTER ) {
    238 			strncat( s_wcd.consoleText, s_wcd.consoleField.GetBuffer(), sizeof( s_wcd.consoleText ) - strlen( s_wcd.consoleText ) - 5 );
    239 			strcat( s_wcd.consoleText, "\n" );
    240 			SetWindowText( s_wcd.hwndInputLine, "" );
    241 
    242 			Sys_Printf( "]%s\n", s_wcd.consoleField.GetBuffer() );
    243 
    244 			// copy line to history buffer
    245 			s_wcd.historyEditLines[s_wcd.nextHistoryLine % COMMAND_HISTORY] = s_wcd.consoleField;
    246 			s_wcd.nextHistoryLine++;
    247 			s_wcd.historyLine = s_wcd.nextHistoryLine;
    248 
    249 			s_wcd.consoleField.Clear();
    250 
    251 			return 0;
    252 		}
    253 
    254 		// command completion
    255 		if ( key == K_TAB ) {
    256 			s_wcd.consoleField.AutoComplete();
    257 
    258 			SetWindowText( s_wcd.hwndInputLine, s_wcd.consoleField.GetBuffer() );
    259 			//s_wcd.consoleField.SetWidthInChars( strlen( s_wcd.consoleField.GetBuffer() ) );
    260 			SendMessage( s_wcd.hwndInputLine, EM_SETSEL, s_wcd.consoleField.GetCursor(), s_wcd.consoleField.GetCursor() );
    261 
    262 			return 0;
    263 		}
    264 
    265 		// clear autocompletion buffer on normal key input
    266 		if ( ( key >= K_SPACE && key <= K_BACKSPACE ) || 
    267 			( key >= K_KP_SLASH && key <= K_KP_PLUS ) || ( key >= K_KP_STAR && key <= K_KP_EQUALS ) ) {
    268 			s_wcd.consoleField.ClearAutoComplete();
    269 		}
    270 		break;
    271 	}
    272 
    273 	return CallWindowProc( s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam );
    274 }
    275 
    276 /*
    277 ** Sys_CreateConsole
    278 */
    279 void Sys_CreateConsole() {
    280 	HDC hDC;
    281 	WNDCLASS wc;
    282 	RECT rect;
    283 	const char *DEDCLASS = WIN32_CONSOLE_CLASS;
    284 	int nHeight;
    285 	int swidth, sheight;
    286 	int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX;
    287 	int i;
    288 
    289 	memset( &wc, 0, sizeof( wc ) );
    290 
    291 	wc.style         = 0;
    292 	wc.lpfnWndProc   = (WNDPROC) ConWndProc;
    293 	wc.cbClsExtra    = 0;
    294 	wc.cbWndExtra    = 0;
    295 	wc.hInstance     = win32.hInstance;
    296 	wc.hIcon         = LoadIcon( win32.hInstance, MAKEINTRESOURCE(IDI_ICON1));
    297 	wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
    298 	wc.hbrBackground = (struct HBRUSH__ *)COLOR_WINDOW;
    299 	wc.lpszMenuName  = 0;
    300 	wc.lpszClassName = DEDCLASS;
    301 
    302 	if ( !RegisterClass (&wc) ) {
    303 		return;
    304 	}
    305 
    306 	rect.left = 0;
    307 	rect.right = 540;
    308 	rect.top = 0;
    309 	rect.bottom = 450;
    310 	AdjustWindowRect( &rect, DEDSTYLE, FALSE );
    311 
    312 	hDC = GetDC( GetDesktopWindow() );
    313 	swidth = GetDeviceCaps( hDC, HORZRES );
    314 	sheight = GetDeviceCaps( hDC, VERTRES );
    315 	ReleaseDC( GetDesktopWindow(), hDC );
    316 
    317 	s_wcd.windowWidth = rect.right - rect.left + 1;
    318 	s_wcd.windowHeight = rect.bottom - rect.top + 1;
    319 
    320 	//s_wcd.hbmLogo = LoadBitmap( win32.hInstance, MAKEINTRESOURCE( IDB_BITMAP_LOGO) );
    321 
    322 	s_wcd.hWnd = CreateWindowEx( 0,
    323 							   DEDCLASS,
    324 							   GAME_NAME,
    325 							   DEDSTYLE,
    326 							   ( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1,
    327 							   NULL,
    328 							   NULL,
    329 							   win32.hInstance,
    330 							   NULL );
    331 
    332 	if ( s_wcd.hWnd == NULL ) {
    333 		return;
    334 	}
    335 
    336 	//
    337 	// create fonts
    338 	//
    339 	hDC = GetDC( s_wcd.hWnd );
    340 	nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 );
    341 
    342 	s_wcd.hfBufferFont = CreateFont( nHeight, 0, 0, 0, FW_LIGHT, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_MODERN | FIXED_PITCH, "Courier New" );
    343 
    344 	ReleaseDC( s_wcd.hWnd, hDC );
    345 
    346 	//
    347 	// create the input line
    348 	//
    349 	s_wcd.hwndInputLine = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | 
    350 												ES_LEFT | ES_AUTOHSCROLL,
    351 												6, 400, 528, 20,
    352 												s_wcd.hWnd, 
    353 												( HMENU ) INPUT_ID,	// child window ID
    354 												win32.hInstance, NULL );
    355 
    356 	//
    357 	// create the buttons
    358 	//
    359 	s_wcd.hwndButtonCopy = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
    360 												5, 425, 72, 24,
    361 												s_wcd.hWnd, 
    362 												( HMENU ) COPY_ID,	// child window ID
    363 												win32.hInstance, NULL );
    364 	SendMessage( s_wcd.hwndButtonCopy, WM_SETTEXT, 0, ( LPARAM ) "copy" );
    365 
    366 	s_wcd.hwndButtonClear = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
    367 												82, 425, 72, 24,
    368 												s_wcd.hWnd, 
    369 												( HMENU ) CLEAR_ID,	// child window ID
    370 												win32.hInstance, NULL );
    371 	SendMessage( s_wcd.hwndButtonClear, WM_SETTEXT, 0, ( LPARAM ) "clear" );
    372 
    373 	s_wcd.hwndButtonQuit = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
    374 												462, 425, 72, 24,
    375 												s_wcd.hWnd, 
    376 												( HMENU ) QUIT_ID,	// child window ID
    377 												win32.hInstance, NULL );
    378 	SendMessage( s_wcd.hwndButtonQuit, WM_SETTEXT, 0, ( LPARAM ) "quit" );
    379 
    380 
    381 	//
    382 	// create the scrollbuffer
    383 	//
    384 	s_wcd.hwndBuffer = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | 
    385 												ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY,
    386 												6, 40, 526, 354,
    387 												s_wcd.hWnd, 
    388 												( HMENU ) EDIT_ID,	// child window ID
    389 												win32.hInstance, NULL );
    390 	SendMessage( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
    391 
    392 	s_wcd.SysInputLineWndProc = ( WNDPROC ) SetWindowLong( s_wcd.hwndInputLine, GWL_WNDPROC, ( long ) InputLineWndProc );
    393 	SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
    394 
    395 // don't show it now that we have a splash screen up
    396 	if ( win32.win_viewlog.GetBool() ) {
    397 		ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT);
    398 		UpdateWindow( s_wcd.hWnd );
    399 		SetForegroundWindow( s_wcd.hWnd );
    400 		SetFocus( s_wcd.hwndInputLine );
    401 	}
    402 
    403 
    404 
    405 	s_wcd.consoleField.Clear();
    406 
    407 	for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
    408 		s_wcd.historyEditLines[i].Clear();
    409 	}
    410 }
    411 
    412 /*
    413 ** Sys_DestroyConsole
    414 */
    415 void Sys_DestroyConsole() {
    416 	if ( s_wcd.hWnd ) {
    417 		ShowWindow( s_wcd.hWnd, SW_HIDE );
    418 		CloseWindow( s_wcd.hWnd );
    419 		DestroyWindow( s_wcd.hWnd );
    420 		s_wcd.hWnd = 0;
    421 	}
    422 }
    423 
    424 /*
    425 ** Sys_ShowConsole
    426 */
    427 void Sys_ShowConsole( int visLevel, bool quitOnClose ) {
    428 
    429 	s_wcd.quitOnClose = quitOnClose;
    430 
    431 	if ( !s_wcd.hWnd ) {
    432 		return;
    433 	}
    434 
    435 	switch ( visLevel ) {
    436 		case 0:
    437 			ShowWindow( s_wcd.hWnd, SW_HIDE );
    438 		break;
    439 		case 1:
    440 			ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL );
    441 			SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
    442 		break;
    443 		case 2:
    444 			ShowWindow( s_wcd.hWnd, SW_MINIMIZE );
    445 		break;
    446 		default:
    447 			Sys_Error( "Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel );
    448 		break;
    449 	}
    450 }
    451 
    452 /*
    453 ** Sys_ConsoleInput
    454 */
    455 char *Sys_ConsoleInput() {
    456 	
    457 	if ( s_wcd.consoleText[0] == 0 ) {
    458 		return NULL;
    459 	}
    460 		
    461 	strcpy( s_wcd.returnedText, s_wcd.consoleText );
    462 	s_wcd.consoleText[0] = 0;
    463 	
    464 	return s_wcd.returnedText;
    465 }
    466 
    467 /*
    468 ** Conbuf_AppendText
    469 */
    470 void Conbuf_AppendText( const char *pMsg )
    471 {
    472 #define CONSOLE_BUFFER_SIZE		16384
    473 
    474 	char buffer[CONSOLE_BUFFER_SIZE*2];
    475 	char *b = buffer;
    476 	const char *msg;
    477 	int bufLen;
    478 	int i = 0;
    479 	static unsigned long s_totalChars;
    480 
    481 	//
    482 	// if the message is REALLY long, use just the last portion of it
    483 	//
    484 	if ( strlen( pMsg ) > CONSOLE_BUFFER_SIZE - 1 )	{
    485 		msg = pMsg + strlen( pMsg ) - CONSOLE_BUFFER_SIZE + 1;
    486 	} else {
    487 		msg = pMsg;
    488 	}
    489 
    490 	//
    491 	// copy into an intermediate buffer
    492 	//
    493 	while ( msg[i] && ( ( b - buffer ) < sizeof( buffer ) - 1 ) ) {
    494 		if ( msg[i] == '\n' && msg[i+1] == '\r' ) {
    495 			b[0] = '\r';
    496 			b[1] = '\n';
    497 			b += 2;
    498 			i++;
    499 		} else if ( msg[i] == '\r' ) {
    500 			b[0] = '\r';
    501 			b[1] = '\n';
    502 			b += 2;
    503 		} else if ( msg[i] == '\n' ) {
    504 			b[0] = '\r';
    505 			b[1] = '\n';
    506 			b += 2;
    507 		} else if ( idStr::IsColor( &msg[i] ) ) {
    508 			i++;
    509 		} else {
    510 			*b= msg[i];
    511 			b++;
    512 		}
    513 		i++;
    514 	}
    515 	*b = 0;
    516 	bufLen = b - buffer;
    517 
    518 	s_totalChars += bufLen;
    519 
    520 	//
    521 	// replace selection instead of appending if we're overflowing
    522 	//
    523 	if ( s_totalChars > 0x7000 ) {
    524 		SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 );
    525 		s_totalChars = bufLen;
    526 	}
    527 
    528 	//
    529 	// put this text into the windows console
    530 	//
    531 	SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff );
    532 	SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 );
    533 	SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) buffer );
    534 }
    535 
    536 /*
    537 ** Win_SetErrorText
    538 */
    539 void Win_SetErrorText( const char *buf ) {
    540 	idStr::Copynz( s_wcd.errorString, buf, sizeof( s_wcd.errorString ) );
    541 	if ( !s_wcd.hwndErrorBox ) {
    542 		s_wcd.hwndErrorBox = CreateWindow( "static", NULL, WS_CHILD | WS_VISIBLE | SS_SUNKEN,
    543 													6, 5, 526, 30,
    544 													s_wcd.hWnd, 
    545 													( HMENU ) ERRORBOX_ID,	// child window ID
    546 													win32.hInstance, NULL );
    547 		SendMessage( s_wcd.hwndErrorBox, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 );
    548 		SetWindowText( s_wcd.hwndErrorBox, s_wcd.errorString );
    549 
    550 		DestroyWindow( s_wcd.hwndInputLine );
    551 		s_wcd.hwndInputLine = NULL;
    552 	}
    553 }