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