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