Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

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