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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "client.h"
27 #include "ref_gl/qgl.h"
28 
29 
30 /* The console's main structure */
31 struct CON_console_s	CON_console;
32 
33 /* CVar for notifications display time */
34 cvar_t *		con_notifytime;
35 
36 
37 
38 /**************************************************************************/
39 /* VARIOUS COMMANDS                                                       */
40 /**************************************************************************/
41 
42 /*
43  * Show or hide the console.
44  */
CON_ToggleConsole(void)45 void CON_ToggleConsole( void )
46 {
47 	SCR_EndLoadingPlaque ();	// get rid of loading plaque
48 
49 	Key_ClearTyping ();
50 	CON_ClearNotify( );
51 
52 	if (cls.key_dest == key_console)
53 	{
54 		M_ForceMenuOff ();
55 		Cvar_Set ("paused", "0");
56 	}
57 	else
58 	{
59 		M_ForceMenuOff ();
60 		cls.key_dest = key_console;
61 
62 		if (Cvar_VariableValue ("maxclients") == 1
63 			&& Com_ServerState ())
64 			Cvar_Set ("paused", "1");
65 	}
66 }
67 
68 
69 /*
70  * Clear the chat area
71  */
_CON_ToggleChat(void)72 static void _CON_ToggleChat( void )
73 {
74 	Key_ClearTyping( );
75 
76 	if ( cls.key_dest == key_console ) {
77 		if ( cls.state == ca_active ) {
78 			M_ForceMenuOff( );
79 			cls.key_dest = key_game;
80 		}
81 	} else {
82 		cls.key_dest = key_console;
83 	}
84 
85 	CON_ClearNotify( );
86 }
87 
88 
89 /*
90  * Toggle the general chat input
91  */
_CON_GeneralChatInput(void)92 static void _CON_GeneralChatInput( void )
93 {
94 	chat_team = false;
95 	chat_irc = false;
96 	cls.key_dest = key_message;
97 	Cbuf_AddText("chatbubble\n");
98 	Cbuf_Execute ();
99 }
100 
101 
102 /*
103  * Toggle the team chat input
104  */
_CON_TeamChatInput(void)105 static void _CON_TeamChatInput( void )
106 {
107 	chat_team = true;
108 	chat_irc = false;
109 	cls.key_dest = key_message;
110 	Cbuf_AddText("chatbubble\n");
111 	Cbuf_Execute ();
112 }
113 
114 
115 /*
116  * Toggle the IRC input
117  */
_CON_IRCInput(void)118 static void _CON_IRCInput( void )
119 {
120 	chat_team = false;
121 	chat_irc = true;
122 	cls.key_dest = key_message;
123 }
124 
125 
126 
127 /**************************************************************************/
128 /* CONSOLE BUFFER ACCESS                                                  */
129 /**************************************************************************/
130 
131 /*
132  * Find the start of the current line
133  *
134  * Parameters:
135  *	line	the initial line to look from
136  *
137  * Returns:
138  *	the identifier of the current line in the buffer
139  */
_CON_FindLineStart(int line)140 static int _CON_FindLineStart( int line )
141 {
142 	line = ( line + CON_MAX_LINES ) % CON_MAX_LINES;
143 	while ( CON_console.lCount[ line ] == 0 ) {
144 		line = ( line - 1 + CON_MAX_LINES ) % CON_MAX_LINES;
145 	}
146 	return line;
147 }
148 
149 
150 /*
151  * Find the start of the next line
152  *
153  * Parameters:
154  *	line	the current location
155  *
156  * Returns:
157  *	the identifier of the next line in the buffer
158  */
_CON_FindNextLine(int line)159 static int _CON_FindNextLine( int line )
160 {
161 	line = ( line + CON_MAX_LINES ) % CON_MAX_LINES;
162 	if ( CON_console.lCount[ line ] == 0 ) {
163 		while ( CON_console.lCount[ line ] == 0 ) {
164 			line = ( line + 1 ) % CON_MAX_LINES;
165 		}
166 	} else {
167 		line = ( line + CON_console.lCount[ line ] ) % CON_MAX_LINES;
168 	}
169 	return line;
170 }
171 
172 
173 /*
174  * Find the start of the previous line
175  *
176  * Parameters:
177  *	line	the current location
178  *
179  * Returns:
180  *	the identifier of the previous line in the buffer
181  */
_CON_FindPreviousLine(int line)182 static int _CON_FindPreviousLine( int line )
183 {
184 	return _CON_FindLineStart( _CON_FindLineStart( line ) - 1 );
185 }
186 
187 
188 /*
189  * End the current line and start a new one
190  */
_CON_NewLine()191 static void _CON_NewLine( )
192 {
193 	CON_console.times[ CON_console.curTime ] = cls.realtime;
194 	CON_console.curTime = ( CON_console.curTime + 1 ) % CON_MAX_NOTIFY;
195 
196 	CON_console.curLine = ( CON_console.curLine + 1 ) % CON_MAX_LINES;
197 	CON_console.lines += 1 - CON_console.lCount[ CON_console.curLine ];
198 	CON_console.lCount[ CON_console.curLine ] = 1;
199 
200 	CON_console.offset = 0;
201 	memset( CON_console.text[ CON_console.curLine ] , 0 , CON_LINE_LENGTH );
202 
203 	CON_console.heights[ CON_console.curLine ] = 0;
204 }
205 
206 
207 /*
208  * Go back to the start of the current line
209  */
_CON_RestartLine()210 static void _CON_RestartLine( )
211 {
212 	CON_console.curLine = _CON_FindLineStart( CON_console.curLine );
213 	CON_console.offset = 0;
214 }
215 
216 
217 /*
218  * Append a character to the console buffer
219  *
220  * Parameters:
221  *	new_char	the character to append
222  */
_CON_AppendChar(char new_char)223 static void _CON_AppendChar( char new_char )
224 {
225 	int lStart = _CON_FindLineStart( CON_console.curLine );
226 
227 	CON_console.text[ CON_console.curLine ][ CON_console.offset ] = new_char;
228 	CON_console.offset = ( CON_console.offset + 1 ) % CON_LINE_LENGTH;
229 	CON_console.heights[ lStart ] = 0;
230 
231 	if ( CON_console.offset == 0 ) {
232 		// Start a new buffer line that is part of the current logical line
233 		CON_console.lCount[ lStart ] ++;
234 		CON_console.curLine = ( CON_console.curLine + 1 ) % CON_MAX_LINES;
235 		CON_console.lines += 1 - CON_console.lCount[ CON_console.curLine ];
236 		memset( CON_console.text[ CON_console.curLine ] , 0 , CON_LINE_LENGTH );
237 	}
238 }
239 
240 
241 /*
242  * Copy the specified logical line into a buffer
243  *
244  * If the current buffer line is full, the logical line will be extended to
245  * include the next buffer line.
246  *
247  * Parameters:
248  *	buffer		the buffer to copy to
249  *	line		the first buffer line of the logical line
250  *
251  * Notes:
252  *	The buffer should have a sufficient size, which can be computed
253  *		using lCount[ line ] * CON_LINE_LENGTH.
254  */
_CON_CopyLine(char * buffer,int line)255 static void _CON_CopyLine( char * buffer , int line )
256 {
257 	int len = CON_console.lCount[ line ];
258 	int i;
259 	for ( i = 0 ; i < len ; i ++ ) {
260 		memcpy( buffer , CON_console.text[ line ] , CON_LINE_LENGTH );
261 		buffer += CON_LINE_LENGTH;
262 		line = ( line + 1 ) % CON_MAX_LINES;
263 	}
264 }
265 
266 
267 
268 /**************************************************************************/
269 /* NOTIFICATIONS                                                          */
270 /**************************************************************************/
271 
272 /* Clear all notifications */
CON_ClearNotify()273 void CON_ClearNotify( )
274 {
275 	int		i;
276 	for ( i = 0 ; i < CON_MAX_NOTIFY ; i ++ ) {
277 		CON_console.times[ i ] = 0;
278 	}
279 }
280 
281 
282 /* Draw the few last lines of the console transparently over the game */
CON_DrawNotify()283 void CON_DrawNotify( )
284 {
285 	FNT_font_t		font;
286 	struct FNT_window_s	box;
287 	int			line;
288 	int			bLines;
289 	int			count;
290 	int			timeIndex;
291 
292 	font = FNT_AutoGet( CL_gameFont );
293 	box.x = 0;
294 	box.y = 0;
295 
296 	if (cls.key_dest == key_message) {
297 		const char *	to_draw;
298 		int		height;
299 
300 		if (chat_team) {
301 			to_draw = "say_team: ";
302 		} else if (chat_irc) {
303 			to_draw = "say_IRC: ";
304 		} else {
305 			to_draw = "say: ";
306 		}
307 		box.width = viddef.width;
308 		box.height = 0;
309 		FNT_BoundedPrint( font , to_draw , FNT_CMODE_NONE , FNT_ALIGN_LEFT , &box , FNT_colors[ 7 ] );
310 		height = box.height;
311 
312 		box.x = box.width + 1;
313 		box.width = viddef.width - box.width;
314 		box.height = 0;
315 		FNT_WrappedPrint( font , chat_buffer , FNT_CMODE_NONE , FNT_ALIGN_LEFT , 30 , &box , FNT_colors[ 7 ] );
316 
317 		box.x = 0;
318 		box.y = ( height > box.height ) ? height : box.height;
319 	}
320 
321 	// Find the first logical line to display
322 	line = _CON_FindLineStart( CON_console.curLine );
323 	bLines = CON_console.lines - CON_console.lCount[ line ];
324 	timeIndex = ( CON_console.curTime - 1 + CON_MAX_NOTIFY ) % CON_MAX_NOTIFY;
325 	for ( count = 0 ; count < CON_MAX_NOTIFY && bLines > 0 ; count ++ ) {
326 		int time = CON_console.times[ timeIndex ];
327 		if ( time == 0 || cls.realtime - time > con_notifytime->value * 1000 ) {
328 			break;
329 		}
330 		timeIndex = ( timeIndex - 1 + CON_MAX_NOTIFY ) % CON_MAX_NOTIFY;
331 
332 		line = _CON_FindPreviousLine( line );
333 		bLines -= CON_console.lCount[ line ];
334 	}
335 
336 	// No lines to display
337 	if ( count == 0 ) {
338 		return;
339 	}
340 
341 	// Display lines
342 	while ( count > 0 ) {
343 		box.width = viddef.width;
344 		box.height = 0;
345 		if ( CON_console.lCount[ line ] == 1 ) {
346 			FNT_WrappedPrint( font , CON_console.text[ line ] , FNT_CMODE_QUAKE_SRS , FNT_ALIGN_LEFT , 30 , &box , FNT_colors[ 7 ] );
347 		} else {
348 			char * buffer = Z_Malloc( CON_LINE_LENGTH * CON_console.lCount[ line ] );
349 			_CON_CopyLine( buffer , line );
350 			FNT_WrappedPrint( font , buffer , FNT_CMODE_QUAKE_SRS , FNT_ALIGN_LEFT , 30 , &box , FNT_colors[ 7 ] );
351 			Z_Free( buffer );
352 		}
353 		box.y += box.height;
354 
355 		line = _CON_FindNextLine( line );
356 		timeIndex = ( timeIndex + 1 ) % CON_MAX_NOTIFY;
357 		count --;
358 	}
359 }
360 
361 
362 
363 /**************************************************************************/
364 /* CONSOLE ACCESS FUNCTIONS                                               */
365 /**************************************************************************/
366 
367 /* Save the console contents out to a file. */
_CON_Dump(void)368 static void _CON_Dump( void )
369 {
370 	char	name[MAX_OSPATH];
371 	int	line , lastLine;
372 	FILE *	f;
373 
374 	if ( Cmd_Argc( ) != 2 ) {
375 		Com_Printf ("usage: condump <filename>\n");
376 		return;
377 	}
378 	Com_sprintf( name , sizeof( name ) , "%s/%s.txt" , FS_Gamedir( ) , Cmd_Argv( 1 ) );
379 
380 	Com_Printf( "Dumping console text to %s.\n" , name );
381 	FS_CreatePath( name );
382 	f = fopen( name, "w" );
383 	if (!f) {
384 		Com_Printf( "ERROR: couldn't open %s.\n" , name );
385 		return;
386 	}
387 
388 	lastLine = _CON_FindLineStart( CON_console.curLine );
389 	line = ( CON_console.curLine + 1 + CON_MAX_LINES - CON_console.lines ) % CON_MAX_LINES;
390 	while ( line != lastLine ) {
391 		if ( CON_console.text[ line ][ 0 ] != 0 ) {
392 			if ( CON_console.lCount[ line ] == 1 ) {
393 				fprintf( f , "%s\n" , CON_console.text[ line ] );
394 			} else {
395 				char * buffer = Z_Malloc( CON_LINE_LENGTH * CON_console.lCount[ line ] );
396 				_CON_CopyLine( buffer , line );
397 				fprintf( f , "%s\n" , buffer );
398 				Z_Free( buffer );
399 			}
400 		}
401 		line = _CON_FindNextLine( line );
402 	}
403 
404 	fclose( f );
405 }
406 
407 /* Clears the console */
CON_Clear(void)408 void CON_Clear( void )
409 {
410 	memset( &CON_console , 0 , sizeof( CON_console ) );
411 	CON_console.lines = 1;
412 	CON_console.lCount[ 0 ] = 1;
413 	CON_console.curLine = 0;
414 	CON_console.offset = 0;
415 	CON_console.initialised = true;
416 }
417 
418 
419 /* Initialise the console. */
CON_Initialise()420 void CON_Initialise( )
421 {
422 	CON_Clear( );
423 	Com_Printf( "Console initialized.\n" );
424 
425 	// Register CVars
426 	con_notifytime = Cvar_Get( "con_notifytime" , "3" , 0 );
427 
428 	// Register commands
429 	Cmd_AddCommand( "toggleconsole", CON_ToggleConsole );
430 	Cmd_AddCommand( "togglechat", _CON_ToggleChat );
431 	Cmd_AddCommand( "messagemode", _CON_GeneralChatInput );
432 	Cmd_AddCommand( "messagemode2", _CON_TeamChatInput );
433 	Cmd_AddCommand( "messagemode3", _CON_IRCInput );
434 	Cmd_AddCommand( "clear" , CON_Clear );
435 	Cmd_AddCommand( "condump" , _CON_Dump );
436 }
437 
438 
439 /* Add text to the console. */
CON_Print(const char * text)440 void CON_Print( const char * text )
441 {
442 	char curChar;
443 
444 	if ( ! CON_console.initialised ) {
445 		return;
446 	}
447 
448 	while ( ( curChar = *text ) != 0 ) {
449 		if ( curChar == '\r' ) {
450 			_CON_RestartLine( );
451 		} else if ( curChar == '\n' ) {
452 			_CON_NewLine( );
453 		} else {
454 			_CON_AppendChar( curChar );
455 		}
456 		text ++;
457 	}
458 }
459 
460 
461 
462 /**************************************************************************/
463 /* CONSOLE DISPLAY FUNCTIONS                                              */
464 /**************************************************************************/
465 
466 /*
467  * Compute the height of a logical console line and update the console's
468  * data structure accordingly.
469  *
470  * Parameters:
471  *	font	the current console font
472  *	line	the start of the logical line for which the computation will
473  *		be performed
474  */
_CON_ComputeLineHeight(FNT_font_t font,int line)475 static void _CON_ComputeLineHeight( FNT_font_t font , int line )
476 {
477 	if ( CON_console.text[ line ][ 0 ] == 0 ) {
478 		// Empty lines are a special case, as we don't need to draw them
479 		CON_console.heights[ line ] = font->size;
480 	} else {
481 		// "Print" the line outside of the screen
482 		struct FNT_window_s box;
483 		box.x = 0 , box.y = viddef.height;
484 		box.width = viddef.width - font->size * 5 , box.height = 0;
485 
486 		if ( CON_console.lCount[ line ] == 1 ) {
487 			FNT_WrappedPrint( font , CON_console.text[ line ] , FNT_CMODE_QUAKE_SRS ,
488 				FNT_ALIGN_LEFT , 0 , &box , FNT_colors[ 0 ] );
489 		} else {
490 			char * buffer = Z_Malloc( CON_console.lCount[ line ] * CON_LINE_LENGTH );
491 			_CON_CopyLine( buffer , line );
492 			FNT_WrappedPrint( font , buffer , FNT_CMODE_QUAKE_SRS ,
493 				FNT_ALIGN_LEFT , 0 , &box , FNT_colors[ 0 ] );
494 			Z_Free( buffer );
495 		}
496 
497 		if ( box.height == 0 ) {
498 			CON_console.heights[ line ] = font->size;
499 		} else {
500 			CON_console.heights[ line ] = box.height;
501 		}
502 	}
503 }
504 
505 
506 /*
507  * Compute the height of all active lines in the console.
508  *
509  * Parameters:
510  *	font	the console's current font
511  *
512  * Returns:
513  *	the total height of the console
514  */
_CON_ComputeHeight(FNT_font_t font)515 static unsigned int _CON_ComputeHeight( FNT_font_t font )
516 {
517 	int			line , lastLine;
518 	unsigned int		total = 0;
519 
520 	lastLine = _CON_FindLineStart( CON_console.curLine );
521 	line = ( CON_console.curLine + 1 + CON_MAX_LINES - CON_console.lines ) % CON_MAX_LINES;
522 	while ( 1 ) {
523 		if ( ! CON_console.heights[ line ] ) {
524 			_CON_ComputeLineHeight( font , line );
525 		}
526 
527 		total += CON_console.heights[ line ];
528 		if ( line == lastLine ) {
529 			break;
530 		}
531 		line = _CON_FindNextLine( line );
532 	}
533 
534 	return total;
535 }
536 
537 
538 /*
539  * Check for font or resolution changes.
540  *
541  * Parameters:
542  *	font	the console's current font
543  *
544  * Returns:
545  *	true if the resolution or fonts have changed, false otherwise
546  */
_CON_CheckResize(FNT_font_t font)547 static qboolean _CON_CheckResize( FNT_font_t font )
548 {
549 	return ( viddef.width != CON_console.pWidth || font->size != CON_console.pSize
550 		|| strcmp( font->face->name , CON_console.pFace ) );
551 }
552 
553 
554 /*
555  * Draw the console's text.
556  *
557  * Parameters:
558  *	font	the current console font
559  *	start	the vertical offset relative to the top of the console's
560  *		contents at which drawing is to begin
561  *	dHeight	the height of the display area
562  */
_CON_DrawConsoleText(FNT_font_t font,int start,int dHeight)563 static void _CON_DrawConsoleText(
564 		FNT_font_t		font ,
565 		int			start ,
566 		int			dHeight
567 	)
568 {
569 	struct FNT_window_s	box;
570 	int			line , lastLine , total;
571 
572 	qglScissor( 0 , viddef.height - dHeight , viddef.width , dHeight );
573 	qglEnable( GL_SCISSOR_TEST );
574 
575 	total = 0;
576 	lastLine = _CON_FindLineStart( CON_console.curLine );
577 	line = ( CON_console.curLine + 1 + CON_MAX_LINES - CON_console.lines ) % CON_MAX_LINES;
578 
579 	do {
580 		if ( total + CON_console.heights[ line ] >= start && CON_console.text[ line ][ 0 ] ) {
581 			box.x = font->size*2;
582 			box.y = total - start;
583 			box.width = viddef.width - font->size * 5;
584 			box.height = 0;
585 
586 			if ( CON_console.lCount[ line ] == 1 ) {
587 				FNT_WrappedPrint( font , CON_console.text[ line ] , FNT_CMODE_QUAKE_SRS ,
588 					FNT_ALIGN_LEFT , 0 , &box , FNT_colors[ 2 ] );
589 			} else {
590 				char * buffer = Z_Malloc( CON_console.lCount[ line ] * CON_LINE_LENGTH );
591 				_CON_CopyLine( buffer , line );
592 				FNT_WrappedPrint( font , buffer , FNT_CMODE_QUAKE_SRS ,
593 					FNT_ALIGN_LEFT , 0 , &box , FNT_colors[ 2 ] );
594 				Z_Free( buffer );
595 			}
596 		}
597 
598 		total += CON_console.heights[ line ];
599 		if ( line == lastLine ) {
600 			break;
601 		}
602 		line = _CON_FindNextLine( line );
603 	} while ( total - start < dHeight );
604 
605 	qglDisable( GL_SCISSOR_TEST );
606 }
607 
608 
609 /*
610  * Draw the scroller on the right of the console.
611  *
612  * For now this is not interactive, it only exists to indicate where in the
613  * console's contents the current view is located.
614  *
615  * This function uses Draw_Fill to generate the scroller's graphics. It uses
616  * the following fill colours: 15 (white, outside of the box), 201 (dark
617  * green, inside of the box) and 208 (bright green, selected area).
618  *
619  * Parameters:
620  *	font_size	The size of the font (used as the vertical offset and
621  *			the scroller's width)
622  *	text_height	Total height of the console's text contents.
623  *	display_height	Height of the viewing area.
624  */
_CON_DrawScroller(int font_size,int text_height,int display_height)625 static void _CON_DrawScroller(
626 		int	font_size ,
627 		int	text_height ,
628 		int	display_height )
629 {
630 	// FIXME: lots of copy-pasted code here. Doesn't matter, we will
631 	// eventually redo the whole console using the menu code.
632 
633 	int hStart = viddef.width - 3 * font_size;
634 	int vStart = font_size / 2;
635 	int tHeight = display_height - font_size;
636 	int bHeight , bStart;
637 
638 	Draw_AlphaStretchTilingPic (hStart, vStart, font_size, font_size, "menu/scroll_border_end", 1);
639 	Draw_AlphaStretchTilingPic (hStart, vStart+font_size, font_size, tHeight-vStart, "menu/scroll_border", 1);
640 	Draw_AlphaStretchTilingPic (hStart+font_size, vStart+tHeight+font_size, -font_size, -font_size, "menu/scroll_border_end", 1);
641 
642 	if ( display_height >= text_height )
643 	{
644 		// Fill whole bar
645 		bStart = vStart;
646 		bHeight = tHeight;
647 	}
648 	else
649 	{
650 		bHeight = tHeight * display_height / text_height;
651 		if ( bHeight < font_size )
652 			bHeight = font_size;
653 		// "Top" offset is height - dHeight, "bottom" offset is 0
654 		// "Top" bar location is vStart, bottom location is vStart + tHeight - bHeight.
655 		bStart = vStart + ( tHeight - bHeight )
656 			* ( CON_console.displayOffset - text_height + display_height )
657 			/ ( display_height - text_height );
658 	}
659 
660 	Draw_AlphaStretchTilingPic (hStart, bStart, font_size, font_size, "menu/scroll_cursor_end", 1);
661 	Draw_AlphaStretchTilingPic (hStart, bStart+font_size, font_size, bHeight-font_size, "menu/scroll_cursor", 1);
662 	Draw_AlphaStretchTilingPic (hStart+font_size, bStart+bHeight+font_size, -font_size, -font_size, "menu/scroll_cursor_end", 1);
663 }
664 
665 
666 /*
667  * Draw the console's input line.
668  *
669  * Parameters:
670  *	font	the current console font
671  *	inputY	the height at which the input line is located
672  */
_CON_DrawInputLine(FNT_font_t font,int inputY)673 static void _CON_DrawInputLine(
674 		FNT_font_t		font ,
675 		int			inputY )
676 {
677 	struct FNT_window_s	box;
678 	char			text[ MAXCMDLINE + 1 ];
679 	unsigned int		wToCursor;
680 	unsigned int		wAfterCursor;
681 	unsigned int		align;
682 	char			old;
683 
684 	if ( cls.key_dest == key_menu || ( cls.key_dest != key_console && cls.state == ca_active ) )
685 		return;
686 
687 	// Copy current line
688 	memcpy( text , key_lines[ edit_line ] , key_linelen );
689 	text[ key_linelen ] = 0;
690 
691 	// Determine width of text before the cursor ...
692 	old = text[ key_linepos ];
693 	text[ key_linepos ] = 0;
694 	box.x = 0;
695 	box.y = viddef.height;
696 	box.width = box.height = 0;
697 	FNT_BoundedPrint( font , text , FNT_CMODE_QUAKE_SRS , FNT_ALIGN_LEFT , &box , FNT_colors[ 2 ] );
698 	wToCursor = box.width;
699 	text[ key_linepos ] = old;
700 
701 	// ... and after the cursor
702 	if ( key_linelen > key_linepos ) {
703 		box.width = box.height = 0;
704 		FNT_BoundedPrint( font , text + key_linepos , FNT_CMODE_QUAKE_SRS , FNT_ALIGN_LEFT , &box , FNT_colors[ 2 ] );
705 		wAfterCursor = box.width;
706 	} else {
707 		wAfterCursor = 0;
708 	}
709 
710 	box.x = font->size;
711 	box.y = inputY;
712 	box.height = 0;
713 	text[ key_linepos ] = 0;
714 	if ( wToCursor + font->size * 5 < viddef.width ) {
715 		// There is enough space for the start of the line
716 		align = FNT_ALIGN_LEFT;
717 	} else {
718 		// Not enough space, print with right alignment
719 		wToCursor = viddef.width * 0.9;
720 		align = FNT_ALIGN_RIGHT;
721 	}
722 	box.width = wToCursor;
723 	FNT_BoundedPrint( font , text , FNT_CMODE_QUAKE_SRS , align , &box , FNT_colors[ 2 ] );
724 
725 	// Draw cursor
726 	if ( ( (int)( cls.realtime >> 8 ) & 1) != 0 ) {
727 		Draw_Fill (box.x + box.width + 1 , box.y + 1 , font->size - 2 , font->size - 2 , RGBA(0,1,0,1));
728 	}
729 
730 	// Draw whatever is after the cursor
731 	if ( wAfterCursor ) {
732 		text[ key_linepos ] = old;
733 		box.x += box.width + font->size;
734 		box.width = viddef.width - ( box.width + font->size * 5 );
735 		box.height = 0;
736 		FNT_BoundedPrint( font , text + key_linepos , FNT_CMODE_QUAKE_SRS , FNT_ALIGN_LEFT , &box , FNT_colors[ 2 ] );
737 	}
738 }
739 
740 
741 /*
742  * Draw the line at the bottom of the console.
743  *
744  * This line includes the version string and the current download status.
745  *
746  * Parameters:
747  *	y	vertical offset of the bottom of the console
748  *
749  * Returns:
750  *	vertical offset above the line at the bottom of the console
751  *
752  */
_CON_DrawConsoleBottom(int y)753 static int _CON_DrawConsoleBottom( int y )
754 {
755 	FNT_font_t	font;
756 	char		buffer[ MAX_STRING_CHARS ];
757 	int		kb , sz;
758 
759 	font = FNT_AutoGet( CL_gameFont );
760 	y -= font->size;
761 
762 	// Draw version string
763 	sz = sizeof( VERSION );
764 	FNT_RawPrint( font , VERSION , sz , false ,
765 		viddef.width - font->size * 5 * sz / 2 , y , FNT_colors[ 7 ] );
766 
767 	// Draw download status if needed
768 	if ( ! ( cls.download && ( kb = (int)ftell( cls.download ) / 1024 ) ) ) {
769 		return y;
770 	}
771 
772 	Com_sprintf( buffer , MAX_STRING_CHARS , "Downloading %s [%s] ... %dKB" ,
773 		cls.downloadname , ( cls.downloadhttp ? "HTTP" : "UDP" ), kb );
774 	FNT_RawPrint( font , buffer , strlen( buffer ) , false ,
775 		font->size * 3 , y , FNT_colors[ 3 ] );
776 
777 	return y;
778 }
779 
780 
781 /* Draw the console. */
CON_DrawConsole(float relSize)782 void CON_DrawConsole( float relSize )
783 {
784 	FNT_font_t		font;
785 	int			dHeight;
786 	int			height;
787 	int			start;
788 	int			fontSize;
789 
790 	// Don't draw if there's no video
791 	if ( viddef.width < 1 ) {
792 		return;
793 	}
794 
795 	// Check for changes and act accordingly
796 	font = FNT_AutoGet( CL_consoleFont );
797 	if ( _CON_CheckResize( font ) ) {
798 		CON_console.pWidth = viddef.width;
799 		CON_console.pSize = font->size;
800 		strcpy( CON_console.pFace , font->face->name );
801 		memset( CON_console.heights , 0 , sizeof( CON_console.heights ) );
802 	}
803 	fontSize = font->size;
804 
805 	// Compute display height and draw background
806 	dHeight = viddef.height * relSize;
807 
808 	// FIXME: lots of copy-pasted code here. Doesn't matter, we will
809 	// eventually redo the whole console using the menu code.
810 	{
811 		int _tile_w, _tile_h;
812 		float tile_w, tile_h;
813 
814 		// assume all tiles are the same size
815 		Draw_GetPicSize (&_tile_w, &_tile_h, "menu/m_topcorner" );
816 
817 		tile_w = (float)_tile_w/64.0*(float)font->size*4.0;
818 		tile_h = (float)_tile_h/64.0*(float)font->size*4.0;
819 
820 
821 		Draw_AlphaStretchTilingPic( -tile_w/4, dHeight-tile_h/2, tile_w, tile_h, "menu/m_bottomcorner", 1 );
822 		Draw_AlphaStretchTilingPic( viddef.width+tile_w/4, dHeight-tile_h/2, -tile_w, tile_h, "menu/m_bottomcorner", 1 );
823 
824 		Draw_AlphaStretchTilingPic( tile_w*0.75, dHeight-tile_h/2, viddef.width-tile_w*1.5, tile_h, "menu/m_bottom", 1 );
825 
826 		Draw_AlphaStretchTilingPic( -tile_w/4, 0, tile_w, dHeight-tile_h/2, "menu/m_side", 1 );
827 		Draw_AlphaStretchTilingPic( viddef.width+tile_w/4, 0, -tile_w, dHeight-tile_h/2, "menu/m_side", 1 );
828 		Draw_AlphaStretchTilingPic( tile_w*0.75, 0, viddef.width-tile_w, dHeight-tile_h/2, "menu/m_background", 1 );
829 	}
830 
831 	// Draw version string and download status
832 	dHeight = _CON_DrawConsoleBottom( dHeight ) - fontSize;
833 
834 	// Compute heights
835 	height = _CON_ComputeHeight( font );
836 	if ( height < dHeight ) {
837 		CON_console.displayOffset = 0;
838 	} else if ( CON_console.displayOffset > height - dHeight ) {
839 		CON_console.displayOffset = height - dHeight;
840 	}
841 	start = height - dHeight - CON_console.displayOffset;
842 
843 	// Draw console contents & input line
844 	_CON_DrawConsoleText( font , start , dHeight );
845 	_CON_DrawScroller( font->size , height , dHeight );
846 	_CON_DrawInputLine( font , dHeight );
847 }
848