Home | History | Annotate | Download | only in WinQuake
      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