1 /* 2 Copyright (C) 1996-1997 Id Software, Inc. 3 4 This program is free software; you can redistribute it and/or 5 modify it under the terms of the GNU General Public License 6 as published by the Free Software Foundation; either version 2 7 of the License, or (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13 See the GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19 */ 20 // console.c 21 22 #include "quakedef.h" 23 24 int con_ormask; 25 console_t con_main; 26 console_t con_chat; 27 console_t *con; // point to either con_main or con_chat 28 29 int con_linewidth; // characters across screen 30 int con_totallines; // total lines in console scrollback 31 32 float con_cursorspeed = 4; 33 34 35 cvar_t con_notifytime = CVAR2("con_notifytime","3"); //seconds 36 37 #define NUM_CON_TIMES 4 38 float con_times[NUM_CON_TIMES]; // realtime time the line was generated 39 // for transparent notify lines 40 41 int con_vislines; 42 int con_notifylines; // scan lines to clear for notify lines 43 44 qboolean con_debuglog; 45 46 #define MAXCMDLINE 256 47 extern char key_lines[32][MAXCMDLINE]; 48 extern int edit_line; 49 extern int key_linepos; 50 51 52 qboolean con_initialized; 53 54 55 void Key_ClearTyping (void) 56 { 57 key_lines[edit_line][1] = 0; // clear any typing 58 key_linepos = 1; 59 } 60 61 /* 62 ================ 63 Con_ToggleConsole_f 64 ================ 65 */ 66 void Con_ToggleConsole_f (void) 67 { 68 Key_ClearTyping (); 69 70 if (key_dest == key_console) 71 { 72 if (cls.state == ca_active) 73 key_dest = key_game; 74 } 75 else 76 key_dest = key_console; 77 78 Con_ClearNotify (); 79 } 80 81 /* 82 ================ 83 Con_ToggleChat_f 84 ================ 85 */ 86 void Con_ToggleChat_f (void) 87 { 88 Key_ClearTyping (); 89 90 if (key_dest == key_console) 91 { 92 if (cls.state == ca_active) 93 key_dest = key_game; 94 } 95 else 96 key_dest = key_console; 97 98 Con_ClearNotify (); 99 } 100 101 /* 102 ================ 103 Con_Clear_f 104 ================ 105 */ 106 void Con_Clear_f (void) 107 { 108 Q_memset (con_main.text, ' ', CON_TEXTSIZE); 109 Q_memset (con_chat.text, ' ', CON_TEXTSIZE); 110 } 111 112 113 /* 114 ================ 115 Con_ClearNotify 116 ================ 117 */ 118 void Con_ClearNotify (void) 119 { 120 int i; 121 122 for (i=0 ; i<NUM_CON_TIMES ; i++) 123 con_times[i] = 0; 124 } 125 126 127 /* 128 ================ 129 Con_MessageMode_f 130 ================ 131 */ 132 void Con_MessageMode_f (void) 133 { 134 chat_team = false; 135 key_dest = key_message; 136 } 137 138 /* 139 ================ 140 Con_MessageMode2_f 141 ================ 142 */ 143 void Con_MessageMode2_f (void) 144 { 145 chat_team = true; 146 key_dest = key_message; 147 } 148 149 /* 150 ================ 151 Con_Resize 152 153 ================ 154 */ 155 void Con_Resize (console_t *con) 156 { 157 int i, j, width, oldwidth, oldtotallines, numlines, numchars; 158 char tbuf[CON_TEXTSIZE]; 159 160 width = (vid.width >> 3) - 2; 161 162 if (width == con_linewidth) 163 return; 164 165 if (width < 1) // video hasn't been initialized yet 166 { 167 width = 38; 168 con_linewidth = width; 169 con_totallines = CON_TEXTSIZE / con_linewidth; 170 Q_memset (con->text, ' ', CON_TEXTSIZE); 171 } 172 else 173 { 174 oldwidth = con_linewidth; 175 con_linewidth = width; 176 oldtotallines = con_totallines; 177 con_totallines = CON_TEXTSIZE / con_linewidth; 178 numlines = oldtotallines; 179 180 if (con_totallines < numlines) 181 numlines = con_totallines; 182 183 numchars = oldwidth; 184 185 if (con_linewidth < numchars) 186 numchars = con_linewidth; 187 188 Q_memcpy (tbuf, con->text, CON_TEXTSIZE); 189 Q_memset (con->text, ' ', CON_TEXTSIZE); 190 191 for (i=0 ; i<numlines ; i++) 192 { 193 for (j=0 ; j<numchars ; j++) 194 { 195 con->text[(con_totallines - 1 - i) * con_linewidth + j] = 196 tbuf[((con->current - i + oldtotallines) % 197 oldtotallines) * oldwidth + j]; 198 } 199 } 200 201 Con_ClearNotify (); 202 } 203 204 con->current = con_totallines - 1; 205 con->display = con->current; 206 } 207 208 209 /* 210 ================ 211 Con_CheckResize 212 213 If the line width has changed, reformat the buffer. 214 ================ 215 */ 216 void Con_CheckResize (void) 217 { 218 Con_Resize (&con_main); 219 Con_Resize (&con_chat); 220 } 221 222 223 /* 224 ================ 225 Con_Init 226 ================ 227 */ 228 void Con_Init (void) 229 { 230 con_debuglog = COM_CheckParm("-condebug"); 231 232 con = &con_main; 233 con_linewidth = -1; 234 Con_CheckResize (); 235 236 Con_Printf ("Console initialized.\n"); 237 238 // 239 // register our commands 240 // 241 Cvar_RegisterVariable (&con_notifytime); 242 243 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); 244 Cmd_AddCommand ("togglechat", Con_ToggleChat_f); 245 Cmd_AddCommand ("messagemode", Con_MessageMode_f); 246 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); 247 Cmd_AddCommand ("clear", Con_Clear_f); 248 con_initialized = true; 249 } 250 251 252 /* 253 =============== 254 Con_Linefeed 255 =============== 256 */ 257 void Con_Linefeed (void) 258 { 259 con->x = 0; 260 if (con->display == con->current) 261 con->display++; 262 con->current++; 263 Q_memset (&con->text[(con->current%con_totallines)*con_linewidth] 264 , ' ', con_linewidth); 265 } 266 267 /* 268 ================ 269 Con_Print 270 271 Handles cursor positioning, line wrapping, etc 272 All console printing must go through this in order to be logged to disk 273 If no console is visible, the notify window will pop up. 274 ================ 275 */ 276 void Con_Print (char *txt) 277 { 278 int y; 279 int c, l; 280 static int cr; 281 int mask; 282 283 if (txt[0] == 1 || txt[0] == 2) 284 { 285 mask = 128; // go to colored text 286 txt++; 287 } 288 else 289 mask = 0; 290 291 292 while ( (c = *txt) ) 293 { 294 // count word length 295 for (l=0 ; l< con_linewidth ; l++) 296 if ( txt[l] <= ' ') 297 break; 298 299 // word wrap 300 if (l != con_linewidth && (con->x + l > con_linewidth) ) 301 con->x = 0; 302 303 txt++; 304 305 if (cr) 306 { 307 con->current--; 308 cr = false; 309 } 310 311 312 if (!con->x) 313 { 314 Con_Linefeed (); 315 // mark time for transparent overlay 316 if (con->current >= 0) 317 con_times[con->current % NUM_CON_TIMES] = realtime; 318 } 319 320 switch (c) 321 { 322 case '\n': 323 con->x = 0; 324 break; 325 326 case '\r': 327 con->x = 0; 328 cr = 1; 329 break; 330 331 default: // display character and advance 332 y = con->current % con_totallines; 333 con->text[y*con_linewidth+con->x] = c | mask | con_ormask; 334 con->x++; 335 if (con->x >= con_linewidth) 336 con->x = 0; 337 break; 338 } 339 340 } 341 } 342 343 344 /* 345 ================ 346 Con_Printf 347 348 Handles cursor positioning, line wrapping, etc 349 ================ 350 */ 351 #define MAXPRINTMSG 4096 352 // FIXME: make a buffer size safe vsprintf? 353 void Con_Printf (char *fmt, ...) 354 { 355 va_list argptr; 356 char msg[MAXPRINTMSG]; 357 static qboolean inupdate; 358 359 va_start (argptr,fmt); 360 vsprintf (msg,fmt,argptr); 361 va_end (argptr); 362 363 // also echo to debugging console 364 Sys_Printf ("%s", msg); // also echo to debugging console 365 366 // log all messages to file 367 if (con_debuglog) 368 Sys_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg); 369 370 if (!con_initialized) 371 return; 372 373 // write it to the scrollable buffer 374 Con_Print (msg); 375 376 // update the screen immediately if the console is displayed 377 if (cls.state != ca_active) 378 { 379 // protect against infinite loop if something in SCR_UpdateScreen calls 380 // Con_Printd 381 if (!inupdate) 382 { 383 inupdate = true; 384 SCR_UpdateScreen (); 385 inupdate = false; 386 } 387 } 388 } 389 390 /* 391 ================ 392 Con_DPrintf 393 394 A Con_Printf that only shows up if the "developer" cvar is set 395 ================ 396 */ 397 void Con_DPrintf (char *fmt, ...) 398 { 399 va_list argptr; 400 char msg[MAXPRINTMSG]; 401 402 if (!developer.value) 403 return; // don't confuse non-developers with techie stuff... 404 405 va_start (argptr,fmt); 406 vsprintf (msg,fmt,argptr); 407 va_end (argptr); 408 409 Con_Printf ("%s", msg); 410 } 411 412 /* 413 ============================================================================== 414 415 DRAWING 416 417 ============================================================================== 418 */ 419 420 421 /* 422 ================ 423 Con_DrawInput 424 425 The input line scrolls horizontally if typing goes beyond the right edge 426 ================ 427 */ 428 void Con_DrawInput (void) 429 { 430 int y; 431 int i; 432 char *text; 433 434 if (key_dest != key_console && cls.state == ca_active) 435 return; // don't draw anything (allways draw if not active) 436 437 text = key_lines[edit_line]; 438 439 // add the cursor frame 440 text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1); 441 442 // fill out remainder with spaces 443 for (i=key_linepos+1 ; i< con_linewidth ; i++) 444 text[i] = ' '; 445 446 // prestep if horizontally scrolling 447 if (key_linepos >= con_linewidth) 448 text += 1 + key_linepos - con_linewidth; 449 450 // draw it 451 y = con_vislines-22; 452 453 for (i=0 ; i<con_linewidth ; i++) 454 Draw_Character ( (i+1)<<3, con_vislines - 22, text[i]); 455 456 // remove cursor 457 key_lines[edit_line][key_linepos] = 0; 458 } 459 460 461 /* 462 ================ 463 Con_DrawNotify 464 465 Draws the last few lines of output transparently over the game top 466 ================ 467 */ 468 void Con_DrawNotify (void) 469 { 470 int x, v; 471 char *text; 472 int i; 473 float time; 474 char *s; 475 int skip; 476 477 v = 0; 478 for (i= con->current-NUM_CON_TIMES+1 ; i<=con->current ; i++) 479 { 480 if (i < 0) 481 continue; 482 time = con_times[i % NUM_CON_TIMES]; 483 if (time == 0) 484 continue; 485 time = realtime - time; 486 if (time > con_notifytime.value) 487 continue; 488 text = con->text + (i % con_totallines)*con_linewidth; 489 490 clearnotify = 0; 491 scr_copytop = 1; 492 493 for (x = 0 ; x < con_linewidth ; x++) 494 Draw_Character ( (x+1)<<3, v, text[x]); 495 496 v += 8; 497 } 498 499 500 if (key_dest == key_message) 501 { 502 clearnotify = 0; 503 scr_copytop = 1; 504 505 if (chat_team) 506 { 507 Draw_String (8, v, "say_team:"); 508 skip = 11; 509 } 510 else 511 { 512 Draw_String (8, v, "say:"); 513 skip = 5; 514 } 515 516 s = chat_buffer; 517 if (chat_bufferlen > (int) ((vid.width>>3)-(skip+1))) 518 s += chat_bufferlen - ((vid.width>>3)-(skip+1)); 519 x = 0; 520 while(s[x]) 521 { 522 Draw_Character ( (x+skip)<<3, v, s[x]); 523 x++; 524 } 525 Draw_Character ( (x+skip)<<3, v, 10+((int)(realtime*con_cursorspeed)&1)); 526 v += 8; 527 } 528 529 if (v > con_notifylines) 530 con_notifylines = v; 531 } 532 533 /* 534 ================ 535 Con_DrawConsole 536 537 Draws the console with the solid background 538 ================ 539 */ 540 void Con_DrawConsole (int lines) 541 { 542 int i, j, x, y, n; 543 int rows; 544 char *text; 545 int row; 546 char dlbar[1024]; 547 548 if (lines <= 0) 549 return; 550 551 // draw the background 552 Draw_ConsoleBackground (lines); 553 554 // draw the text 555 con_vislines = lines; 556 557 // changed to line things up better 558 rows = (lines-22)>>3; // rows of text to draw 559 560 y = lines - 30; 561 562 // draw from the bottom up 563 if (con->display != con->current) 564 { 565 // draw arrows to show the buffer is backscrolled 566 for (x=0 ; x<con_linewidth ; x+=4) 567 Draw_Character ( (x+1)<<3, y, '^'); 568 569 y -= 8; 570 rows--; 571 } 572 573 row = con->display; 574 for (i=0 ; i<rows ; i++, y-=8, row--) 575 { 576 if (row < 0) 577 break; 578 if (con->current - row >= con_totallines) 579 break; // past scrollback wrap point 580 581 text = con->text + (row % con_totallines)*con_linewidth; 582 583 for (x=0 ; x<con_linewidth ; x++) 584 Draw_Character ( (x+1)<<3, y, text[x]); 585 } 586 587 // draw the download bar 588 // figure out width 589 if (cls.download) { 590 if ((text = strrchr(cls.downloadname, '/')) != NULL) 591 text++; 592 else 593 text = cls.downloadname; 594 595 x = con_linewidth - ((con_linewidth * 7) / 40); 596 y = x - strlen(text) - 8; 597 i = con_linewidth/3; 598 if ((int) strlen(text) > i) { 599 y = x - i - 11; 600 strncpy(dlbar, text, i); 601 dlbar[i] = 0; 602 strcat(dlbar, "..."); 603 } else 604 strcpy(dlbar, text); 605 strcat(dlbar, ": "); 606 i = strlen(dlbar); 607 dlbar[i++] = '\x80'; 608 // where's the dot go? 609 if (cls.downloadpercent == 0) 610 n = 0; 611 else 612 n = y * cls.downloadpercent / 100; 613 614 for (j = 0; j < y; j++) 615 if (j == n) 616 dlbar[i++] = '\x83'; 617 else 618 dlbar[i++] = '\x81'; 619 dlbar[i++] = '\x82'; 620 dlbar[i] = 0; 621 622 sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); 623 624 // draw it 625 y = con_vislines-22 + 8; 626 for (i = 0; i < (int) strlen(dlbar); i++) 627 Draw_Character ( (i+1)<<3, y, dlbar[i]); 628 } 629 630 631 // draw the input prompt, user text, and cursor if desired 632 Con_DrawInput (); 633 } 634 635 636 /* 637 ================== 638 Con_NotifyBox 639 ================== 640 */ 641 void Con_NotifyBox (char *text) 642 { 643 double t1, t2; 644 645 // during startup for sound / cd warnings 646 Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); 647 648 Con_Printf (text); 649 650 Con_Printf ("Press a key.\n"); 651 Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); 652 653 key_count = -2; // wait for a key down and up 654 key_dest = key_console; 655 656 do 657 { 658 t1 = Sys_DoubleTime (); 659 SCR_UpdateScreen (); 660 Sys_SendKeyEvents (); 661 t2 = Sys_DoubleTime (); 662 realtime += t2-t1; // make the cursor blink 663 } while (key_count < 0); 664 665 Con_Printf ("\n"); 666 key_dest = key_game; 667 realtime = 0; // put the cursor back to invisible 668 } 669 670 671 /* 672 ================== 673 Con_SafePrintf 674 675 Okay to call even when the screen can't be updated 676 ================== 677 */ 678 void Con_SafePrintf (char *fmt, ...) 679 { 680 va_list argptr; 681 char msg[1024]; 682 int temp; 683 684 va_start (argptr,fmt); 685 vsprintf (msg,fmt,argptr); 686 va_end (argptr); 687 688 temp = scr_disabled_for_loading; 689 scr_disabled_for_loading = true; 690 Con_Printf ("%s", msg); 691 scr_disabled_for_loading = temp; 692 } 693 694