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#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 31int con_linewidth; 32 33float con_cursorspeed = 4; 34 35#define CON_TEXTSIZE 16384 36 37qboolean con_forcedup; // because no entities to refresh 38 39int con_totallines; // total lines in console scrollback 40int con_backscroll; // lines up from bottom to display 41int con_current; // where next message will be printed 42int con_x; // offset in current line for next print 43char *con_text=0; 44 45cvar_t con_notifytime = CVAR2("con_notifytime","3"); //seconds 46 47#define NUM_CON_TIMES 4 48float con_times[NUM_CON_TIMES]; // realtime time the line was generated 49 // for transparent notify lines 50 51int con_vislines; 52 53qboolean con_debuglog; 54 55#define MAXCMDLINE 256 56extern char key_lines[32][MAXCMDLINE]; 57extern int edit_line; 58extern int key_linepos; 59 60 61qboolean con_initialized; 62 63int con_notifylines; // scan lines to clear for notify lines 64 65extern void M_Menu_Main_f (void); 66 67/* 68================ 69Con_ToggleConsole_f 70================ 71*/ 72void 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================ 96Con_Clear_f 97================ 98*/ 99void Con_Clear_f (void) 100{ 101 if (con_text) 102 Q_memset (con_text, ' ', CON_TEXTSIZE); 103} 104 105 106/* 107================ 108Con_ClearNotify 109================ 110*/ 111void 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================ 122Con_MessageMode_f 123================ 124*/ 125extern qboolean team_message; 126 127void Con_MessageMode_f (void) 128{ 129 key_dest = key_message; 130 team_message = false; 131} 132 133 134/* 135================ 136Con_MessageMode2_f 137================ 138*/ 139void Con_MessageMode2_f (void) 140{ 141 key_dest = key_message; 142 team_message = true; 143} 144 145 146/* 147================ 148Con_CheckResize 149 150If the line width has changed, reformat the buffer. 151================ 152*/ 153void 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================ 209Con_Init 210================ 211*/ 212void 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=============== 251Con_Linefeed 252=============== 253*/ 254void 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================ 264Con_Print 265 266Handles cursor positioning, line wrapping, etc 267All console printing must go through this in order to be logged to disk 268If no console is visible, the notify window will pop up. 269================ 270*/ 271void 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================ 350Con_DebugLog 351================ 352*/ 353void 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================ 370Con_Printf 371 372Handles cursor positioning, line wrapping, etc 373================ 374*/ 375#define MAXPRINTMSG 4096 376// FIXME: make a buffer size safe vsprintf? 377void 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================ 419Con_DPrintf 420 421A Con_Printf that only shows up if the "developer" cvar is set 422================ 423*/ 424void 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================== 442Con_SafePrintf 443 444Okay to call even when the screen can't be updated 445================== 446*/ 447void 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 467DRAWING 468 469============================================================================== 470*/ 471 472 473/* 474================ 475Con_DrawInput 476 477The input line scrolls horizontally if typing goes beyond the right edge 478================ 479*/ 480void 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================ 515Con_DrawNotify 516 517Draws the last few lines of output transparently over the game top 518================ 519*/ 520void 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================ 574Con_DrawConsole 575 576Draws the console with the solid background 577The typing input line at the bottom should only be drawn if typing is allowed 578================ 579*/ 580void 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================== 625Con_NotifyBox 626================== 627*/ 628void 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