console.c (12143B)
1 /* 2 Copyright (C) 1997-2001 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 "client.h" 23 24 console_t con; 25 26 cvar_t *con_notifytime; 27 28 29 #define MAXCMDLINE 256 30 extern char key_lines[32][MAXCMDLINE]; 31 extern int edit_line; 32 extern int key_linepos; 33 34 35 void DrawString (int x, int y, char *s) 36 { 37 while (*s) 38 { 39 re.DrawChar (x, y, *s); 40 x+=8; 41 s++; 42 } 43 } 44 45 void DrawAltString (int x, int y, char *s) 46 { 47 while (*s) 48 { 49 re.DrawChar (x, y, *s ^ 0x80); 50 x+=8; 51 s++; 52 } 53 } 54 55 56 void Key_ClearTyping (void) 57 { 58 key_lines[edit_line][1] = 0; // clear any typing 59 key_linepos = 1; 60 } 61 62 /* 63 ================ 64 Con_ToggleConsole_f 65 ================ 66 */ 67 void Con_ToggleConsole_f (void) 68 { 69 SCR_EndLoadingPlaque (); // get rid of loading plaque 70 71 if (cl.attractloop) 72 { 73 Cbuf_AddText ("killserver\n"); 74 return; 75 } 76 77 if (cls.state == ca_disconnected) 78 { // start the demo loop again 79 Cbuf_AddText ("d1\n"); 80 return; 81 } 82 83 Key_ClearTyping (); 84 Con_ClearNotify (); 85 86 if (cls.key_dest == key_console) 87 { 88 M_ForceMenuOff (); 89 Cvar_Set ("paused", "0"); 90 } 91 else 92 { 93 M_ForceMenuOff (); 94 cls.key_dest = key_console; 95 96 if (Cvar_VariableValue ("maxclients") == 1 97 && Com_ServerState ()) 98 Cvar_Set ("paused", "1"); 99 } 100 } 101 102 /* 103 ================ 104 Con_ToggleChat_f 105 ================ 106 */ 107 void Con_ToggleChat_f (void) 108 { 109 Key_ClearTyping (); 110 111 if (cls.key_dest == key_console) 112 { 113 if (cls.state == ca_active) 114 { 115 M_ForceMenuOff (); 116 cls.key_dest = key_game; 117 } 118 } 119 else 120 cls.key_dest = key_console; 121 122 Con_ClearNotify (); 123 } 124 125 /* 126 ================ 127 Con_Clear_f 128 ================ 129 */ 130 void Con_Clear_f (void) 131 { 132 memset (con.text, ' ', CON_TEXTSIZE); 133 } 134 135 136 /* 137 ================ 138 Con_Dump_f 139 140 Save the console contents out to a file 141 ================ 142 */ 143 void Con_Dump_f (void) 144 { 145 int l, x; 146 char *line; 147 FILE *f; 148 char buffer[1024]; 149 char name[MAX_OSPATH]; 150 151 if (Cmd_Argc() != 2) 152 { 153 Com_Printf ("usage: condump <filename>\n"); 154 return; 155 } 156 157 Com_sprintf (name, sizeof(name), "%s/%s.txt", FS_Gamedir(), Cmd_Argv(1)); 158 159 Com_Printf ("Dumped console text to %s.\n", name); 160 FS_CreatePath (name); 161 f = fopen (name, "w"); 162 if (!f) 163 { 164 Com_Printf ("ERROR: couldn't open.\n"); 165 return; 166 } 167 168 // skip empty lines 169 for (l = con.current - con.totallines + 1 ; l <= con.current ; l++) 170 { 171 line = con.text + (l%con.totallines)*con.linewidth; 172 for (x=0 ; x<con.linewidth ; x++) 173 if (line[x] != ' ') 174 break; 175 if (x != con.linewidth) 176 break; 177 } 178 179 // write the remaining lines 180 buffer[con.linewidth] = 0; 181 for ( ; l <= con.current ; l++) 182 { 183 line = con.text + (l%con.totallines)*con.linewidth; 184 strncpy (buffer, line, con.linewidth); 185 for (x=con.linewidth-1 ; x>=0 ; x--) 186 { 187 if (buffer[x] == ' ') 188 buffer[x] = 0; 189 else 190 break; 191 } 192 for (x=0; buffer[x]; x++) 193 buffer[x] &= 0x7f; 194 195 fprintf (f, "%s\n", buffer); 196 } 197 198 fclose (f); 199 } 200 201 202 /* 203 ================ 204 Con_ClearNotify 205 ================ 206 */ 207 void Con_ClearNotify (void) 208 { 209 int i; 210 211 for (i=0 ; i<NUM_CON_TIMES ; i++) 212 con.times[i] = 0; 213 } 214 215 216 /* 217 ================ 218 Con_MessageMode_f 219 ================ 220 */ 221 void Con_MessageMode_f (void) 222 { 223 chat_team = false; 224 cls.key_dest = key_message; 225 } 226 227 /* 228 ================ 229 Con_MessageMode2_f 230 ================ 231 */ 232 void Con_MessageMode2_f (void) 233 { 234 chat_team = true; 235 cls.key_dest = key_message; 236 } 237 238 /* 239 ================ 240 Con_CheckResize 241 242 If the line width has changed, reformat the buffer. 243 ================ 244 */ 245 void Con_CheckResize (void) 246 { 247 int i, j, width, oldwidth, oldtotallines, numlines, numchars; 248 char tbuf[CON_TEXTSIZE]; 249 250 width = (viddef.width >> 3) - 2; 251 252 if (width == con.linewidth) 253 return; 254 255 if (width < 1) // video hasn't been initialized yet 256 { 257 width = 38; 258 con.linewidth = width; 259 con.totallines = CON_TEXTSIZE / con.linewidth; 260 memset (con.text, ' ', CON_TEXTSIZE); 261 } 262 else 263 { 264 oldwidth = con.linewidth; 265 con.linewidth = width; 266 oldtotallines = con.totallines; 267 con.totallines = CON_TEXTSIZE / con.linewidth; 268 numlines = oldtotallines; 269 270 if (con.totallines < numlines) 271 numlines = con.totallines; 272 273 numchars = oldwidth; 274 275 if (con.linewidth < numchars) 276 numchars = con.linewidth; 277 278 memcpy (tbuf, con.text, CON_TEXTSIZE); 279 memset (con.text, ' ', CON_TEXTSIZE); 280 281 for (i=0 ; i<numlines ; i++) 282 { 283 for (j=0 ; j<numchars ; j++) 284 { 285 con.text[(con.totallines - 1 - i) * con.linewidth + j] = 286 tbuf[((con.current - i + oldtotallines) % 287 oldtotallines) * oldwidth + j]; 288 } 289 } 290 291 Con_ClearNotify (); 292 } 293 294 con.current = con.totallines - 1; 295 con.display = con.current; 296 } 297 298 299 /* 300 ================ 301 Con_Init 302 ================ 303 */ 304 void Con_Init (void) 305 { 306 con.linewidth = -1; 307 308 Con_CheckResize (); 309 310 Com_Printf ("Console initialized.\n"); 311 312 // 313 // register our commands 314 // 315 con_notifytime = Cvar_Get ("con_notifytime", "3", 0); 316 317 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); 318 Cmd_AddCommand ("togglechat", Con_ToggleChat_f); 319 Cmd_AddCommand ("messagemode", Con_MessageMode_f); 320 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); 321 Cmd_AddCommand ("clear", Con_Clear_f); 322 Cmd_AddCommand ("condump", Con_Dump_f); 323 con.initialized = true; 324 } 325 326 327 /* 328 =============== 329 Con_Linefeed 330 =============== 331 */ 332 void Con_Linefeed (void) 333 { 334 con.x = 0; 335 if (con.display == con.current) 336 con.display++; 337 con.current++; 338 memset (&con.text[(con.current%con.totallines)*con.linewidth] 339 , ' ', con.linewidth); 340 } 341 342 /* 343 ================ 344 Con_Print 345 346 Handles cursor positioning, line wrapping, etc 347 All console printing must go through this in order to be logged to disk 348 If no console is visible, the text will appear at the top of the game window 349 ================ 350 */ 351 void Con_Print (char *txt) 352 { 353 int y; 354 int c, l; 355 static int cr; 356 int mask; 357 358 if (!con.initialized) 359 return; 360 361 if (txt[0] == 1 || txt[0] == 2) 362 { 363 mask = 128; // go to colored text 364 txt++; 365 } 366 else 367 mask = 0; 368 369 370 while ( (c = *txt) ) 371 { 372 // count word length 373 for (l=0 ; l< con.linewidth ; l++) 374 if ( txt[l] <= ' ') 375 break; 376 377 // word wrap 378 if (l != con.linewidth && (con.x + l > con.linewidth) ) 379 con.x = 0; 380 381 txt++; 382 383 if (cr) 384 { 385 con.current--; 386 cr = false; 387 } 388 389 390 if (!con.x) 391 { 392 Con_Linefeed (); 393 // mark time for transparent overlay 394 if (con.current >= 0) 395 con.times[con.current % NUM_CON_TIMES] = cls.realtime; 396 } 397 398 switch (c) 399 { 400 case '\n': 401 con.x = 0; 402 break; 403 404 case '\r': 405 con.x = 0; 406 cr = 1; 407 break; 408 409 default: // display character and advance 410 y = con.current % con.totallines; 411 con.text[y*con.linewidth+con.x] = c | mask | con.ormask; 412 con.x++; 413 if (con.x >= con.linewidth) 414 con.x = 0; 415 break; 416 } 417 418 } 419 } 420 421 422 /* 423 ============== 424 Con_CenteredPrint 425 ============== 426 */ 427 void Con_CenteredPrint (char *text) 428 { 429 int l; 430 char buffer[1024]; 431 432 l = strlen(text); 433 l = (con.linewidth-l)/2; 434 if (l < 0) 435 l = 0; 436 memset (buffer, ' ', l); 437 strcpy (buffer+l, text); 438 strcat (buffer, "\n"); 439 Con_Print (buffer); 440 } 441 442 /* 443 ============================================================================== 444 445 DRAWING 446 447 ============================================================================== 448 */ 449 450 451 /* 452 ================ 453 Con_DrawInput 454 455 The input line scrolls horizontally if typing goes beyond the right edge 456 ================ 457 */ 458 void Con_DrawInput (void) 459 { 460 int y; 461 int i; 462 char *text; 463 464 if (cls.key_dest == key_menu) 465 return; 466 if (cls.key_dest != key_console && cls.state == ca_active) 467 return; // don't draw anything (always draw if not active) 468 469 text = key_lines[edit_line]; 470 471 // add the cursor frame 472 text[key_linepos] = 10+((int)(cls.realtime>>8)&1); 473 474 // fill out remainder with spaces 475 for (i=key_linepos+1 ; i< con.linewidth ; i++) 476 text[i] = ' '; 477 478 // prestep if horizontally scrolling 479 if (key_linepos >= con.linewidth) 480 text += 1 + key_linepos - con.linewidth; 481 482 // draw it 483 y = con.vislines-16; 484 485 for (i=0 ; i<con.linewidth ; i++) 486 re.DrawChar ( (i+1)<<3, con.vislines - 22, text[i]); 487 488 // remove cursor 489 key_lines[edit_line][key_linepos] = 0; 490 } 491 492 493 /* 494 ================ 495 Con_DrawNotify 496 497 Draws the last few lines of output transparently over the game top 498 ================ 499 */ 500 void Con_DrawNotify (void) 501 { 502 int x, v; 503 char *text; 504 int i; 505 int time; 506 char *s; 507 int skip; 508 509 v = 0; 510 for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++) 511 { 512 if (i < 0) 513 continue; 514 time = con.times[i % NUM_CON_TIMES]; 515 if (time == 0) 516 continue; 517 time = cls.realtime - time; 518 if (time > con_notifytime->value*1000) 519 continue; 520 text = con.text + (i % con.totallines)*con.linewidth; 521 522 for (x = 0 ; x < con.linewidth ; x++) 523 re.DrawChar ( (x+1)<<3, v, text[x]); 524 525 v += 8; 526 } 527 528 529 if (cls.key_dest == key_message) 530 { 531 if (chat_team) 532 { 533 DrawString (8, v, "say_team:"); 534 skip = 11; 535 } 536 else 537 { 538 DrawString (8, v, "say:"); 539 skip = 5; 540 } 541 542 s = chat_buffer; 543 if (chat_bufferlen > (viddef.width>>3)-(skip+1)) 544 s += chat_bufferlen - ((viddef.width>>3)-(skip+1)); 545 x = 0; 546 while(s[x]) 547 { 548 re.DrawChar ( (x+skip)<<3, v, s[x]); 549 x++; 550 } 551 re.DrawChar ( (x+skip)<<3, v, 10+((cls.realtime>>8)&1)); 552 v += 8; 553 } 554 555 if (v) 556 { 557 SCR_AddDirtyPoint (0,0); 558 SCR_AddDirtyPoint (viddef.width-1, v); 559 } 560 } 561 562 /* 563 ================ 564 Con_DrawConsole 565 566 Draws the console with the solid background 567 ================ 568 */ 569 void Con_DrawConsole (float frac) 570 { 571 int i, j, x, y, n; 572 int rows; 573 char *text; 574 int row; 575 int lines; 576 char version[64]; 577 char dlbar[1024]; 578 579 lines = viddef.height * frac; 580 if (lines <= 0) 581 return; 582 583 if (lines > viddef.height) 584 lines = viddef.height; 585 586 // draw the background 587 re.DrawStretchPic (0, -viddef.height+lines, viddef.width, viddef.height, "conback"); 588 SCR_AddDirtyPoint (0,0); 589 SCR_AddDirtyPoint (viddef.width-1,lines-1); 590 591 Com_sprintf (version, sizeof(version), "v%4.2f", VERSION); 592 for (x=0 ; x<5 ; x++) 593 re.DrawChar (viddef.width-44+x*8, lines-12, 128 + version[x] ); 594 595 // draw the text 596 con.vislines = lines; 597 598 #if 0 599 rows = (lines-8)>>3; // rows of text to draw 600 601 y = lines - 24; 602 #else 603 rows = (lines-22)>>3; // rows of text to draw 604 605 y = lines - 30; 606 #endif 607 608 // draw from the bottom up 609 if (con.display != con.current) 610 { 611 // draw arrows to show the buffer is backscrolled 612 for (x=0 ; x<con.linewidth ; x+=4) 613 re.DrawChar ( (x+1)<<3, y, '^'); 614 615 y -= 8; 616 rows--; 617 } 618 619 row = con.display; 620 for (i=0 ; i<rows ; i++, y-=8, row--) 621 { 622 if (row < 0) 623 break; 624 if (con.current - row >= con.totallines) 625 break; // past scrollback wrap point 626 627 text = con.text + (row % con.totallines)*con.linewidth; 628 629 for (x=0 ; x<con.linewidth ; x++) 630 re.DrawChar ( (x+1)<<3, y, text[x]); 631 } 632 633 //ZOID 634 // draw the download bar 635 // figure out width 636 if (cls.download) { 637 if ((text = strrchr(cls.downloadname, '/')) != NULL) 638 text++; 639 else 640 text = cls.downloadname; 641 642 x = con.linewidth - ((con.linewidth * 7) / 40); 643 y = x - strlen(text) - 8; 644 i = con.linewidth/3; 645 if (strlen(text) > i) { 646 y = x - i - 11; 647 strncpy(dlbar, text, i); 648 dlbar[i] = 0; 649 strcat(dlbar, "..."); 650 } else 651 strcpy(dlbar, text); 652 strcat(dlbar, ": "); 653 i = strlen(dlbar); 654 dlbar[i++] = '\x80'; 655 // where's the dot go? 656 if (cls.downloadpercent == 0) 657 n = 0; 658 else 659 n = y * cls.downloadpercent / 100; 660 661 for (j = 0; j < y; j++) 662 if (j == n) 663 dlbar[i++] = '\x83'; 664 else 665 dlbar[i++] = '\x81'; 666 dlbar[i++] = '\x82'; 667 dlbar[i] = 0; 668 669 sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); 670 671 // draw it 672 y = con.vislines-12; 673 for (i = 0; i < strlen(dlbar); i++) 674 re.DrawChar ( (i+1)<<3, y, dlbar[i]); 675 } 676 //ZOID 677 678 // draw the input prompt, user text, and cursor if desired 679 Con_DrawInput (); 680 } 681 682