File: client\console.c

    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 683