win_syscon.c (13970B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 // win_syscon.h 23 #include "../client/client.h" 24 #include "win_local.h" 25 #include "resource.h" 26 #include <errno.h> 27 #include <float.h> 28 #include <fcntl.h> 29 #include <stdio.h> 30 #include <direct.h> 31 #include <io.h> 32 #include <conio.h> 33 34 #define COPY_ID 1 35 #define QUIT_ID 2 36 #define CLEAR_ID 3 37 38 #define ERRORBOX_ID 10 39 #define ERRORTEXT_ID 11 40 41 #define EDIT_ID 100 42 #define INPUT_ID 101 43 44 typedef struct 45 { 46 HWND hWnd; 47 HWND hwndBuffer; 48 49 HWND hwndButtonClear; 50 HWND hwndButtonCopy; 51 HWND hwndButtonQuit; 52 53 HWND hwndErrorBox; 54 HWND hwndErrorText; 55 56 HBITMAP hbmLogo; 57 HBITMAP hbmClearBitmap; 58 59 HBRUSH hbrEditBackground; 60 HBRUSH hbrErrorBackground; 61 62 HFONT hfBufferFont; 63 HFONT hfButtonFont; 64 65 HWND hwndInputLine; 66 67 char errorString[80]; 68 69 char consoleText[512], returnedText[512]; 70 int visLevel; 71 qboolean quitOnClose; 72 int windowWidth, windowHeight; 73 74 WNDPROC SysInputLineWndProc; 75 76 } WinConData; 77 78 static WinConData s_wcd; 79 80 static LONG WINAPI ConWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 81 { 82 char *cmdString; 83 static qboolean s_timePolarity; 84 85 switch (uMsg) 86 { 87 case WM_ACTIVATE: 88 if ( LOWORD( wParam ) != WA_INACTIVE ) 89 { 90 SetFocus( s_wcd.hwndInputLine ); 91 } 92 93 if ( com_viewlog && ( com_dedicated && !com_dedicated->integer ) ) 94 { 95 // if the viewlog is open, check to see if it's being minimized 96 if ( com_viewlog->integer == 1 ) 97 { 98 if ( HIWORD( wParam ) ) // minimized flag 99 { 100 Cvar_Set( "viewlog", "2" ); 101 } 102 } 103 else if ( com_viewlog->integer == 2 ) 104 { 105 if ( !HIWORD( wParam ) ) // minimized flag 106 { 107 Cvar_Set( "viewlog", "1" ); 108 } 109 } 110 } 111 break; 112 113 case WM_CLOSE: 114 if ( ( com_dedicated && com_dedicated->integer ) ) 115 { 116 cmdString = CopyString( "quit" ); 117 Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString ); 118 } 119 else if ( s_wcd.quitOnClose ) 120 { 121 PostQuitMessage( 0 ); 122 } 123 else 124 { 125 Sys_ShowConsole( 0, qfalse ); 126 Cvar_Set( "viewlog", "0" ); 127 } 128 return 0; 129 case WM_CTLCOLORSTATIC: 130 if ( ( HWND ) lParam == s_wcd.hwndBuffer ) 131 { 132 SetBkColor( ( HDC ) wParam, RGB( 0x00, 0x00, 0xB0 ) ); 133 SetTextColor( ( HDC ) wParam, RGB( 0xff, 0xff, 0x00 ) ); 134 135 #if 0 // this draws a background in the edit box, but there are issues with this 136 if ( ( hdcScaled = CreateCompatibleDC( ( HDC ) wParam ) ) != 0 ) 137 { 138 if ( SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo ) ) 139 { 140 StretchBlt( ( HDC ) wParam, 0, 0, 512, 384, 141 hdcScaled, 0, 0, 512, 384, 142 SRCCOPY ); 143 } 144 DeleteDC( hdcScaled ); 145 } 146 #endif 147 return ( long ) s_wcd.hbrEditBackground; 148 } 149 else if ( ( HWND ) lParam == s_wcd.hwndErrorBox ) 150 { 151 if ( s_timePolarity & 1 ) 152 { 153 SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) ); 154 SetTextColor( ( HDC ) wParam, RGB( 0xff, 0x0, 0x00 ) ); 155 } 156 else 157 { 158 SetBkColor( ( HDC ) wParam, RGB( 0x80, 0x80, 0x80 ) ); 159 SetTextColor( ( HDC ) wParam, RGB( 0x00, 0x0, 0x00 ) ); 160 } 161 return ( long ) s_wcd.hbrErrorBackground; 162 } 163 break; 164 165 case WM_COMMAND: 166 if ( wParam == COPY_ID ) 167 { 168 SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); 169 SendMessage( s_wcd.hwndBuffer, WM_COPY, 0, 0 ); 170 } 171 else if ( wParam == QUIT_ID ) 172 { 173 if ( s_wcd.quitOnClose ) 174 { 175 PostQuitMessage( 0 ); 176 } 177 else 178 { 179 cmdString = CopyString( "quit" ); 180 Sys_QueEvent( 0, SE_CONSOLE, 0, 0, strlen( cmdString ) + 1, cmdString ); 181 } 182 } 183 else if ( wParam == CLEAR_ID ) 184 { 185 SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); 186 SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, FALSE, ( LPARAM ) "" ); 187 UpdateWindow( s_wcd.hwndBuffer ); 188 } 189 break; 190 case WM_CREATE: 191 // s_wcd.hbmLogo = LoadBitmap( g_wv.hInstance, MAKEINTRESOURCE( IDB_BITMAP1 ) ); 192 // s_wcd.hbmClearBitmap = LoadBitmap( g_wv.hInstance, MAKEINTRESOURCE( IDB_BITMAP2 ) ); 193 s_wcd.hbrEditBackground = CreateSolidBrush( RGB( 0x00, 0x00, 0xB0 ) ); 194 s_wcd.hbrErrorBackground = CreateSolidBrush( RGB( 0x80, 0x80, 0x80 ) ); 195 SetTimer( hWnd, 1, 1000, NULL ); 196 break; 197 case WM_ERASEBKGND: 198 #if 0 199 HDC hdcScaled; 200 HGDIOBJ oldObject; 201 202 #if 1 // a single, large image 203 hdcScaled = CreateCompatibleDC( ( HDC ) wParam ); 204 assert( hdcScaled != 0 ); 205 206 if ( hdcScaled ) 207 { 208 oldObject = SelectObject( ( HDC ) hdcScaled, s_wcd.hbmLogo ); 209 assert( oldObject != 0 ); 210 if ( oldObject ) 211 { 212 StretchBlt( ( HDC ) wParam, 0, 0, s_wcd.windowWidth, s_wcd.windowHeight, 213 hdcScaled, 0, 0, 512, 384, 214 SRCCOPY ); 215 } 216 DeleteDC( hdcScaled ); 217 hdcScaled = 0; 218 } 219 #else // a repeating brush 220 { 221 HBRUSH hbrClearBrush; 222 RECT r; 223 224 GetWindowRect( hWnd, &r ); 225 226 r.bottom = r.bottom - r.top + 1; 227 r.right = r.right - r.left + 1; 228 r.top = 0; 229 r.left = 0; 230 231 hbrClearBrush = CreatePatternBrush( s_wcd.hbmClearBitmap ); 232 233 assert( hbrClearBrush != 0 ); 234 235 if ( hbrClearBrush ) 236 { 237 FillRect( ( HDC ) wParam, &r, hbrClearBrush ); 238 DeleteObject( hbrClearBrush ); 239 } 240 } 241 #endif 242 return 1; 243 #endif 244 return DefWindowProc( hWnd, uMsg, wParam, lParam ); 245 case WM_TIMER: 246 if ( wParam == 1 ) 247 { 248 s_timePolarity = !s_timePolarity; 249 if ( s_wcd.hwndErrorBox ) 250 { 251 InvalidateRect( s_wcd.hwndErrorBox, NULL, FALSE ); 252 } 253 } 254 break; 255 } 256 257 return DefWindowProc( hWnd, uMsg, wParam, lParam ); 258 } 259 260 LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 261 { 262 char inputBuffer[1024]; 263 264 switch ( uMsg ) 265 { 266 case WM_KILLFOCUS: 267 if ( ( HWND ) wParam == s_wcd.hWnd || 268 ( HWND ) wParam == s_wcd.hwndErrorBox ) 269 { 270 SetFocus( hWnd ); 271 return 0; 272 } 273 break; 274 275 case WM_CHAR: 276 if ( wParam == 13 ) 277 { 278 GetWindowText( s_wcd.hwndInputLine, inputBuffer, sizeof( inputBuffer ) ); 279 strncat( s_wcd.consoleText, inputBuffer, sizeof( s_wcd.consoleText ) - strlen( s_wcd.consoleText ) - 5 ); 280 strcat( s_wcd.consoleText, "\n" ); 281 SetWindowText( s_wcd.hwndInputLine, "" ); 282 283 Sys_Print( va( "]%s\n", inputBuffer ) ); 284 285 return 0; 286 } 287 } 288 289 return CallWindowProc( s_wcd.SysInputLineWndProc, hWnd, uMsg, wParam, lParam ); 290 } 291 292 /* 293 ** Sys_CreateConsole 294 */ 295 void Sys_CreateConsole( void ) 296 { 297 HDC hDC; 298 WNDCLASS wc; 299 RECT rect; 300 const char *DEDCLASS = "Q3 WinConsole"; 301 int nHeight; 302 int swidth, sheight; 303 int DEDSTYLE = WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX; 304 305 memset( &wc, 0, sizeof( wc ) ); 306 307 wc.style = 0; 308 wc.lpfnWndProc = (WNDPROC) ConWndProc; 309 wc.cbClsExtra = 0; 310 wc.cbWndExtra = 0; 311 wc.hInstance = g_wv.hInstance; 312 wc.hIcon = LoadIcon( g_wv.hInstance, MAKEINTRESOURCE(IDI_ICON1)); 313 wc.hCursor = LoadCursor (NULL,IDC_ARROW); 314 wc.hbrBackground = (void *)COLOR_WINDOW; 315 wc.lpszMenuName = 0; 316 wc.lpszClassName = DEDCLASS; 317 318 if ( !RegisterClass (&wc) ) 319 return; 320 321 rect.left = 0; 322 rect.right = 540; 323 rect.top = 0; 324 rect.bottom = 450; 325 AdjustWindowRect( &rect, DEDSTYLE, FALSE ); 326 327 hDC = GetDC( GetDesktopWindow() ); 328 swidth = GetDeviceCaps( hDC, HORZRES ); 329 sheight = GetDeviceCaps( hDC, VERTRES ); 330 ReleaseDC( GetDesktopWindow(), hDC ); 331 332 s_wcd.windowWidth = rect.right - rect.left + 1; 333 s_wcd.windowHeight = rect.bottom - rect.top + 1; 334 335 s_wcd.hWnd = CreateWindowEx( 0, 336 DEDCLASS, 337 "Quake 3 Console", 338 DEDSTYLE, 339 ( swidth - 600 ) / 2, ( sheight - 450 ) / 2 , rect.right - rect.left + 1, rect.bottom - rect.top + 1, 340 NULL, 341 NULL, 342 g_wv.hInstance, 343 NULL ); 344 345 if ( s_wcd.hWnd == NULL ) 346 { 347 return; 348 } 349 350 // 351 // create fonts 352 // 353 hDC = GetDC( s_wcd.hWnd ); 354 nHeight = -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY), 72); 355 356 s_wcd.hfBufferFont = CreateFont( nHeight, 357 0, 358 0, 359 0, 360 FW_LIGHT, 361 0, 362 0, 363 0, 364 DEFAULT_CHARSET, 365 OUT_DEFAULT_PRECIS, 366 CLIP_DEFAULT_PRECIS, 367 DEFAULT_QUALITY, 368 FF_MODERN | FIXED_PITCH, 369 "Courier New" ); 370 371 ReleaseDC( s_wcd.hWnd, hDC ); 372 373 // 374 // create the input line 375 // 376 s_wcd.hwndInputLine = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER | 377 ES_LEFT | ES_AUTOHSCROLL, 378 6, 400, 528, 20, 379 s_wcd.hWnd, 380 ( HMENU ) INPUT_ID, // child window ID 381 g_wv.hInstance, NULL ); 382 383 // 384 // create the buttons 385 // 386 s_wcd.hwndButtonCopy = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 387 5, 425, 72, 24, 388 s_wcd.hWnd, 389 ( HMENU ) COPY_ID, // child window ID 390 g_wv.hInstance, NULL ); 391 SendMessage( s_wcd.hwndButtonCopy, WM_SETTEXT, 0, ( LPARAM ) "copy" ); 392 393 s_wcd.hwndButtonClear = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 394 82, 425, 72, 24, 395 s_wcd.hWnd, 396 ( HMENU ) CLEAR_ID, // child window ID 397 g_wv.hInstance, NULL ); 398 SendMessage( s_wcd.hwndButtonClear, WM_SETTEXT, 0, ( LPARAM ) "clear" ); 399 400 s_wcd.hwndButtonQuit = CreateWindow( "button", NULL, BS_PUSHBUTTON | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 401 462, 425, 72, 24, 402 s_wcd.hWnd, 403 ( HMENU ) QUIT_ID, // child window ID 404 g_wv.hInstance, NULL ); 405 SendMessage( s_wcd.hwndButtonQuit, WM_SETTEXT, 0, ( LPARAM ) "quit" ); 406 407 408 // 409 // create the scrollbuffer 410 // 411 s_wcd.hwndBuffer = CreateWindow( "edit", NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_BORDER | 412 ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, 413 6, 40, 526, 354, 414 s_wcd.hWnd, 415 ( HMENU ) EDIT_ID, // child window ID 416 g_wv.hInstance, NULL ); 417 SendMessage( s_wcd.hwndBuffer, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); 418 419 s_wcd.SysInputLineWndProc = ( WNDPROC ) SetWindowLong( s_wcd.hwndInputLine, GWL_WNDPROC, ( long ) InputLineWndProc ); 420 SendMessage( s_wcd.hwndInputLine, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); 421 422 ShowWindow( s_wcd.hWnd, SW_SHOWDEFAULT); 423 UpdateWindow( s_wcd.hWnd ); 424 SetForegroundWindow( s_wcd.hWnd ); 425 SetFocus( s_wcd.hwndInputLine ); 426 427 s_wcd.visLevel = 1; 428 } 429 430 /* 431 ** Sys_DestroyConsole 432 */ 433 void Sys_DestroyConsole( void ) { 434 if ( s_wcd.hWnd ) { 435 ShowWindow( s_wcd.hWnd, SW_HIDE ); 436 CloseWindow( s_wcd.hWnd ); 437 DestroyWindow( s_wcd.hWnd ); 438 s_wcd.hWnd = 0; 439 } 440 } 441 442 /* 443 ** Sys_ShowConsole 444 */ 445 void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) 446 { 447 s_wcd.quitOnClose = quitOnClose; 448 449 if ( visLevel == s_wcd.visLevel ) 450 { 451 return; 452 } 453 454 s_wcd.visLevel = visLevel; 455 456 if ( !s_wcd.hWnd ) 457 return; 458 459 switch ( visLevel ) 460 { 461 case 0: 462 ShowWindow( s_wcd.hWnd, SW_HIDE ); 463 break; 464 case 1: 465 ShowWindow( s_wcd.hWnd, SW_SHOWNORMAL ); 466 SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff ); 467 break; 468 case 2: 469 ShowWindow( s_wcd.hWnd, SW_MINIMIZE ); 470 break; 471 default: 472 Sys_Error( "Invalid visLevel %d sent to Sys_ShowConsole\n", visLevel ); 473 break; 474 } 475 } 476 477 /* 478 ** Sys_ConsoleInput 479 */ 480 char *Sys_ConsoleInput( void ) 481 { 482 if ( s_wcd.consoleText[0] == 0 ) 483 { 484 return NULL; 485 } 486 487 strcpy( s_wcd.returnedText, s_wcd.consoleText ); 488 s_wcd.consoleText[0] = 0; 489 490 return s_wcd.returnedText; 491 } 492 493 /* 494 ** Conbuf_AppendText 495 */ 496 void Conbuf_AppendText( const char *pMsg ) 497 { 498 #define CONSOLE_BUFFER_SIZE 16384 499 500 char buffer[CONSOLE_BUFFER_SIZE*2]; 501 char *b = buffer; 502 const char *msg; 503 int bufLen; 504 int i = 0; 505 static unsigned long s_totalChars; 506 507 // 508 // if the message is REALLY long, use just the last portion of it 509 // 510 if ( strlen( pMsg ) > CONSOLE_BUFFER_SIZE - 1 ) 511 { 512 msg = pMsg + strlen( pMsg ) - CONSOLE_BUFFER_SIZE + 1; 513 } 514 else 515 { 516 msg = pMsg; 517 } 518 519 // 520 // copy into an intermediate buffer 521 // 522 while ( msg[i] && ( ( b - buffer ) < sizeof( buffer ) - 1 ) ) 523 { 524 if ( msg[i] == '\n' && msg[i+1] == '\r' ) 525 { 526 b[0] = '\r'; 527 b[1] = '\n'; 528 b += 2; 529 i++; 530 } 531 else if ( msg[i] == '\r' ) 532 { 533 b[0] = '\r'; 534 b[1] = '\n'; 535 b += 2; 536 } 537 else if ( msg[i] == '\n' ) 538 { 539 b[0] = '\r'; 540 b[1] = '\n'; 541 b += 2; 542 } 543 else if ( Q_IsColorString( &msg[i] ) ) 544 { 545 i++; 546 } 547 else 548 { 549 *b= msg[i]; 550 b++; 551 } 552 i++; 553 } 554 *b = 0; 555 bufLen = b - buffer; 556 557 s_totalChars += bufLen; 558 559 // 560 // replace selection instead of appending if we're overflowing 561 // 562 if ( s_totalChars > 0x7fff ) 563 { 564 SendMessage( s_wcd.hwndBuffer, EM_SETSEL, 0, -1 ); 565 s_totalChars = bufLen; 566 } 567 568 // 569 // put this text into the windows console 570 // 571 SendMessage( s_wcd.hwndBuffer, EM_LINESCROLL, 0, 0xffff ); 572 SendMessage( s_wcd.hwndBuffer, EM_SCROLLCARET, 0, 0 ); 573 SendMessage( s_wcd.hwndBuffer, EM_REPLACESEL, 0, (LPARAM) buffer ); 574 } 575 576 /* 577 ** Sys_SetErrorText 578 */ 579 void Sys_SetErrorText( const char *buf ) 580 { 581 Q_strncpyz( s_wcd.errorString, buf, sizeof( s_wcd.errorString ) ); 582 583 if ( !s_wcd.hwndErrorBox ) 584 { 585 s_wcd.hwndErrorBox = CreateWindow( "static", NULL, WS_CHILD | WS_VISIBLE | SS_SUNKEN, 586 6, 5, 526, 30, 587 s_wcd.hWnd, 588 ( HMENU ) ERRORBOX_ID, // child window ID 589 g_wv.hInstance, NULL ); 590 SendMessage( s_wcd.hwndErrorBox, WM_SETFONT, ( WPARAM ) s_wcd.hfBufferFont, 0 ); 591 SetWindowText( s_wcd.hwndErrorBox, s_wcd.errorString ); 592 593 DestroyWindow( s_wcd.hwndInputLine ); 594 s_wcd.hwndInputLine = NULL; 595 } 596 }