1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// console.c 21 22#include "quakedef.h" 23 24int con_ormask; 25console_t con_main; 26console_t con_chat; 27console_t *con; // point to either con_main or con_chat 28 29int con_linewidth; // characters across screen 30int con_totallines; // total lines in console scrollback 31 32float con_cursorspeed = 4; 33 34 35cvar_t con_notifytime = CVAR2("con_notifytime","3"); //seconds 36 37#define NUM_CON_TIMES 4 38float con_times[NUM_CON_TIMES]; // realtime time the line was generated 39 // for transparent notify lines 40 41int con_vislines; 42int con_notifylines; // scan lines to clear for notify lines 43 44qboolean con_debuglog; 45 46#define MAXCMDLINE 256 47extern char key_lines[32][MAXCMDLINE]; 48extern int edit_line; 49extern int key_linepos; 50 51 52qboolean con_initialized; 53 54 55void Key_ClearTyping (void) 56{ 57 key_lines[edit_line][1] = 0; // clear any typing 58 key_linepos = 1; 59} 60 61/* 62================ 63Con_ToggleConsole_f 64================ 65*/ 66void 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================ 83Con_ToggleChat_f 84================ 85*/ 86void 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================ 103Con_Clear_f 104================ 105*/ 106void 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================ 115Con_ClearNotify 116================ 117*/ 118void 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================ 129Con_MessageMode_f 130================ 131*/ 132void Con_MessageMode_f (void) 133{ 134 chat_team = false; 135 key_dest = key_message; 136} 137 138/* 139================ 140Con_MessageMode2_f 141================ 142*/ 143void Con_MessageMode2_f (void) 144{ 145 chat_team = true; 146 key_dest = key_message; 147} 148 149/* 150================ 151Con_Resize 152 153================ 154*/ 155void 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================ 211Con_CheckResize 212 213If the line width has changed, reformat the buffer. 214================ 215*/ 216void Con_CheckResize (void) 217{ 218 Con_Resize (&con_main); 219 Con_Resize (&con_chat); 220} 221 222 223/* 224================ 225Con_Init 226================ 227*/ 228void 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=============== 254Con_Linefeed 255=============== 256*/ 257void 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================ 269Con_Print 270 271Handles cursor positioning, line wrapping, etc 272All console printing must go through this in order to be logged to disk 273If no console is visible, the notify window will pop up. 274================ 275*/ 276void 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================ 346Con_Printf 347 348Handles cursor positioning, line wrapping, etc 349================ 350*/ 351#define MAXPRINTMSG 4096 352// FIXME: make a buffer size safe vsprintf? 353void 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================ 392Con_DPrintf 393 394A Con_Printf that only shows up if the "developer" cvar is set 395================ 396*/ 397void 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 415DRAWING 416 417============================================================================== 418*/ 419 420 421/* 422================ 423Con_DrawInput 424 425The input line scrolls horizontally if typing goes beyond the right edge 426================ 427*/ 428void 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================ 463Con_DrawNotify 464 465Draws the last few lines of output transparently over the game top 466================ 467*/ 468void 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================ 535Con_DrawConsole 536 537Draws the console with the solid background 538================ 539*/ 540void 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================== 638Con_NotifyBox 639================== 640*/ 641void 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================== 673Con_SafePrintf 674 675Okay to call even when the screen can't be updated 676================== 677*/ 678void 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