Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cl_console.c (16316B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // console.c
     23 
     24 #include "client.h"
     25 
     26 
     27 int g_console_field_width = 78;
     28 
     29 
     30 #define	NUM_CON_TIMES 4
     31 
     32 #define		CON_TEXTSIZE	32768
     33 typedef struct {
     34 	qboolean	initialized;
     35 
     36 	short	text[CON_TEXTSIZE];
     37 	int		current;		// line where next message will be printed
     38 	int		x;				// offset in current line for next print
     39 	int		display;		// bottom of console displays this line
     40 
     41 	int 	linewidth;		// characters across screen
     42 	int		totallines;		// total lines in console scrollback
     43 
     44 	float	xadjust;		// for wide aspect screens
     45 
     46 	float	displayFrac;	// aproaches finalFrac at scr_conspeed
     47 	float	finalFrac;		// 0.0 to 1.0 lines of console to display
     48 
     49 	int		vislines;		// in scanlines
     50 
     51 	int		times[NUM_CON_TIMES];	// cls.realtime time the line was generated
     52 								// for transparent notify lines
     53 	vec4_t	color;
     54 } console_t;
     55 
     56 extern	console_t	con;
     57 
     58 console_t	con;
     59 
     60 cvar_t		*con_conspeed;
     61 cvar_t		*con_notifytime;
     62 
     63 #define	DEFAULT_CONSOLE_WIDTH	78
     64 
     65 vec4_t	console_color = {1.0, 1.0, 1.0, 1.0};
     66 
     67 
     68 /*
     69 ================
     70 Con_ToggleConsole_f
     71 ================
     72 */
     73 void Con_ToggleConsole_f (void) {
     74 	// closing a full screen console restarts the demo loop
     75 	if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) {
     76 		CL_StartDemoLoop();
     77 		return;
     78 	}
     79 
     80 	Field_Clear( &g_consoleField );
     81 	g_consoleField.widthInChars = g_console_field_width;
     82 
     83 	Con_ClearNotify ();
     84 	cls.keyCatchers ^= KEYCATCH_CONSOLE;
     85 }
     86 
     87 /*
     88 ================
     89 Con_MessageMode_f
     90 ================
     91 */
     92 void Con_MessageMode_f (void) {
     93 	chat_playerNum = -1;
     94 	chat_team = qfalse;
     95 	Field_Clear( &chatField );
     96 	chatField.widthInChars = 30;
     97 
     98 	cls.keyCatchers ^= KEYCATCH_MESSAGE;
     99 }
    100 
    101 /*
    102 ================
    103 Con_MessageMode2_f
    104 ================
    105 */
    106 void Con_MessageMode2_f (void) {
    107 	chat_playerNum = -1;
    108 	chat_team = qtrue;
    109 	Field_Clear( &chatField );
    110 	chatField.widthInChars = 25;
    111 	cls.keyCatchers ^= KEYCATCH_MESSAGE;
    112 }
    113 
    114 /*
    115 ================
    116 Con_MessageMode3_f
    117 ================
    118 */
    119 void Con_MessageMode3_f (void) {
    120 	chat_playerNum = VM_Call( cgvm, CG_CROSSHAIR_PLAYER );
    121 	if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
    122 		chat_playerNum = -1;
    123 		return;
    124 	}
    125 	chat_team = qfalse;
    126 	Field_Clear( &chatField );
    127 	chatField.widthInChars = 30;
    128 	cls.keyCatchers ^= KEYCATCH_MESSAGE;
    129 }
    130 
    131 /*
    132 ================
    133 Con_MessageMode4_f
    134 ================
    135 */
    136 void Con_MessageMode4_f (void) {
    137 	chat_playerNum = VM_Call( cgvm, CG_LAST_ATTACKER );
    138 	if ( chat_playerNum < 0 || chat_playerNum >= MAX_CLIENTS ) {
    139 		chat_playerNum = -1;
    140 		return;
    141 	}
    142 	chat_team = qfalse;
    143 	Field_Clear( &chatField );
    144 	chatField.widthInChars = 30;
    145 	cls.keyCatchers ^= KEYCATCH_MESSAGE;
    146 }
    147 
    148 /*
    149 ================
    150 Con_Clear_f
    151 ================
    152 */
    153 void Con_Clear_f (void) {
    154 	int		i;
    155 
    156 	for ( i = 0 ; i < CON_TEXTSIZE ; i++ ) {
    157 		con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
    158 	}
    159 
    160 	Con_Bottom();		// go to end
    161 }
    162 
    163 						
    164 /*
    165 ================
    166 Con_Dump_f
    167 
    168 Save the console contents out to a file
    169 ================
    170 */
    171 void Con_Dump_f (void)
    172 {
    173 	int		l, x, i;
    174 	short	*line;
    175 	fileHandle_t	f;
    176 	char	buffer[1024];
    177 
    178 	if (Cmd_Argc() != 2)
    179 	{
    180 		Com_Printf ("usage: condump <filename>\n");
    181 		return;
    182 	}
    183 
    184 	Com_Printf ("Dumped console text to %s.\n", Cmd_Argv(1) );
    185 
    186 	f = FS_FOpenFileWrite( Cmd_Argv( 1 ) );
    187 	if (!f)
    188 	{
    189 		Com_Printf ("ERROR: couldn't open.\n");
    190 		return;
    191 	}
    192 
    193 	// skip empty lines
    194 	for (l = con.current - con.totallines + 1 ; l <= con.current ; l++)
    195 	{
    196 		line = con.text + (l%con.totallines)*con.linewidth;
    197 		for (x=0 ; x<con.linewidth ; x++)
    198 			if ((line[x] & 0xff) != ' ')
    199 				break;
    200 		if (x != con.linewidth)
    201 			break;
    202 	}
    203 
    204 	// write the remaining lines
    205 	buffer[con.linewidth] = 0;
    206 	for ( ; l <= con.current ; l++)
    207 	{
    208 		line = con.text + (l%con.totallines)*con.linewidth;
    209 		for(i=0; i<con.linewidth; i++)
    210 			buffer[i] = line[i] & 0xff;
    211 		for (x=con.linewidth-1 ; x>=0 ; x--)
    212 		{
    213 			if (buffer[x] == ' ')
    214 				buffer[x] = 0;
    215 			else
    216 				break;
    217 		}
    218 		strcat( buffer, "\n" );
    219 		FS_Write(buffer, strlen(buffer), f);
    220 	}
    221 
    222 	FS_FCloseFile( f );
    223 }
    224 
    225 						
    226 /*
    227 ================
    228 Con_ClearNotify
    229 ================
    230 */
    231 void Con_ClearNotify( void ) {
    232 	int		i;
    233 	
    234 	for ( i = 0 ; i < NUM_CON_TIMES ; i++ ) {
    235 		con.times[i] = 0;
    236 	}
    237 }
    238 
    239 						
    240 
    241 /*
    242 ================
    243 Con_CheckResize
    244 
    245 If the line width has changed, reformat the buffer.
    246 ================
    247 */
    248 void Con_CheckResize (void)
    249 {
    250 	int		i, j, width, oldwidth, oldtotallines, numlines, numchars;
    251 	MAC_STATIC short	tbuf[CON_TEXTSIZE];
    252 
    253 	width = (SCREEN_WIDTH / SMALLCHAR_WIDTH) - 2;
    254 
    255 	if (width == con.linewidth)
    256 		return;
    257 
    258 	if (width < 1)			// video hasn't been initialized yet
    259 	{
    260 		width = DEFAULT_CONSOLE_WIDTH;
    261 		con.linewidth = width;
    262 		con.totallines = CON_TEXTSIZE / con.linewidth;
    263 		for(i=0; i<CON_TEXTSIZE; i++)
    264 
    265 			con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
    266 	}
    267 	else
    268 	{
    269 		oldwidth = con.linewidth;
    270 		con.linewidth = width;
    271 		oldtotallines = con.totallines;
    272 		con.totallines = CON_TEXTSIZE / con.linewidth;
    273 		numlines = oldtotallines;
    274 
    275 		if (con.totallines < numlines)
    276 			numlines = con.totallines;
    277 
    278 		numchars = oldwidth;
    279 	
    280 		if (con.linewidth < numchars)
    281 			numchars = con.linewidth;
    282 
    283 		Com_Memcpy (tbuf, con.text, CON_TEXTSIZE * sizeof(short));
    284 		for(i=0; i<CON_TEXTSIZE; i++)
    285 
    286 			con.text[i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
    287 
    288 
    289 		for (i=0 ; i<numlines ; i++)
    290 		{
    291 			for (j=0 ; j<numchars ; j++)
    292 			{
    293 				con.text[(con.totallines - 1 - i) * con.linewidth + j] =
    294 						tbuf[((con.current - i + oldtotallines) %
    295 							  oldtotallines) * oldwidth + j];
    296 			}
    297 		}
    298 
    299 		Con_ClearNotify ();
    300 	}
    301 
    302 	con.current = con.totallines - 1;
    303 	con.display = con.current;
    304 }
    305 
    306 
    307 /*
    308 ================
    309 Con_Init
    310 ================
    311 */
    312 void Con_Init (void) {
    313 	int		i;
    314 
    315 	con_notifytime = Cvar_Get ("con_notifytime", "3", 0);
    316 	con_conspeed = Cvar_Get ("scr_conspeed", "3", 0);
    317 
    318 	Field_Clear( &g_consoleField );
    319 	g_consoleField.widthInChars = g_console_field_width;
    320 	for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
    321 		Field_Clear( &historyEditLines[i] );
    322 		historyEditLines[i].widthInChars = g_console_field_width;
    323 	}
    324 
    325 	Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
    326 	Cmd_AddCommand ("messagemode", Con_MessageMode_f);
    327 	Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
    328 	Cmd_AddCommand ("messagemode3", Con_MessageMode3_f);
    329 	Cmd_AddCommand ("messagemode4", Con_MessageMode4_f);
    330 	Cmd_AddCommand ("clear", Con_Clear_f);
    331 	Cmd_AddCommand ("condump", Con_Dump_f);
    332 }
    333 
    334 
    335 /*
    336 ===============
    337 Con_Linefeed
    338 ===============
    339 */
    340 void Con_Linefeed (qboolean skipnotify)
    341 {
    342 	int		i;
    343 
    344 	// mark time for transparent overlay
    345 	if (con.current >= 0)
    346 	{
    347     if (skipnotify)
    348 		  con.times[con.current % NUM_CON_TIMES] = 0;
    349     else
    350 		  con.times[con.current % NUM_CON_TIMES] = cls.realtime;
    351 	}
    352 
    353 	con.x = 0;
    354 	if (con.display == con.current)
    355 		con.display++;
    356 	con.current++;
    357 	for(i=0; i<con.linewidth; i++)
    358 		con.text[(con.current%con.totallines)*con.linewidth+i] = (ColorIndex(COLOR_WHITE)<<8) | ' ';
    359 }
    360 
    361 /*
    362 ================
    363 CL_ConsolePrint
    364 
    365 Handles cursor positioning, line wrapping, etc
    366 All console printing must go through this in order to be logged to disk
    367 If no console is visible, the text will appear at the top of the game window
    368 ================
    369 */
    370 void CL_ConsolePrint( char *txt ) {
    371 	int		y;
    372 	int		c, l;
    373 	int		color;
    374 	qboolean skipnotify = qfalse;		// NERVE - SMF
    375 	int prev;							// NERVE - SMF
    376 
    377 	// TTimo - prefix for text that shows up in console but not in notify
    378 	// backported from RTCW
    379 	if ( !Q_strncmp( txt, "[skipnotify]", 12 ) ) {
    380 		skipnotify = qtrue;
    381 		txt += 12;
    382 	}
    383 	
    384 	// for some demos we don't want to ever show anything on the console
    385 	if ( cl_noprint && cl_noprint->integer ) {
    386 		return;
    387 	}
    388 	
    389 	if (!con.initialized) {
    390 		con.color[0] = 
    391 		con.color[1] = 
    392 		con.color[2] =
    393 		con.color[3] = 1.0f;
    394 		con.linewidth = -1;
    395 		Con_CheckResize ();
    396 		con.initialized = qtrue;
    397 	}
    398 
    399 	color = ColorIndex(COLOR_WHITE);
    400 
    401 	while ( (c = *txt) != 0 ) {
    402 		if ( Q_IsColorString( txt ) ) {
    403 			color = ColorIndex( *(txt+1) );
    404 			txt += 2;
    405 			continue;
    406 		}
    407 
    408 		// count word length
    409 		for (l=0 ; l< con.linewidth ; l++) {
    410 			if ( txt[l] <= ' ') {
    411 				break;
    412 			}
    413 
    414 		}
    415 
    416 		// word wrap
    417 		if (l != con.linewidth && (con.x + l >= con.linewidth) ) {
    418 			Con_Linefeed(skipnotify);
    419 
    420 		}
    421 
    422 		txt++;
    423 
    424 		switch (c)
    425 		{
    426 		case '\n':
    427 			Con_Linefeed (skipnotify);
    428 			break;
    429 		case '\r':
    430 			con.x = 0;
    431 			break;
    432 		default:	// display character and advance
    433 			y = con.current % con.totallines;
    434 			con.text[y*con.linewidth+con.x] = (color << 8) | c;
    435 			con.x++;
    436 			if (con.x >= con.linewidth) {
    437 				Con_Linefeed(skipnotify);
    438 				con.x = 0;
    439 			}
    440 			break;
    441 		}
    442 	}
    443 
    444 
    445 	// mark time for transparent overlay
    446 	if (con.current >= 0) {
    447 		// NERVE - SMF
    448 		if ( skipnotify ) {
    449 			prev = con.current % NUM_CON_TIMES - 1;
    450 			if ( prev < 0 )
    451 				prev = NUM_CON_TIMES - 1;
    452 			con.times[prev] = 0;
    453 		}
    454 		else
    455 		// -NERVE - SMF
    456 			con.times[con.current % NUM_CON_TIMES] = cls.realtime;
    457 	}
    458 }
    459 
    460 
    461 /*
    462 ==============================================================================
    463 
    464 DRAWING
    465 
    466 ==============================================================================
    467 */
    468 
    469 
    470 /*
    471 ================
    472 Con_DrawInput
    473 
    474 Draw the editline after a ] prompt
    475 ================
    476 */
    477 void Con_DrawInput (void) {
    478 	int		y;
    479 
    480 	if ( cls.state != CA_DISCONNECTED && !(cls.keyCatchers & KEYCATCH_CONSOLE ) ) {
    481 		return;
    482 	}
    483 
    484 	y = con.vislines - ( SMALLCHAR_HEIGHT * 2 );
    485 
    486 	re.SetColor( con.color );
    487 
    488 	SCR_DrawSmallChar( con.xadjust + 1 * SMALLCHAR_WIDTH, y, ']' );
    489 
    490 	Field_Draw( &g_consoleField, con.xadjust + 2 * SMALLCHAR_WIDTH, y,
    491 		SCREEN_WIDTH - 3 * SMALLCHAR_WIDTH, qtrue );
    492 }
    493 
    494 
    495 /*
    496 ================
    497 Con_DrawNotify
    498 
    499 Draws the last few lines of output transparently over the game top
    500 ================
    501 */
    502 void Con_DrawNotify (void)
    503 {
    504 	int		x, v;
    505 	short	*text;
    506 	int		i;
    507 	int		time;
    508 	int		skip;
    509 	int		currentColor;
    510 
    511 	currentColor = 7;
    512 	re.SetColor( g_color_table[currentColor] );
    513 
    514 	v = 0;
    515 	for (i= con.current-NUM_CON_TIMES+1 ; i<=con.current ; i++)
    516 	{
    517 		if (i < 0)
    518 			continue;
    519 		time = con.times[i % NUM_CON_TIMES];
    520 		if (time == 0)
    521 			continue;
    522 		time = cls.realtime - time;
    523 		if (time > con_notifytime->value*1000)
    524 			continue;
    525 		text = con.text + (i % con.totallines)*con.linewidth;
    526 
    527 		if (cl.snap.ps.pm_type != PM_INTERMISSION && cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) {
    528 			continue;
    529 		}
    530 
    531 		for (x = 0 ; x < con.linewidth ; x++) {
    532 			if ( ( text[x] & 0xff ) == ' ' ) {
    533 				continue;
    534 			}
    535 			if ( ( (text[x]>>8)&7 ) != currentColor ) {
    536 				currentColor = (text[x]>>8)&7;
    537 				re.SetColor( g_color_table[currentColor] );
    538 			}
    539 			SCR_DrawSmallChar( cl_conXOffset->integer + con.xadjust + (x+1)*SMALLCHAR_WIDTH, v, text[x] & 0xff );
    540 		}
    541 
    542 		v += SMALLCHAR_HEIGHT;
    543 	}
    544 
    545 	re.SetColor( NULL );
    546 
    547 	if (cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME) ) {
    548 		return;
    549 	}
    550 
    551 	// draw the chat line
    552 	if ( cls.keyCatchers & KEYCATCH_MESSAGE )
    553 	{
    554 		if (chat_team)
    555 		{
    556 			SCR_DrawBigString (8, v, "say_team:", 1.0f );
    557 			skip = 11;
    558 		}
    559 		else
    560 		{
    561 			SCR_DrawBigString (8, v, "say:", 1.0f );
    562 			skip = 5;
    563 		}
    564 
    565 		Field_BigDraw( &chatField, skip * BIGCHAR_WIDTH, v,
    566 			SCREEN_WIDTH - ( skip + 1 ) * BIGCHAR_WIDTH, qtrue );
    567 
    568 		v += BIGCHAR_HEIGHT;
    569 	}
    570 
    571 }
    572 
    573 /*
    574 ================
    575 Con_DrawSolidConsole
    576 
    577 Draws the console with the solid background
    578 ================
    579 */
    580 void Con_DrawSolidConsole( float frac ) {
    581 	int				i, x, y;
    582 	int				rows;
    583 	short			*text;
    584 	int				row;
    585 	int				lines;
    586 //	qhandle_t		conShader;
    587 	int				currentColor;
    588 	vec4_t			color;
    589 
    590 	lines = cls.glconfig.vidHeight * frac;
    591 	if (lines <= 0)
    592 		return;
    593 
    594 	if (lines > cls.glconfig.vidHeight )
    595 		lines = cls.glconfig.vidHeight;
    596 
    597 	// on wide screens, we will center the text
    598 	con.xadjust = 0;
    599 	SCR_AdjustFrom640( &con.xadjust, NULL, NULL, NULL );
    600 
    601 	// draw the background
    602 	y = frac * SCREEN_HEIGHT - 2;
    603 	if ( y < 1 ) {
    604 		y = 0;
    605 	}
    606 	else {
    607 		SCR_DrawPic( 0, 0, SCREEN_WIDTH, y, cls.consoleShader );
    608 	}
    609 
    610 	color[0] = 1;
    611 	color[1] = 0;
    612 	color[2] = 0;
    613 	color[3] = 1;
    614 	SCR_FillRect( 0, y, SCREEN_WIDTH, 2, color );
    615 
    616 
    617 	// draw the version number
    618 
    619 	re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
    620 
    621 	i = strlen( Q3_VERSION );
    622 
    623 	for (x=0 ; x<i ; x++) {
    624 
    625 		SCR_DrawSmallChar( cls.glconfig.vidWidth - ( i - x ) * SMALLCHAR_WIDTH, 
    626 
    627 			(lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), Q3_VERSION[x] );
    628 
    629 	}
    630 
    631 
    632 	// draw the text
    633 	con.vislines = lines;
    634 	rows = (lines-SMALLCHAR_WIDTH)/SMALLCHAR_WIDTH;		// rows of text to draw
    635 
    636 	y = lines - (SMALLCHAR_HEIGHT*3);
    637 
    638 	// draw from the bottom up
    639 	if (con.display != con.current)
    640 	{
    641 	// draw arrows to show the buffer is backscrolled
    642 		re.SetColor( g_color_table[ColorIndex(COLOR_RED)] );
    643 		for (x=0 ; x<con.linewidth ; x+=4)
    644 			SCR_DrawSmallChar( con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, '^' );
    645 		y -= SMALLCHAR_HEIGHT;
    646 		rows--;
    647 	}
    648 	
    649 	row = con.display;
    650 
    651 	if ( con.x == 0 ) {
    652 		row--;
    653 	}
    654 
    655 	currentColor = 7;
    656 	re.SetColor( g_color_table[currentColor] );
    657 
    658 	for (i=0 ; i<rows ; i++, y -= SMALLCHAR_HEIGHT, row--)
    659 	{
    660 		if (row < 0)
    661 			break;
    662 		if (con.current - row >= con.totallines) {
    663 			// past scrollback wrap point
    664 			continue;	
    665 		}
    666 
    667 		text = con.text + (row % con.totallines)*con.linewidth;
    668 
    669 		for (x=0 ; x<con.linewidth ; x++) {
    670 			if ( ( text[x] & 0xff ) == ' ' ) {
    671 				continue;
    672 			}
    673 
    674 			if ( ( (text[x]>>8)&7 ) != currentColor ) {
    675 				currentColor = (text[x]>>8)&7;
    676 				re.SetColor( g_color_table[currentColor] );
    677 			}
    678 			SCR_DrawSmallChar(  con.xadjust + (x+1)*SMALLCHAR_WIDTH, y, text[x] & 0xff );
    679 		}
    680 	}
    681 
    682 	// draw the input prompt, user text, and cursor if desired
    683 	Con_DrawInput ();
    684 
    685 	re.SetColor( NULL );
    686 }
    687 
    688 
    689 
    690 /*
    691 ==================
    692 Con_DrawConsole
    693 ==================
    694 */
    695 void Con_DrawConsole( void ) {
    696 	// check for console width changes from a vid mode change
    697 	Con_CheckResize ();
    698 
    699 	// if disconnected, render console full screen
    700 	if ( cls.state == CA_DISCONNECTED ) {
    701 		if ( !( cls.keyCatchers & (KEYCATCH_UI | KEYCATCH_CGAME)) ) {
    702 			Con_DrawSolidConsole( 1.0 );
    703 			return;
    704 		}
    705 	}
    706 
    707 	if ( con.displayFrac ) {
    708 		Con_DrawSolidConsole( con.displayFrac );
    709 	} else {
    710 		// draw notify lines
    711 		if ( cls.state == CA_ACTIVE ) {
    712 			Con_DrawNotify ();
    713 		}
    714 	}
    715 }
    716 
    717 //================================================================
    718 
    719 /*
    720 ==================
    721 Con_RunConsole
    722 
    723 Scroll it up or down
    724 ==================
    725 */
    726 void Con_RunConsole (void) {
    727 	// decide on the destination height of the console
    728 	if ( cls.keyCatchers & KEYCATCH_CONSOLE )
    729 		con.finalFrac = 0.5;		// half screen
    730 	else
    731 		con.finalFrac = 0;				// none visible
    732 	
    733 	// scroll towards the destination height
    734 	if (con.finalFrac < con.displayFrac)
    735 	{
    736 		con.displayFrac -= con_conspeed->value*cls.realFrametime*0.001;
    737 		if (con.finalFrac > con.displayFrac)
    738 			con.displayFrac = con.finalFrac;
    739 
    740 	}
    741 	else if (con.finalFrac > con.displayFrac)
    742 	{
    743 		con.displayFrac += con_conspeed->value*cls.realFrametime*0.001;
    744 		if (con.finalFrac < con.displayFrac)
    745 			con.displayFrac = con.finalFrac;
    746 	}
    747 
    748 }
    749 
    750 
    751 void Con_PageUp( void ) {
    752 	con.display -= 2;
    753 	if ( con.current - con.display >= con.totallines ) {
    754 		con.display = con.current - con.totallines + 1;
    755 	}
    756 }
    757 
    758 void Con_PageDown( void ) {
    759 	con.display += 2;
    760 	if (con.display > con.current) {
    761 		con.display = con.current;
    762 	}
    763 }
    764 
    765 void Con_Top( void ) {
    766 	con.display = con.totallines;
    767 	if ( con.current - con.display >= con.totallines ) {
    768 		con.display = con.current - con.totallines + 1;
    769 	}
    770 }
    771 
    772 void Con_Bottom( void ) {
    773 	con.display = con.current;
    774 }
    775 
    776 
    777 void Con_Close( void ) {
    778 	if ( !com_cl_running->integer ) {
    779 		return;
    780 	}
    781 	Field_Clear( &g_consoleField );
    782 	Con_ClearNotify ();
    783 	cls.keyCatchers &= ~KEYCATCH_CONSOLE;
    784 	con.finalFrac = 0;				// none visible
    785 	con.displayFrac = 0;
    786 }