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