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 #include "prompt.h"
24 #include "version.h"
25 
26 #define	CON_TIMES		4
27 #define CON_TIMES_MASK	( CON_TIMES - 1 )
28 
29 #define CON_TOTALLINES			1024	// total lines in console scrollback
30 #define CON_TOTALLINES_MASK		( CON_TOTALLINES - 1 )
31 
32 /* max chars in a single line.
33  * this should be large enough to hold (maxscreenwidth / charwidth) chars,
34  * plus some extra color escape codes
35  */
36 #define CON_LINEWIDTH	512
37 
38 typedef struct console_s {
39 	qboolean	initialized;
40 
41 	char	text[CON_TOTALLINES][CON_LINEWIDTH];
42 	int		current;		// line where next message will be printed
43 	int		x;				// offset in current line for next print
44 	int		display;		// bottom of console displays this line
45 
46 	int 	linewidth;		// characters across screen
47 	int		vidWidth, vidHeight;
48 	float	scale;
49 
50 	int		times[CON_TIMES];	// cls.realtime time the line was generated
51 								// for transparent notify lines
52 	qboolean	addToNotify;
53 
54 	qhandle_t	conbackImage;
55 	qhandle_t	charsetImage;
56 
57 	int		charWidth, charHeight;
58 
59 	float	currentHeight;	// aproaches scr_conlines at scr_conspeed
60 	float	destHeight;		// 0.0 to 1.0 lines of console to display
61 	float	maxHeight;
62 
63 	inputField_t chatField;
64 	commandPrompt_t prompt;
65 
66 	qboolean	chatTeam;
67 } console_t;
68 
69 static console_t	con;
70 
71 static cvar_t	*con_notifytime;
72 static cvar_t	*con_clock;
73 static cvar_t	*con_height;
74 static cvar_t	*con_speed;
75 static cvar_t	*con_chat;
76 static cvar_t	*con_alpha;
77 static cvar_t	*con_scale;
78 static cvar_t	*con_font;
79 static cvar_t	*con_background;
80 
81 // ============================================================================
82 
83 /*
84 ================
85 Con_AddToNotify
86 ================
87 */
Con_AddToNotify(qboolean add)88 void Con_AddToNotify( qboolean add ) {
89 	con.addToNotify = add;
90 }
91 
92 /*
93 ================
94 Con_ClearTyping
95 ================
96 */
Con_ClearTyping(void)97 void Con_ClearTyping( void ) {
98 	// clear any typing
99 	IF_Clear( &con.prompt.inputLine );
100 }
101 
102 /*
103 ================
104 Con_Close
105 ================
106 */
Con_Close(void)107 void Con_Close( void ) {
108 	Key_SetDest( cls.key_dest & ~KEY_CONSOLE );
109 	con.currentHeight = 0;
110 
111 	Con_ClearTyping();
112 	Con_ClearNotify_f();
113 
114 	Cvar_Set( "cl_paused", "0" );
115 }
116 
117 /*
118 ================
119 Con_ToggleConsole_f
120 ================
121 */
Con_ToggleConsole_f(void)122 void Con_ToggleConsole_f( void ) {
123     if( sv_running->integer && cl.attractLoop == ATR_DEMO && !cls.demoplayback )
124     {
125 		Cbuf_AddText( "killserver\n" );
126 		//return;
127 	}
128 
129 	Con_ClearTyping();
130 	Con_ClearNotify_f();
131 
132 	if( cls.key_dest & KEY_CONSOLE ) {
133 		Key_SetDest( cls.key_dest & ~KEY_CONSOLE );
134 		//Cvar_Set( "cl_paused", "0" );
135 		return;
136 	}
137 
138 	// FIXME: use old q2 style
139 	Key_SetDest( ( cls.key_dest | KEY_CONSOLE ) & ~KEY_MESSAGE );
140 
141 	// only pause in single player
142 	//if( Cvar_VariableInteger( "maxclients" ) == 1 ) {
143 	//	Cvar_Set( "cl_paused", "1" );
144 	//}
145 
146 }
147 
148 /*
149 ================
150 Con_Clear_f
151 ================
152 */
Con_Clear_f(void)153 void Con_Clear_f( void ) {
154 	memset( con.text, 0, sizeof( con.text ) );
155 	con.display = con.current;
156 }
157 
Con_Dump_g(const char * partial,int state)158 static const char *Con_Dump_g( const char *partial, int state ) {
159 	return Com_FileNameGenerator( "", ".txt", partial, qtrue, state );
160 }
161 
162 /*
163 ================
164 Con_Dump_f
165 
166 Save the console contents out to a file
167 ================
168 */
Con_Dump_f(void)169 void Con_Dump_f( void ) {
170 	int		l;
171 	char	*line;
172 	fileHandle_t	f;
173 	char	buffer[CON_LINEWIDTH];
174 	char	name[MAX_QPATH];
175 
176 	if( Cmd_Argc() != 2 ) {
177 		Com_Printf( "Usage: %s <filename>\n", Cmd_Argv( 0 ) );
178 		return;
179 	}
180 
181 	Cmd_ArgvBuffer( 1, name, sizeof( name ) );
182 	COM_DefaultExtension( name, ".txt", sizeof( buffer ) );
183 
184 	FS_FOpenFile( name, &f, FS_MODE_WRITE );
185 	if( !f ) {
186 		Com_EPrintf( "Couldn't open %s for writing.\n", name );
187 		return;
188 	}
189 
190 	// skip empty lines
191 	for( l = con.current - CON_TOTALLINES + 1 ; l <= con.current ; l++ ) {
192 		if( con.text[l & CON_TOTALLINES_MASK][0] ) {
193 			break;
194 		}
195 	}
196 
197 	// write the remaining lines
198 	for( ; l <= con.current ; l++ ) {
199 		line = con.text[l & CON_TOTALLINES_MASK];
200 		Q_CleanColorStr( line, buffer, sizeof( buffer ) );
201 
202 		FS_FPrintf( f, "%s\n", buffer );
203 	}
204 
205 	FS_FCloseFile( f );
206 
207 	Com_Printf( "Dumped console text to %s.\n", name );
208 }
209 
210 
211 /*
212 ================
213 Con_ClearNotify_f
214 ================
215 */
Con_ClearNotify_f(void)216 void Con_ClearNotify_f( void ) {
217 	int		i;
218 
219 	for( i = 0; i < CON_TIMES; i++ )
220 		con.times[i] = 0;
221 }
222 
223 
224 /*
225 ================
226 Con_MessageMode_f
227 ================
228 */
Con_MessageMode_f(void)229 void Con_MessageMode_f( void ) {
230 	con.chatTeam = qfalse;
231 	Key_SetDest( cls.key_dest | KEY_MESSAGE );
232 }
233 
234 /*
235 ================
236 Con_MessageMode2_f
237 ================
238 */
Con_MessageMode2_f(void)239 void Con_MessageMode2_f( void ) {
240 	con.chatTeam = qtrue;
241 	Key_SetDest( cls.key_dest | KEY_MESSAGE );
242 }
243 
244 /*
245 ================
246 Con_CheckResize
247 
248 If the line width has changed, reformat the buffer.
249 ================
250 */
Con_CheckResize(void)251 void Con_CheckResize( void ) {
252 	int		width;
253 
254 	con.vidWidth = scr_glconfig.vidWidth * con.scale;
255 	con.vidHeight = scr_glconfig.vidHeight * con.scale;
256 
257 	width = ( con.vidWidth / con.charWidth ) - 2;
258 
259 	if( width == con.linewidth )
260 		return;
261 
262 	con.linewidth = width > CON_LINEWIDTH ? CON_LINEWIDTH : width;
263 	con.prompt.inputLine.visibleChars = con.linewidth;
264 	con.prompt.widthInChars = con.linewidth;
265 	con.chatField.visibleChars = con.linewidth;
266 }
267 
268 
269 /*
270 ================
271 Con_OnChangeVar
272 ================
273 */
Con_OnChangeVar(cvar_t * self,void * arg)274 static void Con_OnChangeVar( cvar_t *self, void *arg ) {
275 	if( con.initialized && cls.ref_initialized ) {
276 		Con_SetupDC();
277 	}
278 }
279 
280 
281 /*
282 ================
283 Con_Init
284 ================
285 */
Con_Init(void)286 void Con_Init( void ) {
287 	memset( &con, 0, sizeof( con ) );
288 
289 //
290 // register our commands
291 //
292 	con_notifytime = Cvar_Get( "con_notifytime", "3", 0 );
293 	con_clock = Cvar_Get( "con_clock", "0", CVAR_ARCHIVE );
294 	con_height = Cvar_Get( "con_height", "0.5", CVAR_ARCHIVE );
295 	con_speed = Cvar_Get( "scr_conspeed", "3", 0 );
296 	con_chat = Cvar_Get( "con_chat", "0", 0 );
297 	con_alpha = Cvar_Get( "con_alpha", "1", CVAR_ARCHIVE );
298 	con_scale = Cvar_Get( "con_scale", "1", CVAR_ARCHIVE );
299 	con_font = Cvar_Get( "con_font", "conchars", CVAR_ARCHIVE );
300 	con_font->changedFunc = Con_OnChangeVar;
301 	con_background = Cvar_Get( "con_background", "conback", CVAR_ARCHIVE );
302 	con_background->changedFunc = Con_OnChangeVar;
303 
304 	Cmd_AddCommand( "toggleconsole", Con_ToggleConsole_f );
305 	Cmd_AddCommand( "messagemode", Con_MessageMode_f );
306 	Cmd_AddCommand( "messagemode2", Con_MessageMode2_f );
307 	Cmd_AddCommand( "clear", Con_Clear_f );
308 	Cmd_AddCommand( "clearnotify", Con_ClearNotify_f );
309 	Cmd_AddCommandEx( "condump", Con_Dump_f, Con_Dump_g );
310 
311 	IF_Init( &con.prompt.inputLine, 1, MAX_FIELD_TEXT );
312 	IF_Init( &con.chatField, 1, MAX_FIELD_TEXT );
313 
314 	con.prompt.Printf = Con_Printf;
315 
316 	con.addToNotify = qtrue;
317 
318 	// use default width if no video initialized yet
319 	scr_glconfig.vidWidth = 640;
320 	scr_glconfig.vidHeight = 480;
321 	con.linewidth = -1;
322 	con.charWidth = 8;
323 	con.charHeight = 8;
324 	con.maxHeight = 1;
325 	con.scale = 1;
326 
327 	Con_CheckResize();
328 
329 	con.initialized = qtrue;
330 
331 	Com_Printf( "Console initialized.\n" );
332 }
333 
334 /*
335 ================
336 Con_Shutdown
337 ================
338 */
Con_Shutdown(void)339 void Con_Shutdown( void ) {
340 	Prompt_Clear( &con.prompt );
341 }
342 
343 
344 /*
345 ===============
346 Con_Linefeed
347 ===============
348 */
Con_Linefeed(void)349 void Con_Linefeed( void ) {
350 	con.x = 0;
351 
352 	if( con.display == con.current )
353 		con.display++;
354 	con.current++;
355 
356 	memset( con.text[con.current & CON_TOTALLINES_MASK], 0,
357             sizeof( con.text[0] ) );
358 }
359 
360 /*
361 ================
362 Con_Print
363 
364 Handles cursor positioning, line wrapping, etc
365 All console printing must go through this in order to be displayed on screen
366 If no console is visible, the text will appear at the top of the game window
367 ================
368 */
Con_Print(const char * txt)369 void Con_Print( const char *txt ) {
370 	int color;
371 	static qboolean	cr;
372 	char *p;
373 	int		l;
374 
375 	if( !con.initialized )
376 		return;
377 
378 	color = 0;
379 	while( *txt ) {
380 	// count word length
381 		l = 0;
382 		p = ( char * )txt;
383 		while( *p > 32 || *p == Q_COLOR_ESCAPE ) {
384 			if( Q_IsColorString( p ) ) {
385 				p += 2;
386 			} else {
387 				l++; p++;
388 			}
389 		}
390 
391 	// word wrap
392 		p = con.text[con.current & CON_TOTALLINES_MASK];
393 		if( l < con.linewidth && ( Q_DrawStrlen( p ) + l > con.linewidth ) )
394 			con.x = 0;
395 
396 		if( cr ) {
397 			cr = qfalse;
398 			con.current--;
399 		}
400 
401 		if( !con.x ) {
402 			Con_Linefeed();
403 
404 		// add color from last line
405 			if( color ) {
406 				p = con.text[con.current & CON_TOTALLINES_MASK];
407 				p[con.x++] = Q_COLOR_ESCAPE;
408 				p[con.x++] = color;
409 			}
410 		}
411 
412 		switch( *txt ) {
413 		case '\r':
414 			cr = qtrue;
415 		case '\n':
416 			con.x = 0;
417 			//color = 0;
418 			break;
419 		case Q_COLOR_ESCAPE:
420 			if( !txt[1] ) {
421                 break;
422             }
423             txt++;
424 			color = *txt;
425             p = con.text[con.current & CON_TOTALLINES_MASK];
426             p[con.x++] = Q_COLOR_ESCAPE;
427             p[con.x++] = color;
428             break;
429 		default:	// display character and advance
430 			p = con.text[con.current & CON_TOTALLINES_MASK];
431 			p[con.x++] = *txt;
432 			if( Q_DrawStrlen( p ) > con.linewidth )
433 				con.x = 0;
434 			break;
435 		}
436 
437 		txt++;
438 
439 	}
440 
441 	// mark time for transparent overlay
442 	// mark every time something is printed,
443 	// so long lines don't disappear to early
444 	if( con.current >= 0 && con.addToNotify )
445 		con.times[con.current & CON_TIMES_MASK] = cls.realtime;
446 }
447 
448 /*
449 ================
450 Con_Printf
451 
452 Useful for printing text to graphical console only,
453 bypassing system console and logfiles
454 ================
455 */
Con_Printf(const char * fmt,...)456 void Con_Printf( const char *fmt, ... ) {
457 	va_list		argptr;
458 	char		msg[MAXPRINTMSG];
459 
460 	va_start( argptr, fmt );
461 	Q_vsnprintf( msg, sizeof( msg ), fmt, argptr );
462 	va_end( argptr );
463 
464     Con_Print( msg );
465 }
466 
467 /*
468 ================
469 Con_SetupDC
470 ================
471 */
Con_SetupDC(void)472 void Con_SetupDC( void ) {
473 	if( !( con.charsetImage = ref.RegisterFont( con_font->string ) ) ) {
474         /* fall back to default */
475 		Com_WPrintf( "Couldn't load %s, falling back to default...\n", con_font->string );
476 		con.charsetImage = ref.RegisterFont( "conchars" );
477 	}
478     if( !con.charsetImage ) {
479         Com_Error( ERR_FATAL, "Couldn't load pics/conchars.pcx" );
480     }
481     ref.DrawGetFontSize( &con.charWidth, &con.charHeight, con.charsetImage );
482 
483 	con.conbackImage = ref.RegisterPic( con_background->string );
484 
485 }
486 
487 /*
488 ==============================================================================
489 
490 DRAWING
491 
492 ==============================================================================
493 */
494 
495 #define CON_PRESTEP		( 10 + con.charHeight * 2 )
496 
497 /*
498 ================
499 Con_DrawNotify
500 
501 Draws the last few lines of output transparently over the game top
502 ================
503 */
Con_DrawNotify(void)504 void Con_DrawNotify( void ) {
505 	int		v;
506 	char	*text;
507 	int		i;
508 	int		time;
509 	int		skip;
510 	float	alpha;
511 	int		flags;
512 
513 	/* only draw notify in game */
514 	if( cls.state != ca_active ) {
515 		return;
516 	}
517 	if( cls.key_dest & ( KEY_MENU|KEY_CONSOLE ) ) {
518 		return;
519 	}
520 	if( con.currentHeight ) {
521 		return;
522 	}
523 
524 	flags = 0;
525 
526 	v = 0;
527 	for( i = con.current - CON_TIMES + 1; i <= con.current; i++ ) {
528 		if( i < 0 )
529 			continue;
530 		time = con.times[i & CON_TIMES_MASK];
531 		if( time == 0 )
532 			continue;
533 		// alpha fade the last string left on screen
534 		alpha = SCR_FadeAlpha( time, con_notifytime->value * 1000, 300 );
535 		if( !alpha )
536 			continue;
537 		text = con.text[i & CON_TOTALLINES_MASK];
538 
539 		if( v || i != con.current ) {
540 			alpha = 1;	// don't fade
541 		}
542 
543         ref.SetColor( DRAW_COLOR_ALPHA, ( byte * )&alpha );
544 		ref.DrawString( con.charWidth, v, flags, con.linewidth, text,
545                 con.charsetImage );
546 
547 		v += con.charHeight;
548 	}
549 
550     ref.SetColor( DRAW_COLOR_CLEAR, NULL );
551 
552 	if( cls.key_dest & KEY_MESSAGE ) {
553 		if( con.chatTeam ) {
554 			text = "say_team:";
555 			skip = 11;
556 		} else {
557 			text = "say:";
558 			skip = 5;
559 		}
560 
561 		ref.DrawString( con.charWidth, v, flags, MAX_STRING_CHARS, text,
562                 con.charsetImage );
563 		IF_Draw( &con.chatField, skip * con.charWidth, v,
564                 flags | UI_DRAWCURSOR, con.charsetImage );
565 
566 	}
567 
568 
569 }
570 
571 /*
572 ================
573 Con_DrawSolidConsole
574 
575 Draws the console with the solid background
576 ================
577 */
Con_DrawSolidConsole(void)578 void Con_DrawSolidConsole( void ) {
579 	int				i, y;
580 	int				rows;
581 	char			*text;
582 	int				row;
583 	char			buffer[CON_LINEWIDTH];
584 	int				vislines;
585 	float			alpha;
586 	int				flags;
587 	clipRect_t		clip;
588 
589 	vislines = con.vidHeight * con.currentHeight;
590 	if( vislines <= 0 )
591 		return;
592 
593 	if( vislines > con.vidHeight )
594 		vislines = con.vidHeight;
595 
596 // setup transparency
597 	if( cls.state == ca_active &&
598 		con_alpha->value &&
599         ( cls.key_dest & KEY_MENU ) == 0 )
600     {
601 		alpha = 0.5f + 0.5f * ( con.currentHeight / con_height->value );
602 
603 		Cvar_ClampValue( con_alpha, 0, 1 );
604 		alpha *= con_alpha->value;
605 
606 		ref.SetColor( DRAW_COLOR_ALPHA, ( byte * )&alpha );
607 	}
608 
609 	clip.left = 0;
610 	clip.top = 0;
611 	clip.right = 0;
612 	clip.bottom = 0;
613 	ref.SetClipRect( DRAW_CLIP_TOP, &clip );
614 
615 // draw the background
616 	if( cls.state != ca_active || ( cls.key_dest & KEY_MENU ) || con_alpha->value ) {
617 		ref.DrawStretchPic( 0, vislines - con.vidHeight,
618             con.vidWidth, con.vidHeight, con.conbackImage );
619 	}
620 
621 // setup text rendering flags
622 	flags = 0;
623 
624 	ref.SetColor( DRAW_COLOR_RGBA, colorCyan );
625 
626 // draw clock
627 	if( con_clock->integer ) {
628 		extern void Com_Time_m( char *buffer, int bufferSize );
629 
630 		Com_Time_m( buffer, sizeof( buffer ) );
631 
632 		UIS_DrawStringEx( con.vidWidth - 8,
633 			vislines - CON_PRESTEP, flags | UI_RIGHT, MAX_STRING_CHARS,
634 			    buffer, con.charsetImage );
635 	}
636 
637 // draw version
638 	UIS_DrawStringEx( con.vidWidth - 8,
639 		vislines - CON_PRESTEP + con.charHeight, flags | UI_RIGHT,
640 		    MAX_STRING_CHARS, APPLICATION " " VERSION, con.charsetImage );
641 
642 
643 // draw the text
644 	y = vislines - CON_PRESTEP;
645 	rows = y / con.charHeight + 1;		// rows of text to draw
646 
647 // draw arrows to show the buffer is backscrolled
648 	if( con.display != con.current ) {
649 		ref.SetColor( DRAW_COLOR_RGBA, colorRed );
650 		for( i = 1; i < con.linewidth; i += 4 ) {
651 			ref.DrawChar( i * con.charWidth, y, flags, '^', con.charsetImage );
652 		}
653 
654 		y -= con.charHeight;
655 		rows--;
656 	}
657 
658 // draw from the bottom up
659 	ref.SetColor( DRAW_COLOR_CLEAR, NULL );
660 	row = con.display;
661 	for( i = 0; i < rows; i++ ) {
662 		if( row < 0 )
663 			break;
664 		if( con.current - row > CON_TOTALLINES - 1 )
665 			break;		// past scrollback wrap point
666 
667 		text = con.text[row & CON_TOTALLINES_MASK];
668 
669 		ref.DrawString( con.charWidth, y, flags, con.linewidth, text,
670                 con.charsetImage );
671 
672 		y -= con.charHeight;
673 		row--;
674 
675 	}
676 
677 //ZOID
678 	// draw the download bar
679 	// figure out width
680 	if( cls.download ) {
681 		int x, n, j;
682 
683 		if( ( text = strrchr( cls.downloadname, '/') ) != NULL )
684 			text++;
685 		else
686 			text = cls.downloadname;
687 
688 		x = con.linewidth - ( ( con.linewidth * 7 ) / 40 );
689 		y = x - strlen( text ) - 8;
690 		i = con.linewidth / 3;
691 		if ( strlen( text ) > i ) {
692 			y = x - i - 11;
693 			strncpy( buffer, text, i );
694 			buffer[i] = 0;
695 			strcat( buffer, "..." );
696 		} else {
697 			strcpy( buffer, text );
698 		}
699 		strcat( buffer, ": " );
700 		i = strlen( buffer );
701 		buffer[i++] = '\x80';
702 		// where's the dot go?
703 		n = y * cls.downloadpercent / 100;
704 		for ( j = 0; j < y; j++ ) {
705 			if ( j == n ) {
706 				buffer[i++] = '\x83';
707 			} else {
708 				buffer[i++] = '\x81';
709 			}
710 		}
711 		buffer[i++] = '\x82';
712 		buffer[i] = 0;
713 
714 		sprintf( buffer + i, " %02d%%", cls.downloadpercent );
715 
716 		// draw it
717 		y = vislines - 10;
718 		ref.DrawString( con.charWidth, y, 0, CON_LINEWIDTH, buffer, con.charsetImage );
719 	}
720 //ZOID
721 
722 // draw the input prompt, user text, and cursor if desired
723 	if( cls.key_dest & KEY_CONSOLE ) {
724 		y = vislines - CON_PRESTEP + con.charHeight;
725 
726 		// draw it
727 		IF_Draw( &con.prompt.inputLine, 2 * con.charWidth, y,
728                 flags | UI_DRAWCURSOR, con.charsetImage );
729 
730 		// draw command prompt
731 		ref.SetColor( DRAW_COLOR_RGBA, colorYellow );
732 		ref.DrawChar( con.charWidth, y, flags, 17, con.charsetImage );
733 	}
734 
735 	// restore rendering parameters
736     ref.SetColor( DRAW_COLOR_CLEAR, NULL );
737 	ref.SetClipRect( DRAW_CLIP_DISABLED, NULL );
738 
739 }
740 
741 //=============================================================================
742 
Con_SetMaxHeight(float frac)743 void Con_SetMaxHeight( float frac ) {
744 	con.maxHeight = frac;
745 }
746 
747 /*
748 ==================
749 Con_RunConsole
750 
751 Scroll it up or down
752 ==================
753 */
Con_RunConsole(void)754 void Con_RunConsole( void ) {
755 	Cvar_ClampValue( con_height, 0.1f, con.maxHeight );
756 
757 	if( cls.state == ca_disconnected && !( cls.key_dest & KEY_MENU ) ) {
758 		/* draw fullscreen console */
759 		con.destHeight = con.maxHeight;
760 		con.currentHeight = con.destHeight;
761 		return;
762 	}
763 
764 	if( cls.state > ca_disconnected && cls.state < ca_active ) {
765 		if( !cls.ui_initialized ) {
766 			/* draw half-screen console */
767 			con.destHeight = min( con.maxHeight, 0.5f );
768 			con.currentHeight = con.destHeight;
769 			return;
770 		}
771 	}
772 
773 // decide on the height of the console
774 	if( cls.key_dest & KEY_CONSOLE ) {
775 		con.destHeight = con_height->value;		// half screen
776 	} else {
777 		con.destHeight = 0;				// none visible
778 	}
779 
780 	if( con.currentHeight > con.destHeight ) {
781 		con.currentHeight -= con_speed->value * cls.frametime;
782 		if( con.currentHeight < con.destHeight ) {
783 			con.currentHeight = con.destHeight;
784 		}
785 	} else if( con.currentHeight < con.destHeight ) {
786 		con.currentHeight += con_speed->value * cls.frametime;
787 		if( con.currentHeight > con.destHeight ) {
788 			con.currentHeight = con.destHeight;
789 		}
790 	}
791 }
792 
793 /*
794 ==================
795 SCR_DrawConsole
796 ==================
797 */
Con_DrawConsole(void)798 void Con_DrawConsole( void ) {
799 	Cvar_ClampValue( con_scale, 1, 9 );
800 
801 	con.scale = 1.0f / con_scale->value;
802 	ref.SetScale( &con.scale );
803 
804 	Con_CheckResize();
805 	Con_DrawSolidConsole();
806 	Con_DrawNotify();
807 
808 	ref.SetScale( NULL );
809 
810 }
811 
812 
813 /*
814 ==============================================================================
815 
816 			LINE TYPING INTO THE CONSOLE AND COMMAND COMPLETION
817 
818 ==============================================================================
819 */
820 
821 /*
822 ====================
823 Key_Console
824 
825 Interactive line editing and console scrollback
826 ====================
827 */
Key_Console(int key)828 void Key_Console( int key ) {
829 	if( key == 'l' && Key_IsDown( K_CTRL ) ) {
830 		Con_Clear_f();
831 		return;
832 	}
833 
834 	if( key == K_ENTER || key == K_KP_ENTER ) {
835 		char *cmd;
836 
837 		if( !( cmd = Prompt_Action( &con.prompt ) ) ) {
838 			Con_Printf( "]\n" );
839 			return;
840 		}
841 
842 		// backslash text are commands, else chat
843 		if( cmd[0] == '\\' || cmd[0] == '/' ) {
844 			Cbuf_AddText( cmd + 1 );	// skip slash
845 		} else {
846 			if( cls.state == ca_active && con_chat->integer ) {
847 				Cbuf_AddText( va( "cmd say \"%s\"", cmd ) );
848 			} else {
849 				Cbuf_AddText( cmd );
850 			}
851 		}
852 		Cbuf_AddText( "\n" );
853 
854 		Con_Printf( "]%s\n", cmd );
855 
856 		if( cls.state == ca_disconnected ) {
857 			SCR_UpdateScreen ();	// force an update, because the command
858 									// may take some time
859 		}
860 		return;
861 	}
862 
863 	if( key == K_TAB ) {
864 		Prompt_CompleteCommand( &con.prompt, qtrue );
865 		return;
866 	}
867 
868 	if( key == K_UPARROW ||
869             ( key == 'p' && Key_IsDown( K_CTRL ) ) )
870     {
871 		Prompt_HistoryUp( &con.prompt );
872 		return;
873 	}
874 
875 	if( key == K_DOWNARROW ||
876             ( key == 'n' && Key_IsDown( K_CTRL ) ) )
877     {
878 		Prompt_HistoryDown( &con.prompt );
879 		return;
880 	}
881 
882 
883 	if( key == K_PGUP || key == K_MWHEELUP ) {
884 		if( Key_IsDown( K_CTRL ) ) {
885 			con.display -= 6;
886 		} else {
887 			con.display -= 2;
888 		}
889 
890 		if( con.display < 1 ) {
891 			con.display = 1;
892 		}
893 		return;
894 	}
895 
896 	if( key == K_PGDN || key == K_MWHEELDOWN ) {
897 		if( Key_IsDown( K_CTRL ) ) {
898 			con.display += 6;
899 		} else {
900 			con.display += 2;
901 		}
902 		if( con.display > con.current ) {
903 			con.display = con.current;
904 		}
905 		return;
906 	}
907 
908 
909 	if( key == K_HOME && Key_IsDown( K_CTRL ) ) {
910 		con.display = 1;
911 		return;
912 	}
913 
914 	if( key == K_END && Key_IsDown( K_CTRL ) ) {
915 		con.display = con.current;
916 		return;
917 	}
918 
919 	IF_KeyEvent( &con.prompt.inputLine, key );
920 
921 }
922 
Char_Console(int key)923 void Char_Console( int key ) {
924 	IF_CharEvent( &con.prompt.inputLine, key );
925 }
926 
927 /*
928 ====================
929 Key_Message
930 ====================
931 */
Key_Message(int key)932 void Key_Message( int key ) {
933 	if( key == 'l' && Key_IsDown( K_CTRL ) ) {
934 		IF_Clear( &con.chatField );
935 		return;
936 	}
937 
938 	if( key == K_ENTER || key == K_KP_ENTER ) {
939 		Cbuf_AddText( con.chatTeam ? "say_team \"" : "say \"" );
940 		Cbuf_AddText( con.chatField.text );
941 		Cbuf_AddText( "\"\n" );
942 
943 		Key_SetDest( cls.key_dest & ~KEY_MESSAGE );
944 		IF_Clear( &con.chatField );
945 		return;
946 	}
947 
948 	if( key == K_ESCAPE ) {
949 		Key_SetDest( cls.key_dest & ~KEY_MESSAGE );
950 		IF_Clear( &con.chatField );
951 		return;
952 	}
953 
954 	IF_KeyEvent( &con.chatField, key );
955 }
956 
Char_Message(int key)957 void Char_Message( int key ) {
958 	IF_CharEvent( &con.chatField, key );
959 }
960 
961 
962 
963