]> icculus.org git repositories - divverent/darkplaces.git/blob - console.c
NetConn_ReceivedMessage now verifies that the packet's internal length value matches...
[divverent/darkplaces.git] / console.c
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 #if !defined(WIN32) || defined(__MINGW32__)
23 # include <unistd.h>
24 #endif
25 #include <time.h>
26 #include "quakedef.h"
27
28 int con_linewidth;
29
30 float con_cursorspeed = 4;
31
32 #define         CON_TEXTSIZE    131072
33
34 // total lines in console scrollback
35 int con_totallines;
36 // lines up from bottom to display
37 int con_backscroll;
38 // where next message will be printed
39 int con_current;
40 // offset in current line for next print
41 int con_x;
42 char con_text[CON_TEXTSIZE];
43
44 //seconds
45 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3"};
46 cvar_t con_notify = {CVAR_SAVE, "con_notify","4"};
47
48 #define MAX_NOTIFYLINES 32
49 // cl.time time the line was generated for transparent notify lines
50 float con_times[MAX_NOTIFYLINES];
51
52 int con_vislines;
53
54 #define MAXCMDLINE      256
55 extern char key_lines[32][MAXCMDLINE];
56 extern int edit_line;
57 extern int key_linepos;
58 extern int key_insert;
59
60
61 qboolean con_initialized;
62
63
64 /*
65 ==============================================================================
66
67 LOGGING
68
69 ==============================================================================
70 */
71
72 cvar_t log_file = {0, "log_file",""};
73 char crt_log_file [MAX_OSPATH] = "";
74 qfile_t* logfile = NULL;
75
76 qbyte* logqueue = NULL;
77 size_t logq_ind = 0;
78 size_t logq_size = 0;
79
80 void Log_ConPrint (const char *msg);
81
82 /*
83 ====================
84 Log_Timestamp
85 ====================
86 */
87 const char* Log_Timestamp (const char *desc)
88 {
89         static char timestamp [128];
90         time_t crt_time;
91         const struct tm *crt_tm;
92         char timestring [64];
93
94         // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
95         time (&crt_time);
96         crt_tm = localtime (&crt_time);
97         strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
98
99         if (desc != NULL)
100                 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
101         else
102                 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
103
104         return timestamp;
105 }
106
107
108 /*
109 ====================
110 Log_Open
111 ====================
112 */
113 void Log_Open (void)
114 {
115         if (logfile != NULL || log_file.string[0] == '\0')
116                 return;
117
118         logfile = FS_Open (log_file.string, "ab", false, false);
119         if (logfile != NULL)
120         {
121                 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
122                 FS_Print (logfile, Log_Timestamp ("Log started"));
123         }
124 }
125
126
127 /*
128 ====================
129 Log_Close
130 ====================
131 */
132 void Log_Close (void)
133 {
134         if (logfile == NULL)
135                 return;
136
137         FS_Print (logfile, Log_Timestamp ("Log stopped"));
138         FS_Print (logfile, "\n");
139         FS_Close (logfile);
140
141         logfile = NULL;
142         crt_log_file[0] = '\0';
143 }
144
145
146 /*
147 ====================
148 Log_Start
149 ====================
150 */
151 void Log_Start (void)
152 {
153         Log_Open ();
154
155         // Dump the contents of the log queue into the log file and free it
156         if (logqueue != NULL)
157         {
158                 if (logfile != NULL && logq_ind != 0)
159                         FS_Write (logfile, logqueue, logq_ind);
160                 Mem_Free (logqueue);
161                 logqueue = NULL;
162                 logq_ind = 0;
163                 logq_size = 0;
164         }
165 }
166
167
168 /*
169 ================
170 Log_ConPrint
171 ================
172 */
173 void Log_ConPrint (const char *msg)
174 {
175         static qboolean inprogress = false;
176
177         // don't allow feedback loops with memory error reports
178         if (inprogress)
179                 return;
180         inprogress = true;
181
182         // Until the host is completely initialized, we maintain a log queue
183         // to store the messages, since the log can't be started before
184         if (logqueue != NULL)
185         {
186                 size_t remain = logq_size - logq_ind;
187                 size_t len = strlen (msg);
188
189                 // If we need to enlarge the log queue
190                 if (len > remain)
191                 {
192                         unsigned int factor = ((logq_ind + len) / logq_size) + 1;
193                         qbyte* newqueue;
194
195                         logq_size *= factor;
196                         newqueue = Mem_Alloc (tempmempool, logq_size);
197                         memcpy (newqueue, logqueue, logq_ind);
198                         Mem_Free (logqueue);
199                         logqueue = newqueue;
200                         remain = logq_size - logq_ind;
201                 }
202                 memcpy (&logqueue[logq_ind], msg, len);
203                 logq_ind += len;
204
205                 inprogress = false;
206                 return;
207         }
208
209         // Check if log_file has changed
210         if (strcmp (crt_log_file, log_file.string) != 0)
211         {
212                 Log_Close ();
213                 Log_Open ();
214         }
215
216         // If a log file is available
217         if (logfile != NULL)
218                 FS_Print (logfile, msg);
219         inprogress = false;
220 }
221
222
223 /*
224 ================
225 Log_Printf
226 ================
227 */
228 void Log_Printf (const char *logfilename, const char *fmt, ...)
229 {
230         qfile_t *file;
231
232         file = FS_Open (logfilename, "ab", true, false);
233         if (file != NULL)
234         {
235                 va_list argptr;
236
237                 va_start (argptr, fmt);
238                 FS_VPrintf (file, fmt, argptr);
239                 va_end (argptr);
240
241                 FS_Close (file);
242         }
243 }
244
245
246 /*
247 ==============================================================================
248
249 CONSOLE
250
251 ==============================================================================
252 */
253
254 /*
255 ================
256 Con_ToggleConsole_f
257 ================
258 */
259 void Con_ToggleConsole_f (void)
260 {
261         // toggle the 'user wants console' bit
262         key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
263         memset (con_times, 0, sizeof(con_times));
264 }
265
266 /*
267 ================
268 Con_Clear_f
269 ================
270 */
271 void Con_Clear_f (void)
272 {
273         if (con_text)
274                 memset (con_text, ' ', CON_TEXTSIZE);
275 }
276
277
278 /*
279 ================
280 Con_ClearNotify
281 ================
282 */
283 void Con_ClearNotify (void)
284 {
285         int i;
286
287         for (i=0 ; i<MAX_NOTIFYLINES ; i++)
288                 con_times[i] = 0;
289 }
290
291
292 /*
293 ================
294 Con_MessageMode_f
295 ================
296 */
297 void Con_MessageMode_f (void)
298 {
299         key_dest = key_message;
300         chat_team = false;
301 }
302
303
304 /*
305 ================
306 Con_MessageMode2_f
307 ================
308 */
309 void Con_MessageMode2_f (void)
310 {
311         key_dest = key_message;
312         chat_team = true;
313 }
314
315
316 /*
317 ================
318 Con_CheckResize
319
320 If the line width has changed, reformat the buffer.
321 ================
322 */
323 void Con_CheckResize (void)
324 {
325         int i, j, width, oldwidth, oldtotallines, numlines, numchars;
326         char tbuf[CON_TEXTSIZE];
327
328         width = (vid_conwidth.integer >> 3);
329
330         if (width == con_linewidth)
331                 return;
332
333         oldwidth = con_linewidth;
334         con_linewidth = width;
335         oldtotallines = con_totallines;
336         con_totallines = CON_TEXTSIZE / con_linewidth;
337         numlines = oldtotallines;
338
339         if (con_totallines < numlines)
340                 numlines = con_totallines;
341
342         numchars = oldwidth;
343
344         if (con_linewidth < numchars)
345                 numchars = con_linewidth;
346
347         memcpy (tbuf, con_text, CON_TEXTSIZE);
348         memset (con_text, ' ', CON_TEXTSIZE);
349
350         for (i=0 ; i<numlines ; i++)
351         {
352                 for (j=0 ; j<numchars ; j++)
353                 {
354                         con_text[(con_totallines - 1 - i) * con_linewidth + j] =
355                                         tbuf[((con_current - i + oldtotallines) %
356                                                   oldtotallines) * oldwidth + j];
357                 }
358         }
359
360         Con_ClearNotify ();
361
362         con_backscroll = 0;
363         con_current = con_totallines - 1;
364 }
365
366 /*
367 ================
368 Con_Init
369 ================
370 */
371 void Con_Init (void)
372 {
373         memset (con_text, ' ', CON_TEXTSIZE);
374         con_linewidth = 80;
375         con_totallines = CON_TEXTSIZE / con_linewidth;
376
377         // Allocate a log queue
378         logq_size = 512;
379         logqueue = Mem_Alloc (tempmempool, logq_size);
380         logq_ind = 0;
381
382         Cvar_RegisterVariable (&log_file);
383
384         // support for the classic Quake option
385 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
386         if (COM_CheckParm ("-condebug") != 0)
387                 Cvar_SetQuick (&log_file, "qconsole.log");
388 }
389
390 void Con_Init_Commands (void)
391 {
392         // register our cvars
393         Cvar_RegisterVariable (&con_notifytime);
394         Cvar_RegisterVariable (&con_notify);
395
396         // register our commands
397         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
398         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
399         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
400         Cmd_AddCommand ("clear", Con_Clear_f);
401
402         con_initialized = true;
403         Con_Print("Console initialized.\n");
404 }
405
406
407 /*
408 ===============
409 Con_Linefeed
410 ===============
411 */
412 void Con_Linefeed (void)
413 {
414         if (con_backscroll)
415                 con_backscroll++;
416
417         con_x = 0;
418         con_current++;
419         memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
420 }
421
422 /*
423 ================
424 Con_PrintToHistory
425
426 Handles cursor positioning, line wrapping, etc
427 All console printing must go through this in order to be displayed
428 If no console is visible, the notify window will pop up.
429 ================
430 */
431 void Con_PrintToHistory(const char *txt, int mask)
432 {
433         int y, c, l;
434         static int cr;
435
436         while ( (c = *txt) )
437         {
438         // count word length
439                 for (l=0 ; l< con_linewidth ; l++)
440                         if ( txt[l] <= ' ')
441                                 break;
442
443         // word wrap
444                 if (l != con_linewidth && (con_x + l > con_linewidth) )
445                         con_x = 0;
446
447                 txt++;
448
449                 if (cr)
450                 {
451                         con_current--;
452                         cr = false;
453                 }
454
455
456                 if (!con_x)
457                 {
458                         Con_Linefeed ();
459                 // mark time for transparent overlay
460                         if (con_current >= 0)
461                         {
462                                 if (con_notify.integer < 0)
463                                         Cvar_SetValueQuick(&con_notify, 0);
464                                 if (con_notify.integer > MAX_NOTIFYLINES)
465                                         Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
466                                 if (con_notify.integer > 0)
467                                         con_times[con_current % con_notify.integer] = cl.time;
468                         }
469                 }
470
471                 switch (c)
472                 {
473                 case '\n':
474                         con_x = 0;
475                         break;
476
477                 case '\r':
478                         con_x = 0;
479                         cr = 1;
480                         break;
481
482                 default:        // display character and advance
483                         y = con_current % con_totallines;
484                         con_text[y*con_linewidth+con_x] = c | mask;
485                         con_x++;
486                         if (con_x >= con_linewidth)
487                                 con_x = 0;
488                         break;
489                 }
490
491         }
492 }
493
494 /* The translation table between the graphical font and plain ASCII  --KB */
495 static char qfont_table[256] = {
496         '\0', '#',  '#',  '#',  '#',  '.',  '#',  '#',
497         '#',  9,    10,   '#',  ' ',  13,   '.',  '.',
498         '[',  ']',  '0',  '1',  '2',  '3',  '4',  '5',
499         '6',  '7',  '8',  '9',  '.',  '<',  '=',  '>',
500         ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'',
501         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
502         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
503         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
504         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
505         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
506         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
507         'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
508         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
509         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
510         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
511         'x',  'y',  'z',  '{',  '|',  '}',  '~',  '<',
512
513         '<',  '=',  '>',  '#',  '#',  '.',  '#',  '#',
514         '#',  '#',  ' ',  '#',  ' ',  '>',  '.',  '.',
515         '[',  ']',  '0',  '1',  '2',  '3',  '4',  '5',
516         '6',  '7',  '8',  '9',  '.',  '<',  '=',  '>',
517         ' ',  '!',  '"',  '#',  '$',  '%',  '&',  '\'',
518         '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
519         '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',
520         '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
521         '@',  'A',  'B',  'C',  'D',  'E',  'F',  'G',
522         'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
523         'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',
524         'X',  'Y',  'Z',  '[',  '\\', ']',  '^',  '_',
525         '`',  'a',  'b',  'c',  'd',  'e',  'f',  'g',
526         'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
527         'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
528         'x',  'y',  'z',  '{',  '|',  '}',  '~',  '<'
529 };
530
531 /*
532 ================
533 Con_Print
534
535 Prints to all appropriate console targets, and adds timestamps
536 ================
537 */
538 extern cvar_t timestamps;
539 extern cvar_t timeformat;
540 extern qboolean sys_nostdout;
541 void Con_Print(const char *msg)
542 {
543         int mask = 0;
544         static int index = 0;
545         static char line[16384];
546
547         for (;*msg;msg++)
548         {
549                 if (index == 0)
550                 {
551                         // if this is the beginning of a new line, print timestamp
552                         char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
553                         // special color codes for chat messages must always come first
554                         // for Con_PrintToHistory to work properly
555                         if (*msg <= 2)
556                         {
557                                 if (*msg == 1)
558                                 {
559                                         // play talk wav
560                                         S_LocalSound ("sound/misc/talk.wav");
561                                 }
562                                 if (gamemode == GAME_NEXUIZ)
563                                 {
564                                         line[index++] = '^';
565                                         line[index++] = '3';
566                                 }
567                                 else
568                                 {
569                                         // go to colored text
570                                         mask = 128;
571                                 }
572                                 msg++;
573                         }
574                         // store timestamp
575                         for (;*timestamp;index++, timestamp++)
576                                 if (index < sizeof(line) - 2)
577                                         line[index] = *timestamp;
578                 }
579                 // append the character
580                 line[index++] = *msg;
581                 // if this is a newline character, we have a complete line to print
582                 if (*msg == '\n' || index >= 16000)
583                 {
584                         // terminate the line
585                         line[index] = 0;
586                         // send to log file
587                         Log_ConPrint(line);
588                         // send to terminal or dedicated server window
589                         if (!sys_nostdout)
590                         {
591                                 unsigned char *p;
592                                 for (p = (unsigned char *) line;*p; p++)
593                                         *p = qfont_table[*p];
594                                 Sys_PrintToTerminal(line);
595                         }
596                         // send to scrollable buffer
597                         if (con_initialized && cls.state != ca_dedicated)
598                                 Con_PrintToHistory(line, mask);
599                         // empty the line buffer
600                         index = 0;
601                 }
602         }
603 }
604
605
606 // LordHavoc: increased from 4096 to 16384
607 #define MAXPRINTMSG     16384
608
609 /*
610 ================
611 Con_Printf
612
613 Prints to all appropriate console targets
614 ================
615 */
616 void Con_Printf(const char *fmt, ...)
617 {
618         va_list argptr;
619         char msg[MAXPRINTMSG];
620
621         va_start(argptr,fmt);
622         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
623         va_end(argptr);
624
625         Con_Print(msg);
626 }
627
628 /*
629 ================
630 Con_DPrint
631
632 A Con_Print that only shows up if the "developer" cvar is set
633 ================
634 */
635 void Con_DPrint(const char *msg)
636 {
637         if (!developer.integer)
638                 return;                 // don't confuse non-developers with techie stuff...
639         Con_Print(msg);
640 }
641
642 /*
643 ================
644 Con_DPrintf
645
646 A Con_Printf that only shows up if the "developer" cvar is set
647 ================
648 */
649 void Con_DPrintf(const char *fmt, ...)
650 {
651         va_list argptr;
652         char msg[MAXPRINTMSG];
653
654         if (!developer.integer)
655                 return;                 // don't confuse non-developers with techie stuff...
656
657         va_start(argptr,fmt);
658         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
659         va_end(argptr);
660
661         Con_Print(msg);
662 }
663
664
665 /*
666 ==============================================================================
667
668 DRAWING
669
670 ==============================================================================
671 */
672
673 static vec4_t _con_colors[] =
674 {
675         // Quake3 colors
676         // LordHavoc: why on earth is cyan before magenta in Quake3?
677         // LordHavoc: note: Doom3 uses white for [0] and [7]
678         {0.0, 0.0, 0.0, 1.0}, // black
679         {1.0, 0.0, 0.0, 1.0}, // red
680         {0.0, 1.0, 0.0, 1.0}, // green
681         {1.0, 1.0, 0.0, 1.0}, // yellow
682         {0.0, 0.0, 1.0, 1.0}, // blue
683         {0.0, 1.0, 1.0, 1.0}, // cyan
684         {1.0, 0.0, 1.0, 1.0}, // magenta
685         {1.0, 1.0, 1.0, 1.0}  // white
686         // Black's color table
687         //{1.0, 1.0, 1.0, 1.0},
688         //{1.0, 0.0, 0.0, 1.0},
689         //{0.0, 1.0, 0.0, 1.0},
690         //{0.0, 0.0, 1.0, 1.0},
691         //{1.0, 1.0, 0.0, 1.0},
692         //{0.0, 1.0, 1.0, 1.0},
693         //{1.0, 0.0, 1.0, 1.0},
694         //{0.1, 0.1, 0.1, 1.0}
695 };
696
697 #define _con_colors_count       (sizeof(_con_colors) / sizeof(vec3_t))
698 #define _con_color_tag          '^'
699
700 // color is read and changed in the end
701 static void _Con_DrawString( float x, float y, const char *text, int maxlen, float scalex, float scaley, int flags )
702 {
703         vec_t *color;
704         const char *first, *last;
705         int len;
706
707         color = _con_colors[7];
708         if( maxlen < 1)
709                 len = strlen( text );
710         else
711                 len = min( maxlen, (signed) strlen( text ));
712
713     first = last = text;
714         while( 1 ) {
715                 // iterate until we get the next color tag or reach the end of the text part to draw
716                 for( ; len && *last != _con_color_tag ; len--, last++ )
717                         ;
718                 // only draw the partial string if we have read anything
719                 if( last != first ) {
720                         // draw the string
721                         DrawQ_String( x, y, first, last - first, scalex, scaley, color[0], color[1], color[2], color[3], flags );
722                         // update x to be at the new start position
723                         x += (last - first) * scalex;
724                         // if we have reached the end, we have finished
725                         if( !len )
726                                 break;
727                 }
728                 first = last;
729                 // jump over the tag
730                 last++;
731                 len--;
732                 if( len && '0' <= *last && *last <= '9' ) {
733                         int index = 0;
734
735                         while( '0' <= *last && *last <= '9' && len ) {
736                                 index = index * 10 + *last - '0';
737                                 if( index < _con_colors_count ) {
738                                         last++;
739                                         len--;
740                                 } else {
741                                         index /= 10;
742                                         break;
743                                 }
744                         }
745
746                         color = _con_colors[index];
747                         // we dont want to display the color tag and the color index
748                         first = last;
749                 }
750         }
751 }
752
753 /*
754 ================
755 Con_DrawInput
756
757 The input line scrolls horizontally if typing goes beyond the right edge
758
759 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
760 ================
761 */
762 void Con_DrawInput (void)
763 {
764         int             y;
765         int             i;
766         char editlinecopy[257], *text;
767
768         if (!key_consoleactive)
769                 return;         // don't draw anything
770
771         text = strcpy(editlinecopy, key_lines[edit_line]);
772
773         // Advanced Console Editing by Radix radix@planetquake.com
774         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
775         // use strlen of edit_line instead of key_linepos to allow editing
776         // of early characters w/o erasing
777
778         y = strlen(text);
779
780 // fill out remainder with spaces
781         for (i = y; i < 256; i++)
782                 text[i] = ' ';
783
784         // add the cursor frame
785         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
786                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
787
788 //      text[key_linepos + 1] = 0;
789
790         // prestep if horizontally scrolling
791         if (key_linepos >= con_linewidth)
792                 text += 1 + key_linepos - con_linewidth;
793
794         // draw it
795         _Con_DrawString(0, con_vislines - 16, text, con_linewidth, 8, 8, 0);
796
797         // remove cursor
798 //      key_lines[edit_line][key_linepos] = 0;
799 }
800
801
802 /*
803 ================
804 Con_DrawNotify
805
806 Draws the last few lines of output transparently over the game top
807 ================
808 */
809 void Con_DrawNotify (void)
810 {
811         int             x, v;
812         char    *text;
813         int             i;
814         float   time;
815         extern char chat_buffer[];
816         char    temptext[256];
817
818         if (con_notify.integer < 0)
819                 Cvar_SetValueQuick(&con_notify, 0);
820         if (con_notify.integer > MAX_NOTIFYLINES)
821                 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
822         if (gamemode == GAME_TRANSFUSION)
823                 v = 8;
824         else
825                 v = 0;
826         for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
827         {
828                 if (i < 0)
829                         continue;
830                 time = con_times[i % con_notify.integer];
831                 if (time == 0)
832                         continue;
833                 time = cl.time - time;
834                 if (time > con_notifytime.value)
835                         continue;
836                 text = con_text + (i % con_totallines)*con_linewidth;
837
838                 if (gamemode == GAME_NEXUIZ) {
839                         int linewidth;
840
841                         for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
842                         x = (vid_conwidth.integer - linewidth * 8) / 2;
843                 } else
844                         x = 0;
845
846                 _Con_DrawString(x, v, text, con_linewidth, 8, 8, 0);
847
848                 v += 8;
849         }
850
851
852         if (key_dest == key_message)
853         {
854                 x = 0;
855
856                 // LordHavoc: speedup, and other improvements
857                 if (chat_team)
858                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
859                 else
860                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
861                 while (strlen(temptext) >= (size_t) con_linewidth)
862                 {
863                         _Con_DrawString (0, v, temptext, con_linewidth, 8, 8, 0);
864                         strcpy(temptext, &temptext[con_linewidth]);
865                         v += 8;
866                 }
867                 if (strlen(temptext) > 0)
868                 {
869                         _Con_DrawString (0, v, temptext, 0, 8, 8, 0);
870                         v += 8;
871                 }
872         }
873 }
874
875 /*
876 ================
877 Con_DrawConsole
878
879 Draws the console with the solid background
880 The typing input line at the bottom should only be drawn if typing is allowed
881 ================
882 */
883 extern char engineversion[40];
884 void Con_DrawConsole (int lines)
885 {
886         int i, y, rows, j;
887         char *text;
888
889         if (lines <= 0)
890                 return;
891
892 // draw the background
893         if (scr_conbrightness.value >= 0.01f)
894                 DrawQ_Pic(0, lines - vid_conheight.integer, "gfx/conback", vid_conwidth.integer, vid_conheight.integer, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, scr_conalpha.value, 0);
895         else
896                 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
897         DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
898
899 // draw the text
900         con_vislines = lines;
901
902         rows = (lines-16)>>3;           // rows of text to draw
903         y = lines - 16 - (rows<<3);     // may start slightly negative
904
905         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
906         {
907                 j = max(i - con_backscroll, 0);
908                 text = con_text + (j % con_totallines)*con_linewidth;
909
910                 _Con_DrawString(0, y, text, con_linewidth, 8, 8, 0);
911         }
912
913 // draw the input prompt, user text, and cursor if desired
914         Con_DrawInput ();
915 }
916
917 /*
918         Con_DisplayList
919
920         New function for tab-completion system
921         Added by EvilTypeGuy
922         MEGA Thanks to Taniwha
923
924 */
925 void Con_DisplayList(const char **list)
926 {
927         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
928         const char **walk = list;
929
930         while (*walk) {
931                 len = strlen(*walk);
932                 if (len > maxlen)
933                         maxlen = len;
934                 walk++;
935         }
936         maxlen += 1;
937
938         while (*list) {
939                 len = strlen(*list);
940                 if (pos + maxlen >= width) {
941                         Con_Print("\n");
942                         pos = 0;
943                 }
944
945                 Con_Print(*list);
946                 for (i = 0; i < (maxlen - len); i++)
947                         Con_Print(" ");
948
949                 pos += maxlen;
950                 list++;
951         }
952
953         if (pos)
954                 Con_Print("\n\n");
955 }
956
957 /*
958         Con_CompleteCommandLine
959
960         New function for tab-completion system
961         Added by EvilTypeGuy
962         Thanks to Fett erich@heintz.com
963         Thanks to taniwha
964
965 */
966 void Con_CompleteCommandLine (void)
967 {
968         const char *cmd = "", *s;
969         const char **list[3] = {0, 0, 0};
970         int c, v, a, i, cmd_len;
971
972         s = key_lines[edit_line] + 1;
973         // Count number of possible matches
974         c = Cmd_CompleteCountPossible(s);
975         v = Cvar_CompleteCountPossible(s);
976         a = Cmd_CompleteAliasCountPossible(s);
977
978         if (!(c + v + a))       // No possible matches
979                 return;
980
981         if (c + v + a == 1) {
982                 if (c)
983                         list[0] = Cmd_CompleteBuildList(s);
984                 else if (v)
985                         list[0] = Cvar_CompleteBuildList(s);
986                 else
987                         list[0] = Cmd_CompleteAliasBuildList(s);
988                 cmd = *list[0];
989                 cmd_len = strlen (cmd);
990         } else {
991                 if (c)
992                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
993                 if (v)
994                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
995                 if (a)
996                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
997
998                 cmd_len = strlen (s);
999                 do {
1000                         for (i = 0; i < 3; i++) {
1001                                 char ch = cmd[cmd_len];
1002                                 const char **l = list[i];
1003                                 if (l) {
1004                                         while (*l && (*l)[cmd_len] == ch)
1005                                                 l++;
1006                                         if (*l)
1007                                                 break;
1008                                 }
1009                         }
1010                         if (i == 3)
1011                                 cmd_len++;
1012                 } while (i == 3);
1013                 // 'quakebar'
1014                 Con_Print("\n\35");
1015                 for (i = 0; i < con_linewidth - 4; i++)
1016                         Con_Print("\36");
1017                 Con_Print("\37\n");
1018
1019                 // Print Possible Commands
1020                 if (c) {
1021                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1022                         Con_DisplayList(list[0]);
1023                 }
1024
1025                 if (v) {
1026                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1027                         Con_DisplayList(list[1]);
1028                 }
1029
1030                 if (a) {
1031                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1032                         Con_DisplayList(list[2]);
1033                 }
1034         }
1035
1036         if (cmd) {
1037                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
1038                 key_linepos = cmd_len + 1;
1039                 if (c + v + a == 1) {
1040                         key_lines[edit_line][key_linepos] = ' ';
1041                         key_linepos++;
1042                 }
1043                 key_lines[edit_line][key_linepos] = 0;
1044         }
1045         for (i = 0; i < 3; i++)
1046                 if (list[i])
1047                         Mem_Free((void *)list[i]);
1048 }
1049