Console.cpp (31914B)
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 #include "ConsoleHistory.h" 32 #include "../renderer/ResolutionScale.h" 33 #include "Common_local.h" 34 35 #define CON_TEXTSIZE 0x30000 36 #define NUM_CON_TIMES 4 37 #define CONSOLE_FIRSTREPEAT 200 38 #define CONSOLE_REPEAT 100 39 40 #define COMMAND_HISTORY 64 41 42 struct overlayText_t { 43 idStr text; 44 justify_t justify; 45 int time; 46 }; 47 48 // the console will query the cvar and command systems for 49 // command completion information 50 51 class idConsoleLocal : public idConsole { 52 public: 53 virtual void Init(); 54 virtual void Shutdown(); 55 virtual bool ProcessEvent( const sysEvent_t * event, bool forceAccept ); 56 virtual bool Active(); 57 virtual void ClearNotifyLines(); 58 virtual void Close(); 59 virtual void Print( const char *text ); 60 virtual void Draw( bool forceFullScreen ); 61 62 virtual void PrintOverlay( idOverlayHandle &handle, justify_t justify, const char *text, ... ); 63 64 virtual idDebugGraph * CreateGraph( int numItems ); 65 virtual void DestroyGraph( idDebugGraph * graph ); 66 67 void Dump( const char *toFile ); 68 void Clear(); 69 70 private: 71 void KeyDownEvent( int key ); 72 73 void Linefeed(); 74 75 void PageUp(); 76 void PageDown(); 77 void Top(); 78 void Bottom(); 79 80 void DrawInput(); 81 void DrawNotify(); 82 void DrawSolidConsole( float frac ); 83 84 void Scroll(); 85 void SetDisplayFraction( float frac ); 86 void UpdateDisplayFraction(); 87 88 void DrawTextLeftAlign( float x, float &y, const char *text, ... ); 89 void DrawTextRightAlign( float x, float &y, const char *text, ... ); 90 91 float DrawFPS( float y ); 92 float DrawMemoryUsage( float y ); 93 94 void DrawOverlayText( float & leftY, float & rightY, float & centerY ); 95 void DrawDebugGraphs(); 96 97 //============================ 98 99 // allow these constants to be adjusted for HMD 100 int LOCALSAFE_LEFT; 101 int LOCALSAFE_RIGHT; 102 int LOCALSAFE_TOP; 103 int LOCALSAFE_BOTTOM; 104 int LOCALSAFE_WIDTH; 105 int LOCALSAFE_HEIGHT; 106 int LINE_WIDTH; 107 int TOTAL_LINES; 108 109 bool keyCatching; 110 111 short text[CON_TEXTSIZE]; 112 int current; // line where next message will be printed 113 int x; // offset in current line for next print 114 int display; // bottom of console displays this line 115 int lastKeyEvent; // time of last key event for scroll delay 116 int nextKeyEvent; // keyboard repeat rate 117 118 float displayFrac; // approaches finalFrac at con_speed 119 float finalFrac; // 0.0 to 1.0 lines of console to display 120 int fracTime; // time of last displayFrac update 121 122 int vislines; // in scanlines 123 124 int times[NUM_CON_TIMES]; // cls.realtime time the line was generated 125 // for transparent notify lines 126 idVec4 color; 127 128 idEditField historyEditLines[COMMAND_HISTORY]; 129 130 int nextHistoryLine;// the last line in the history buffer, not masked 131 int historyLine; // the line being displayed from history buffer 132 // will be <= nextHistoryLine 133 134 idEditField consoleField; 135 136 idList< overlayText_t > overlayText; 137 idList< idDebugGraph *> debugGraphs; 138 139 static idCVar con_speed; 140 static idCVar con_notifyTime; 141 static idCVar con_noPrint; 142 }; 143 144 static idConsoleLocal localConsole; 145 idConsole * console = &localConsole; 146 147 idCVar idConsoleLocal::con_speed( "con_speed", "3", CVAR_SYSTEM, "speed at which the console moves up and down" ); 148 idCVar idConsoleLocal::con_notifyTime( "con_notifyTime", "3", CVAR_SYSTEM, "time messages are displayed onscreen when console is pulled up" ); 149 #ifdef DEBUG 150 idCVar idConsoleLocal::con_noPrint( "con_noPrint", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "print on the console but not onscreen when console is pulled up" ); 151 #else 152 idCVar idConsoleLocal::con_noPrint( "con_noPrint", "1", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "print on the console but not onscreen when console is pulled up" ); 153 #endif 154 155 /* 156 ============================================================================= 157 158 Misc stats 159 160 ============================================================================= 161 */ 162 163 /* 164 ================== 165 idConsoleLocal::DrawTextLeftAlign 166 ================== 167 */ 168 void idConsoleLocal::DrawTextLeftAlign( float x, float &y, const char *text, ... ) { 169 char string[MAX_STRING_CHARS]; 170 va_list argptr; 171 va_start( argptr, text ); 172 idStr::vsnPrintf( string, sizeof( string ), text, argptr ); 173 va_end( argptr ); 174 renderSystem->DrawSmallStringExt( x, y + 2, string, colorWhite, true ); 175 y += SMALLCHAR_HEIGHT + 4; 176 } 177 178 /* 179 ================== 180 idConsoleLocal::DrawTextRightAlign 181 ================== 182 */ 183 void idConsoleLocal::DrawTextRightAlign( float x, float &y, const char *text, ... ) { 184 char string[MAX_STRING_CHARS]; 185 va_list argptr; 186 va_start( argptr, text ); 187 int i = idStr::vsnPrintf( string, sizeof( string ), text, argptr ); 188 va_end( argptr ); 189 renderSystem->DrawSmallStringExt( x - i * SMALLCHAR_WIDTH, y + 2, string, colorWhite, true ); 190 y += SMALLCHAR_HEIGHT + 4; 191 } 192 193 194 195 196 /* 197 ================== 198 idConsoleLocal::DrawFPS 199 ================== 200 */ 201 #define FPS_FRAMES 6 202 float idConsoleLocal::DrawFPS( float y ) { 203 static int previousTimes[FPS_FRAMES]; 204 static int index; 205 static int previous; 206 207 // don't use serverTime, because that will be drifting to 208 // correct for internet lag changes, timescales, timedemos, etc 209 int t = Sys_Milliseconds(); 210 int frameTime = t - previous; 211 previous = t; 212 213 previousTimes[index % FPS_FRAMES] = frameTime; 214 index++; 215 if ( index > FPS_FRAMES ) { 216 // average multiple frames together to smooth changes out a bit 217 int total = 0; 218 for ( int i = 0 ; i < FPS_FRAMES ; i++ ) { 219 total += previousTimes[i]; 220 } 221 if ( !total ) { 222 total = 1; 223 } 224 int fps = 1000000 * FPS_FRAMES / total; 225 fps = ( fps + 500 ) / 1000; 226 227 const char * s = va( "%ifps", fps ); 228 int w = strlen( s ) * BIGCHAR_WIDTH; 229 230 renderSystem->DrawBigStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, s, colorWhite, true ); 231 } 232 233 y += BIGCHAR_HEIGHT + 4; 234 235 // print the resolution scale so we can tell when we are at reduced resolution 236 idStr resolutionText; 237 resolutionScale.GetConsoleText( resolutionText ); 238 int w = resolutionText.Length() * BIGCHAR_WIDTH; 239 renderSystem->DrawBigStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, resolutionText.c_str(), colorWhite, true ); 240 241 const int gameThreadTotalTime = commonLocal.GetGameThreadTotalTime(); 242 const int gameThreadGameTime = commonLocal.GetGameThreadGameTime(); 243 const int gameThreadRenderTime = commonLocal.GetGameThreadRenderTime(); 244 const int rendererBackEndTime = commonLocal.GetRendererBackEndMicroseconds(); 245 const int rendererShadowsTime = commonLocal.GetRendererShadowsMicroseconds(); 246 const int rendererGPUIdleTime = commonLocal.GetRendererIdleMicroseconds(); 247 const int rendererGPUTime = commonLocal.GetRendererGPUMicroseconds(); 248 const int maxTime = 16; 249 250 y += SMALLCHAR_HEIGHT + 4; 251 idStr timeStr; 252 timeStr.Format( "%sG+RF: %4d", gameThreadTotalTime > maxTime ? S_COLOR_RED : "", gameThreadTotalTime ); 253 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 254 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 255 y += SMALLCHAR_HEIGHT + 4; 256 257 timeStr.Format( "%sG: %4d", gameThreadGameTime > maxTime ? S_COLOR_RED : "", gameThreadGameTime ); 258 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 259 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 260 y += SMALLCHAR_HEIGHT + 4; 261 262 timeStr.Format( "%sRF: %4d", gameThreadRenderTime > maxTime ? S_COLOR_RED : "", gameThreadRenderTime ); 263 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 264 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 265 y += SMALLCHAR_HEIGHT + 4; 266 267 timeStr.Format( "%sRB: %4.1f", rendererBackEndTime > maxTime * 1000 ? S_COLOR_RED : "", rendererBackEndTime / 1000.0f ); 268 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 269 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 270 y += SMALLCHAR_HEIGHT + 4; 271 272 timeStr.Format( "%sSV: %4.1f", rendererShadowsTime > maxTime * 1000 ? S_COLOR_RED : "", rendererShadowsTime / 1000.0f ); 273 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 274 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 275 y += SMALLCHAR_HEIGHT + 4; 276 277 timeStr.Format( "%sIDLE: %4.1f", rendererGPUIdleTime > maxTime * 1000 ? S_COLOR_RED : "", rendererGPUIdleTime / 1000.0f ); 278 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 279 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 280 y += SMALLCHAR_HEIGHT + 4; 281 282 timeStr.Format( "%sGPU: %4.1f", rendererGPUTime > maxTime * 1000 ? S_COLOR_RED : "", rendererGPUTime / 1000.0f ); 283 w = timeStr.LengthWithoutColors() * SMALLCHAR_WIDTH; 284 renderSystem->DrawSmallStringExt( LOCALSAFE_RIGHT - w, idMath::Ftoi( y ) + 2, timeStr.c_str(), colorWhite, false ); 285 286 return y + BIGCHAR_HEIGHT + 4; 287 } 288 289 /* 290 ================== 291 idConsoleLocal::DrawMemoryUsage 292 ================== 293 */ 294 float idConsoleLocal::DrawMemoryUsage( float y ) { 295 return y; 296 } 297 298 //========================================================================= 299 300 /* 301 ============== 302 Con_Clear_f 303 ============== 304 */ 305 static void Con_Clear_f( const idCmdArgs &args ) { 306 localConsole.Clear(); 307 } 308 309 /* 310 ============== 311 Con_Dump_f 312 ============== 313 */ 314 static void Con_Dump_f( const idCmdArgs &args ) { 315 if ( args.Argc() != 2 ) { 316 common->Printf( "usage: conDump <filename>\n" ); 317 return; 318 } 319 320 idStr fileName = args.Argv(1); 321 fileName.DefaultFileExtension(".txt"); 322 323 common->Printf( "Dumped console text to %s.\n", fileName.c_str() ); 324 325 localConsole.Dump( fileName.c_str() ); 326 } 327 328 /* 329 ============== 330 idConsoleLocal::Init 331 ============== 332 */ 333 void idConsoleLocal::Init() { 334 int i; 335 336 keyCatching = false; 337 338 LOCALSAFE_LEFT = 32; 339 LOCALSAFE_RIGHT = 608; 340 LOCALSAFE_TOP = 24; 341 LOCALSAFE_BOTTOM = 456; 342 LOCALSAFE_WIDTH = LOCALSAFE_RIGHT - LOCALSAFE_LEFT; 343 LOCALSAFE_HEIGHT = LOCALSAFE_BOTTOM - LOCALSAFE_TOP; 344 345 LINE_WIDTH = ( ( LOCALSAFE_WIDTH / SMALLCHAR_WIDTH ) - 2 ); 346 TOTAL_LINES = (CON_TEXTSIZE / LINE_WIDTH); 347 348 lastKeyEvent = -1; 349 nextKeyEvent = CONSOLE_FIRSTREPEAT; 350 351 consoleField.Clear(); 352 consoleField.SetWidthInChars( LINE_WIDTH ); 353 354 for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) { 355 historyEditLines[i].Clear(); 356 historyEditLines[i].SetWidthInChars( LINE_WIDTH ); 357 } 358 359 cmdSystem->AddCommand( "clear", Con_Clear_f, CMD_FL_SYSTEM, "clears the console" ); 360 cmdSystem->AddCommand( "conDump", Con_Dump_f, CMD_FL_SYSTEM, "dumps the console text to a file" ); 361 } 362 363 /* 364 ============== 365 idConsoleLocal::Shutdown 366 ============== 367 */ 368 void idConsoleLocal::Shutdown() { 369 cmdSystem->RemoveCommand( "clear" ); 370 cmdSystem->RemoveCommand( "conDump" ); 371 372 debugGraphs.DeleteContents( true ); 373 } 374 375 /* 376 ================ 377 idConsoleLocal::Active 378 ================ 379 */ 380 bool idConsoleLocal::Active() { 381 return keyCatching; 382 } 383 384 /* 385 ================ 386 idConsoleLocal::ClearNotifyLines 387 ================ 388 */ 389 void idConsoleLocal::ClearNotifyLines() { 390 int i; 391 392 for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) { 393 times[i] = 0; 394 } 395 } 396 397 /* 398 ================ 399 idConsoleLocal::Close 400 ================ 401 */ 402 void idConsoleLocal::Close() { 403 keyCatching = false; 404 SetDisplayFraction( 0 ); 405 displayFrac = 0; // don't scroll to that point, go immediately 406 ClearNotifyLines(); 407 } 408 409 /* 410 ================ 411 idConsoleLocal::Clear 412 ================ 413 */ 414 void idConsoleLocal::Clear() { 415 int i; 416 417 for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) { 418 text[i] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' '; 419 } 420 421 Bottom(); // go to end 422 } 423 424 /* 425 ================ 426 idConsoleLocal::Dump 427 428 Save the console contents out to a file 429 ================ 430 */ 431 void idConsoleLocal::Dump( const char *fileName ) { 432 int l, x, i; 433 short * line; 434 idFile *f; 435 char * buffer = (char *)alloca( LINE_WIDTH + 3 ); 436 437 f = fileSystem->OpenFileWrite( fileName ); 438 if ( !f ) { 439 common->Warning( "couldn't open %s", fileName ); 440 return; 441 } 442 443 // skip empty lines 444 l = current - TOTAL_LINES + 1; 445 if ( l < 0 ) { 446 l = 0; 447 } 448 for ( ; l <= current ; l++ ) 449 { 450 line = text + ( l % TOTAL_LINES ) * LINE_WIDTH; 451 for ( x = 0; x < LINE_WIDTH; x++ ) 452 if ( ( line[x] & 0xff ) > ' ' ) 453 break; 454 if ( x != LINE_WIDTH ) 455 break; 456 } 457 458 // write the remaining lines 459 for ( ; l <= current; l++ ) { 460 line = text + ( l % TOTAL_LINES ) * LINE_WIDTH; 461 for( i = 0; i < LINE_WIDTH; i++ ) { 462 buffer[i] = line[i] & 0xff; 463 } 464 for ( x = LINE_WIDTH-1; x >= 0; x-- ) { 465 if ( buffer[x] <= ' ' ) { 466 buffer[x] = 0; 467 } else { 468 break; 469 } 470 } 471 buffer[x+1] = '\r'; 472 buffer[x+2] = '\n'; 473 buffer[x+3] = 0; 474 f->Write( buffer, strlen( buffer ) ); 475 } 476 477 fileSystem->CloseFile( f ); 478 } 479 480 /* 481 ================ 482 idConsoleLocal::PageUp 483 ================ 484 */ 485 void idConsoleLocal::PageUp() { 486 display -= 2; 487 if ( current - display >= TOTAL_LINES ) { 488 display = current - TOTAL_LINES + 1; 489 } 490 } 491 492 /* 493 ================ 494 idConsoleLocal::PageDown 495 ================ 496 */ 497 void idConsoleLocal::PageDown() { 498 display += 2; 499 if ( display > current ) { 500 display = current; 501 } 502 } 503 504 /* 505 ================ 506 idConsoleLocal::Top 507 ================ 508 */ 509 void idConsoleLocal::Top() { 510 display = 0; 511 } 512 513 /* 514 ================ 515 idConsoleLocal::Bottom 516 ================ 517 */ 518 void idConsoleLocal::Bottom() { 519 display = current; 520 } 521 522 523 /* 524 ============================================================================= 525 526 CONSOLE LINE EDITING 527 528 ============================================================================== 529 */ 530 531 /* 532 ==================== 533 KeyDownEvent 534 535 Handles history and console scrollback 536 ==================== 537 */ 538 void idConsoleLocal::KeyDownEvent( int key ) { 539 540 // Execute F key bindings 541 if ( key >= K_F1 && key <= K_F12 ) { 542 idKeyInput::ExecKeyBinding( key ); 543 return; 544 } 545 546 // ctrl-L clears screen 547 if ( key == K_L && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) { 548 Clear(); 549 return; 550 } 551 552 // enter finishes the line 553 if ( key == K_ENTER || key == K_KP_ENTER ) { 554 555 common->Printf ( "]%s\n", consoleField.GetBuffer() ); 556 557 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, consoleField.GetBuffer() ); // valid command 558 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "\n" ); 559 560 // copy line to history buffer 561 562 if ( consoleField.GetBuffer()[ 0 ] != '\n' && consoleField.GetBuffer()[ 0 ] != '\0' ) { 563 consoleHistory.AddToHistory( consoleField.GetBuffer() ); 564 } 565 566 consoleField.Clear(); 567 consoleField.SetWidthInChars( LINE_WIDTH ); 568 569 const bool captureToImage = false; 570 common->UpdateScreen( captureToImage );// force an update, because the command 571 // may take some time 572 return; 573 } 574 575 // command completion 576 577 if ( key == K_TAB ) { 578 consoleField.AutoComplete(); 579 return; 580 } 581 582 // command history (ctrl-p ctrl-n for unix style) 583 584 if ( ( key == K_UPARROW ) || 585 ( key == K_P && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) ) { 586 idStr hist = consoleHistory.RetrieveFromHistory( true ); 587 if ( !hist.IsEmpty() ) { 588 consoleField.SetBuffer( hist ); 589 } 590 return; 591 } 592 593 if ( ( key == K_DOWNARROW ) || 594 ( key == K_N && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) ) { 595 idStr hist = consoleHistory.RetrieveFromHistory( false ); 596 if ( !hist.IsEmpty() ) { 597 consoleField.SetBuffer( hist ); 598 } 599 return; 600 } 601 602 // console scrolling 603 if ( key == K_PGUP ) { 604 PageUp(); 605 lastKeyEvent = eventLoop->Milliseconds(); 606 nextKeyEvent = CONSOLE_FIRSTREPEAT; 607 return; 608 } 609 610 if ( key == K_PGDN ) { 611 PageDown(); 612 lastKeyEvent = eventLoop->Milliseconds(); 613 nextKeyEvent = CONSOLE_FIRSTREPEAT; 614 return; 615 } 616 617 if ( key == K_MWHEELUP ) { 618 PageUp(); 619 return; 620 } 621 622 if ( key == K_MWHEELDOWN ) { 623 PageDown(); 624 return; 625 } 626 627 // ctrl-home = top of console 628 if ( key == K_HOME && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) { 629 Top(); 630 return; 631 } 632 633 // ctrl-end = bottom of console 634 if ( key == K_END && ( idKeyInput::IsDown( K_LCTRL ) || idKeyInput::IsDown( K_RCTRL ) ) ) { 635 Bottom(); 636 return; 637 } 638 639 // pass to the normal editline routine 640 consoleField.KeyDownEvent( key ); 641 } 642 643 /* 644 ============== 645 Scroll 646 deals with scrolling text because we don't have key repeat 647 ============== 648 */ 649 void idConsoleLocal::Scroll( ) { 650 if (lastKeyEvent == -1 || (lastKeyEvent+200) > eventLoop->Milliseconds()) { 651 return; 652 } 653 // console scrolling 654 if ( idKeyInput::IsDown( K_PGUP ) ) { 655 PageUp(); 656 nextKeyEvent = CONSOLE_REPEAT; 657 return; 658 } 659 660 if ( idKeyInput::IsDown( K_PGDN ) ) { 661 PageDown(); 662 nextKeyEvent = CONSOLE_REPEAT; 663 return; 664 } 665 } 666 667 /* 668 ============== 669 SetDisplayFraction 670 671 Causes the console to start opening the desired amount. 672 ============== 673 */ 674 void idConsoleLocal::SetDisplayFraction( float frac ) { 675 finalFrac = frac; 676 fracTime = Sys_Milliseconds(); 677 } 678 679 /* 680 ============== 681 UpdateDisplayFraction 682 683 Scrolls the console up or down based on conspeed 684 ============== 685 */ 686 void idConsoleLocal::UpdateDisplayFraction() { 687 if ( con_speed.GetFloat() <= 0.1f ) { 688 fracTime = Sys_Milliseconds(); 689 displayFrac = finalFrac; 690 return; 691 } 692 693 // scroll towards the destination height 694 if ( finalFrac < displayFrac ) { 695 displayFrac -= con_speed.GetFloat() * ( Sys_Milliseconds() - fracTime ) * 0.001f; 696 if ( finalFrac > displayFrac ) { 697 displayFrac = finalFrac; 698 } 699 fracTime = Sys_Milliseconds(); 700 } else if ( finalFrac > displayFrac ) { 701 displayFrac += con_speed.GetFloat() * ( Sys_Milliseconds() - fracTime ) * 0.001f; 702 if ( finalFrac < displayFrac ) { 703 displayFrac = finalFrac; 704 } 705 fracTime = Sys_Milliseconds(); 706 } 707 } 708 709 /* 710 ============== 711 ProcessEvent 712 ============== 713 */ 714 bool idConsoleLocal::ProcessEvent( const sysEvent_t *event, bool forceAccept ) { 715 const bool consoleKey = event->evType == SE_KEY && event->evValue == K_GRAVE && com_allowConsole.GetBool(); 716 717 // we always catch the console key event 718 if ( !forceAccept && consoleKey ) { 719 // ignore up events 720 if ( event->evValue2 == 0 ) { 721 return true; 722 } 723 724 consoleField.ClearAutoComplete(); 725 726 // a down event will toggle the destination lines 727 if ( keyCatching ) { 728 Close(); 729 Sys_GrabMouseCursor( true ); 730 } else { 731 consoleField.Clear(); 732 keyCatching = true; 733 if ( idKeyInput::IsDown( K_LSHIFT ) || idKeyInput::IsDown( K_RSHIFT ) ) { 734 // if the shift key is down, don't open the console as much 735 SetDisplayFraction( 0.2f ); 736 } else { 737 SetDisplayFraction( 0.5f ); 738 } 739 } 740 return true; 741 } 742 743 // if we aren't key catching, dump all the other events 744 if ( !forceAccept && !keyCatching ) { 745 return false; 746 } 747 748 // handle key and character events 749 if ( event->evType == SE_CHAR ) { 750 // never send the console key as a character 751 if ( event->evValue != '`' && event->evValue != '~' ) { 752 consoleField.CharEvent( event->evValue ); 753 } 754 return true; 755 } 756 757 if ( event->evType == SE_KEY ) { 758 // ignore up key events 759 if ( event->evValue2 == 0 ) { 760 return true; 761 } 762 763 KeyDownEvent( event->evValue ); 764 return true; 765 } 766 767 // we don't handle things like mouse, joystick, and network packets 768 return false; 769 } 770 771 /* 772 ============================================================================== 773 774 PRINTING 775 776 ============================================================================== 777 */ 778 779 /* 780 =============== 781 Linefeed 782 =============== 783 */ 784 void idConsoleLocal::Linefeed() { 785 int i; 786 787 // mark time for transparent overlay 788 if ( current >= 0 ) { 789 times[current % NUM_CON_TIMES] = Sys_Milliseconds(); 790 } 791 792 x = 0; 793 if ( display == current ) { 794 display++; 795 } 796 current++; 797 for ( i = 0; i < LINE_WIDTH; i++ ) { 798 int offset = ( (unsigned int)current % TOTAL_LINES ) * LINE_WIDTH + i; 799 text[offset] = (idStr::ColorIndex(C_COLOR_CYAN)<<8) | ' '; 800 } 801 } 802 803 804 /* 805 ================ 806 Print 807 808 Handles cursor positioning, line wrapping, etc 809 ================ 810 */ 811 void idConsoleLocal::Print( const char *txt ) { 812 int y; 813 int c, l; 814 int color; 815 816 if ( TOTAL_LINES == 0 ) { 817 // not yet initialized 818 return; 819 } 820 821 color = idStr::ColorIndex( C_COLOR_CYAN ); 822 823 while ( (c = *(const unsigned char*)txt) != 0 ) { 824 if ( idStr::IsColor( txt ) ) { 825 if ( *(txt+1) == C_COLOR_DEFAULT ) { 826 color = idStr::ColorIndex( C_COLOR_CYAN ); 827 } else { 828 color = idStr::ColorIndex( *(txt+1) ); 829 } 830 txt += 2; 831 continue; 832 } 833 834 y = current % TOTAL_LINES; 835 836 // if we are about to print a new word, check to see 837 // if we should wrap to the new line 838 if ( c > ' ' && ( x == 0 || text[y*LINE_WIDTH+x-1] <= ' ' ) ) { 839 // count word length 840 for (l=0 ; l< LINE_WIDTH ; l++) { 841 if ( txt[l] <= ' ') { 842 break; 843 } 844 } 845 846 // word wrap 847 if (l != LINE_WIDTH && (x + l >= LINE_WIDTH) ) { 848 Linefeed(); 849 } 850 } 851 852 txt++; 853 854 switch( c ) { 855 case '\n': 856 Linefeed (); 857 break; 858 case '\t': 859 do { 860 text[y*LINE_WIDTH+x] = (color << 8) | ' '; 861 x++; 862 if ( x >= LINE_WIDTH ) { 863 Linefeed(); 864 x = 0; 865 } 866 } while ( x & 3 ); 867 break; 868 case '\r': 869 x = 0; 870 break; 871 default: // display character and advance 872 text[y*LINE_WIDTH+x] = (color << 8) | c; 873 x++; 874 if ( x >= LINE_WIDTH ) { 875 Linefeed(); 876 x = 0; 877 } 878 break; 879 } 880 } 881 882 883 // mark time for transparent overlay 884 if ( current >= 0 ) { 885 times[current % NUM_CON_TIMES] = Sys_Milliseconds(); 886 } 887 } 888 889 890 /* 891 ============================================================================== 892 893 DRAWING 894 895 ============================================================================== 896 */ 897 898 899 /* 900 ================ 901 DrawInput 902 903 Draw the editline after a ] prompt 904 ================ 905 */ 906 void idConsoleLocal::DrawInput() { 907 int y, autoCompleteLength; 908 909 y = vislines - ( SMALLCHAR_HEIGHT * 2 ); 910 911 if ( consoleField.GetAutoCompleteLength() != 0 ) { 912 autoCompleteLength = strlen( consoleField.GetBuffer() ) - consoleField.GetAutoCompleteLength(); 913 914 if ( autoCompleteLength > 0 ) { 915 renderSystem->DrawFilled( idVec4( 0.8f, 0.2f, 0.2f, 0.45f ), 916 LOCALSAFE_LEFT + 2 * SMALLCHAR_WIDTH + consoleField.GetAutoCompleteLength() * SMALLCHAR_WIDTH, 917 y + 2, autoCompleteLength * SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT - 2 ); 918 } 919 } 920 921 renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) ); 922 923 renderSystem->DrawSmallChar( LOCALSAFE_LEFT + 1 * SMALLCHAR_WIDTH, y, ']' ); 924 925 consoleField.Draw( LOCALSAFE_LEFT + 2 * SMALLCHAR_WIDTH, y, SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, true ); 926 } 927 928 929 /* 930 ================ 931 DrawNotify 932 933 Draws the last few lines of output transparently over the game top 934 ================ 935 */ 936 void idConsoleLocal::DrawNotify() { 937 int x, v; 938 short *text_p; 939 int i; 940 int time; 941 int currentColor; 942 943 if ( con_noPrint.GetBool() ) { 944 return; 945 } 946 947 currentColor = idStr::ColorIndex( C_COLOR_WHITE ); 948 renderSystem->SetColor( idStr::ColorForIndex( currentColor ) ); 949 950 v = 0; 951 for ( i = current-NUM_CON_TIMES+1; i <= current; i++ ) { 952 if ( i < 0 ) { 953 continue; 954 } 955 time = times[i % NUM_CON_TIMES]; 956 if ( time == 0 ) { 957 continue; 958 } 959 time = Sys_Milliseconds() - time; 960 if ( time > con_notifyTime.GetFloat() * 1000 ) { 961 continue; 962 } 963 text_p = text + (i % TOTAL_LINES)*LINE_WIDTH; 964 965 for ( x = 0; x < LINE_WIDTH; x++ ) { 966 if ( ( text_p[x] & 0xff ) == ' ' ) { 967 continue; 968 } 969 if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) { 970 currentColor = idStr::ColorIndex(text_p[x]>>8); 971 renderSystem->SetColor( idStr::ColorForIndex( currentColor ) ); 972 } 973 renderSystem->DrawSmallChar( LOCALSAFE_LEFT + (x+1)*SMALLCHAR_WIDTH, v, text_p[x] & 0xff ); 974 } 975 976 v += SMALLCHAR_HEIGHT; 977 } 978 979 renderSystem->SetColor( colorCyan ); 980 } 981 982 /* 983 ================ 984 DrawSolidConsole 985 986 Draws the console with the solid background 987 ================ 988 */ 989 void idConsoleLocal::DrawSolidConsole( float frac ) { 990 int i, x; 991 float y; 992 int rows; 993 short *text_p; 994 int row; 995 int lines; 996 int currentColor; 997 998 lines = idMath::Ftoi( SCREEN_HEIGHT * frac ); 999 if ( lines <= 0 ) { 1000 return; 1001 } 1002 1003 if ( lines > SCREEN_HEIGHT ) { 1004 lines = SCREEN_HEIGHT; 1005 } 1006 1007 // draw the background 1008 y = frac * SCREEN_HEIGHT - 2; 1009 if ( y < 1.0f ) { 1010 y = 0.0f; 1011 } else { 1012 renderSystem->DrawFilled( idVec4( 0.0f, 0.0f, 0.0f, 0.75f ), 0, 0, SCREEN_WIDTH, y ); 1013 } 1014 1015 renderSystem->DrawFilled( colorCyan, 0, y, SCREEN_WIDTH, 2 ); 1016 1017 // draw the version number 1018 1019 renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) ); 1020 1021 idStr version = va( "%s.%i.%i", ENGINE_VERSION, BUILD_NUMBER, BUILD_NUMBER_MINOR ); 1022 i = version.Length(); 1023 1024 for ( x = 0; x < i; x++ ) { 1025 renderSystem->DrawSmallChar( LOCALSAFE_WIDTH - ( i - x ) * SMALLCHAR_WIDTH, 1026 (lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/4)), version[x] ); 1027 1028 } 1029 1030 1031 // draw the text 1032 vislines = lines; 1033 rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH; // rows of text to draw 1034 1035 y = lines - (SMALLCHAR_HEIGHT*3); 1036 1037 // draw from the bottom up 1038 if ( display != current ) { 1039 // draw arrows to show the buffer is backscrolled 1040 renderSystem->SetColor( idStr::ColorForIndex( C_COLOR_CYAN ) ); 1041 for ( x = 0; x < LINE_WIDTH; x += 4 ) { 1042 renderSystem->DrawSmallChar( LOCALSAFE_LEFT + (x+1)*SMALLCHAR_WIDTH, idMath::Ftoi( y ), '^' ); 1043 } 1044 y -= SMALLCHAR_HEIGHT; 1045 rows--; 1046 } 1047 1048 row = display; 1049 1050 if ( x == 0 ) { 1051 row--; 1052 } 1053 1054 currentColor = idStr::ColorIndex( C_COLOR_WHITE ); 1055 renderSystem->SetColor( idStr::ColorForIndex( currentColor ) ); 1056 1057 for ( i = 0; i < rows; i++, y -= SMALLCHAR_HEIGHT, row-- ) { 1058 if ( row < 0 ) { 1059 break; 1060 } 1061 if ( current - row >= TOTAL_LINES ) { 1062 // past scrollback wrap point 1063 continue; 1064 } 1065 1066 text_p = text + (row % TOTAL_LINES)*LINE_WIDTH; 1067 1068 for ( x = 0; x < LINE_WIDTH; x++ ) { 1069 if ( ( text_p[x] & 0xff ) == ' ' ) { 1070 continue; 1071 } 1072 1073 if ( idStr::ColorIndex(text_p[x]>>8) != currentColor ) { 1074 currentColor = idStr::ColorIndex(text_p[x]>>8); 1075 renderSystem->SetColor( idStr::ColorForIndex( currentColor ) ); 1076 } 1077 renderSystem->DrawSmallChar( LOCALSAFE_LEFT + (x+1)*SMALLCHAR_WIDTH, idMath::Ftoi( y ), text_p[x] & 0xff ); 1078 } 1079 } 1080 1081 // draw the input prompt, user text, and cursor if desired 1082 DrawInput(); 1083 1084 renderSystem->SetColor( colorCyan ); 1085 } 1086 1087 1088 /* 1089 ============== 1090 Draw 1091 1092 ForceFullScreen is used by the editor 1093 ============== 1094 */ 1095 void idConsoleLocal::Draw( bool forceFullScreen ) { 1096 if ( forceFullScreen ) { 1097 // if we are forced full screen because of a disconnect, 1098 // we want the console closed when we go back to a session state 1099 Close(); 1100 // we are however catching keyboard input 1101 keyCatching = true; 1102 } 1103 1104 Scroll(); 1105 1106 UpdateDisplayFraction(); 1107 1108 if ( forceFullScreen ) { 1109 DrawSolidConsole( 1.0f ); 1110 } else if ( displayFrac ) { 1111 DrawSolidConsole( displayFrac ); 1112 } else { 1113 // only draw the notify lines if the developer cvar is set, 1114 // or we are a debug build 1115 if ( !con_noPrint.GetBool() ) { 1116 DrawNotify(); 1117 } 1118 } 1119 1120 float lefty = LOCALSAFE_TOP; 1121 float righty = LOCALSAFE_TOP; 1122 float centery = LOCALSAFE_TOP; 1123 if ( com_showFPS.GetBool() ) { 1124 righty = DrawFPS( righty ); 1125 } 1126 if ( com_showMemoryUsage.GetBool() ) { 1127 righty = DrawMemoryUsage( righty ); 1128 } 1129 DrawOverlayText( lefty, righty, centery ); 1130 DrawDebugGraphs(); 1131 } 1132 1133 /* 1134 ======================== 1135 idConsoleLocal::PrintOverlay 1136 ======================== 1137 */ 1138 void idConsoleLocal::PrintOverlay( idOverlayHandle &handle, justify_t justify, const char *text, ... ) { 1139 if ( handle.index >= 0 && handle.index < overlayText.Num() ) { 1140 if ( overlayText[handle.index].time == handle.time ) { 1141 return; 1142 } 1143 } 1144 1145 char string[MAX_PRINT_MSG]; 1146 va_list argptr; 1147 va_start( argptr, text ); 1148 idStr::vsnPrintf( string, sizeof( string ), text, argptr ); 1149 va_end( argptr ); 1150 1151 overlayText_t &overlay = overlayText.Alloc(); 1152 overlay.text = string; 1153 overlay.justify = justify; 1154 overlay.time = Sys_Milliseconds(); 1155 1156 handle.index = overlayText.Num() - 1; 1157 handle.time = overlay.time; 1158 } 1159 1160 /* 1161 ======================== 1162 idConsoleLocal::DrawOverlayText 1163 ======================== 1164 */ 1165 void idConsoleLocal::DrawOverlayText( float & leftY, float & rightY, float & centerY ) { 1166 for ( int i = 0; i < overlayText.Num(); i++ ) { 1167 const idStr & text = overlayText[i].text; 1168 1169 int maxWidth = 0; 1170 int numLines = 0; 1171 for ( int j = 0; j < text.Length(); j++ ) { 1172 int width = 1; 1173 for (; j < text.Length() && text[j] != '\n'; j++ ) { 1174 width++; 1175 } 1176 numLines++; 1177 if ( width > maxWidth ) { 1178 maxWidth = width; 1179 } 1180 } 1181 1182 idVec4 bgColor( 0.0f, 0.0f, 0.0f, 0.75f ); 1183 1184 const float width = maxWidth * SMALLCHAR_WIDTH; 1185 const float height = numLines * ( SMALLCHAR_HEIGHT + 4 ); 1186 const float bgAdjust = - 0.5f * SMALLCHAR_WIDTH; 1187 if ( overlayText[i].justify == JUSTIFY_LEFT ) { 1188 renderSystem->DrawFilled( bgColor, LOCALSAFE_LEFT + bgAdjust, leftY, width, height ); 1189 } else if ( overlayText[i].justify == JUSTIFY_RIGHT ) { 1190 renderSystem->DrawFilled( bgColor, LOCALSAFE_RIGHT - width + bgAdjust, rightY, width, height ); 1191 } else if ( overlayText[i].justify == JUSTIFY_CENTER_LEFT || overlayText[i].justify == JUSTIFY_CENTER_RIGHT ) { 1192 renderSystem->DrawFilled( bgColor, LOCALSAFE_LEFT + ( LOCALSAFE_WIDTH - width + bgAdjust ) * 0.5f, centerY, width, height ); 1193 } else { 1194 assert( false ); 1195 } 1196 1197 idStr singleLine; 1198 for ( int j = 0; j < text.Length(); j += singleLine.Length() + 1 ) { 1199 singleLine = ""; 1200 for ( int k = j; k < text.Length() && text[k] != '\n'; k++ ) { 1201 singleLine.Append( text[k] ); 1202 } 1203 if ( overlayText[i].justify == JUSTIFY_LEFT ) { 1204 DrawTextLeftAlign( LOCALSAFE_LEFT, leftY, "%s", singleLine.c_str() ); 1205 } else if ( overlayText[i].justify == JUSTIFY_RIGHT ) { 1206 DrawTextRightAlign( LOCALSAFE_RIGHT, rightY, "%s", singleLine.c_str() ); 1207 } else if ( overlayText[i].justify == JUSTIFY_CENTER_LEFT ) { 1208 DrawTextLeftAlign( LOCALSAFE_LEFT + ( LOCALSAFE_WIDTH - width ) * 0.5f, centerY, "%s", singleLine.c_str() ); 1209 } else if ( overlayText[i].justify == JUSTIFY_CENTER_RIGHT ) { 1210 DrawTextRightAlign( LOCALSAFE_LEFT + ( LOCALSAFE_WIDTH + width ) * 0.5f, centerY, "%s", singleLine.c_str() ); 1211 } else { 1212 assert( false ); 1213 } 1214 } 1215 } 1216 overlayText.SetNum( 0 ); 1217 } 1218 1219 /* 1220 ======================== 1221 idConsoleLocal::CreateGraph 1222 ======================== 1223 */ 1224 idDebugGraph * idConsoleLocal::CreateGraph( int numItems ) { 1225 idDebugGraph * graph = new (TAG_SYSTEM) idDebugGraph( numItems ); 1226 debugGraphs.Append( graph ); 1227 return graph; 1228 } 1229 1230 /* 1231 ======================== 1232 idConsoleLocal::DestroyGraph 1233 ======================== 1234 */ 1235 void idConsoleLocal::DestroyGraph( idDebugGraph * graph ) { 1236 debugGraphs.Remove( graph ); 1237 delete graph; 1238 } 1239 1240 /* 1241 ======================== 1242 idConsoleLocal::DrawDebugGraphs 1243 ======================== 1244 */ 1245 void idConsoleLocal::DrawDebugGraphs() { 1246 for ( int i = 0; i < debugGraphs.Num(); i++ ) { 1247 debugGraphs[i]->Render( renderSystem ); 1248 } 1249 }