-The new Key_Event is enabled by default now and Ive added a check so the
[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                         // reset the color 
554                         // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
555                         line[index++] = STRING_COLOR_TAG;
556                         // assert( STRING_COLOR_DEFAULT < 10 )
557                         line[index++] = STRING_COLOR_DEFAULT + '0';
558                         // special color codes for chat messages must always come first
559                         // for Con_PrintToHistory to work properly
560                         if (*msg <= 2)
561                         {
562                                 if (*msg == 1)
563                                 {
564                                         // play talk wav
565                                         S_LocalSound ("sound/misc/talk.wav");
566                                 }
567                                 if (gamemode == GAME_NEXUIZ)
568                                 {
569                                         line[index++] = '^';
570                                         line[index++] = '3';
571                                 }
572                                 else
573                                 {
574                                         // go to colored text
575                                         mask = 128;
576                                 }
577                                 msg++;
578                         }
579                         // store timestamp
580                         for (;*timestamp;index++, timestamp++)
581                                 if (index < sizeof(line) - 2)
582                                         line[index] = *timestamp;
583                 }
584                 // append the character
585                 line[index++] = *msg;
586                 // if this is a newline character, we have a complete line to print
587                 if (*msg == '\n' || index >= 16000)
588                 {
589                         // terminate the line
590                         line[index] = 0;
591                         // send to log file
592                         Log_ConPrint(line);
593                         // send to scrollable buffer
594                         if (con_initialized && cls.state != ca_dedicated)
595                                 Con_PrintToHistory(line, mask);
596                         // send to terminal or dedicated server window
597                         if (!sys_nostdout)
598                         {
599                                 unsigned char *p;
600                                 for (p = (unsigned char *) line;*p; p++)
601                                         *p = qfont_table[*p];
602                                 Sys_PrintToTerminal(line);
603                         }
604                         // empty the line buffer
605                         index = 0;
606                 }
607         }
608 }
609
610
611 // LordHavoc: increased from 4096 to 16384
612 #define MAXPRINTMSG     16384
613
614 /*
615 ================
616 Con_Printf
617
618 Prints to all appropriate console targets
619 ================
620 */
621 void Con_Printf(const char *fmt, ...)
622 {
623         va_list argptr;
624         char msg[MAXPRINTMSG];
625
626         va_start(argptr,fmt);
627         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
628         va_end(argptr);
629
630         Con_Print(msg);
631 }
632
633 /*
634 ================
635 Con_DPrint
636
637 A Con_Print that only shows up if the "developer" cvar is set
638 ================
639 */
640 void Con_DPrint(const char *msg)
641 {
642         if (!developer.integer)
643                 return;                 // don't confuse non-developers with techie stuff...
644         Con_Print(msg);
645 }
646
647 /*
648 ================
649 Con_DPrintf
650
651 A Con_Printf that only shows up if the "developer" cvar is set
652 ================
653 */
654 void Con_DPrintf(const char *fmt, ...)
655 {
656         va_list argptr;
657         char msg[MAXPRINTMSG];
658
659         if (!developer.integer)
660                 return;                 // don't confuse non-developers with techie stuff...
661
662         va_start(argptr,fmt);
663         dpvsnprintf(msg,sizeof(msg),fmt,argptr);
664         va_end(argptr);
665
666         Con_Print(msg);
667 }
668
669
670 /*
671 ==============================================================================
672
673 DRAWING
674
675 ==============================================================================
676 */
677
678 /*
679 ================
680 Con_DrawInput
681
682 The input line scrolls horizontally if typing goes beyond the right edge
683
684 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
685 ================
686 */
687 void Con_DrawInput (void)
688 {
689         int             y;
690         int             i;
691         char editlinecopy[257], *text;
692
693         if (!key_consoleactive)
694                 return;         // don't draw anything
695
696         text = strcpy(editlinecopy, key_lines[edit_line]);
697
698         // Advanced Console Editing by Radix radix@planetquake.com
699         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
700         // use strlen of edit_line instead of key_linepos to allow editing
701         // of early characters w/o erasing
702
703         y = strlen(text);
704
705 // fill out remainder with spaces
706         for (i = y; i < 256; i++)
707                 text[i] = ' ';
708
709         // add the cursor frame
710         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
711                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
712
713 //      text[key_linepos + 1] = 0;
714
715         // prestep if horizontally scrolling
716         if (key_linepos >= con_linewidth)
717                 text += 1 + key_linepos - con_linewidth;
718
719         // draw it
720         DrawQ_ColoredString(0, con_vislines - 16, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, NULL );
721
722         // remove cursor
723 //      key_lines[edit_line][key_linepos] = 0;
724 }
725
726
727 /*
728 ================
729 Con_DrawNotify
730
731 Draws the last few lines of output transparently over the game top
732 ================
733 */
734 void Con_DrawNotify (void)
735 {
736         int             x, v;
737         char    *text;
738         int             i;
739         float   time;
740         extern char chat_buffer[];
741         char    temptext[256];
742         int colorindex = -1; //-1 for default
743
744         if (con_notify.integer < 0)
745                 Cvar_SetValueQuick(&con_notify, 0);
746         if (con_notify.integer > MAX_NOTIFYLINES)
747                 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
748         if (gamemode == GAME_TRANSFUSION)
749                 v = 8;
750         else
751                 v = 0;
752         for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
753         {
754
755                 if (i < 0)
756                         continue;
757                 time = con_times[i % con_notify.integer];
758                 if (time == 0)
759                         continue;
760                 time = cl.time - time;
761                 if (time > con_notifytime.value)
762                         continue;
763                 text = con_text + (i % con_totallines)*con_linewidth;
764
765                 if (gamemode == GAME_NEXUIZ) {
766                         int linewidth;
767
768                         for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
769                         x = (vid_conwidth.integer - linewidth * 8) / 2;
770                 } else
771                         x = 0;
772
773                 DrawQ_ColoredString( x, v, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex ); 
774                         
775                 v += 8;
776         }
777
778
779         if (key_dest == key_message)
780         {
781                 int colorindex = -1;
782
783                 x = 0;
784
785                 // LordHavoc: speedup, and other improvements
786                 if (chat_team)
787                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
788                 else
789                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
790                 while (strlen(temptext) >= (size_t) con_linewidth)
791                 {
792                         DrawQ_ColoredString( 0, v, temptext, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
793                         strcpy(temptext, &temptext[con_linewidth]);
794                         v += 8;
795                 }
796                 if (strlen(temptext) > 0)
797                 {
798                         DrawQ_ColoredString( 0, v, temptext, 0, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
799                         v += 8;
800                 }
801         }
802 }
803
804 /*
805 ================
806 Con_DrawConsole
807
808 Draws the console with the solid background
809 The typing input line at the bottom should only be drawn if typing is allowed
810 ================
811 */
812 extern char engineversion[40];
813 void Con_DrawConsole (int lines)
814 {
815         int i, y, rows, j;
816         char *text;
817         int colorindex = -1;
818
819         if (lines <= 0)
820                 return;
821
822 // draw the background
823         if (scr_conbrightness.value >= 0.01f)
824                 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);
825         else
826                 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
827         DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
828
829 // draw the text
830         con_vislines = lines;
831
832         rows = (lines-16)>>3;           // rows of text to draw
833         y = lines - 16 - (rows<<3);     // may start slightly negative
834
835         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
836         {
837                 j = max(i - con_backscroll, 0);
838                 text = con_text + (j % con_totallines)*con_linewidth;
839
840                 DrawQ_ColoredString( 0, y, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
841         }
842
843 // draw the input prompt, user text, and cursor if desired
844         Con_DrawInput ();
845 }
846
847 /*
848         Con_DisplayList
849
850         New function for tab-completion system
851         Added by EvilTypeGuy
852         MEGA Thanks to Taniwha
853
854 */
855 void Con_DisplayList(const char **list)
856 {
857         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
858         const char **walk = list;
859
860         while (*walk) {
861                 len = strlen(*walk);
862                 if (len > maxlen)
863                         maxlen = len;
864                 walk++;
865         }
866         maxlen += 1;
867
868         while (*list) {
869                 len = strlen(*list);
870                 if (pos + maxlen >= width) {
871                         Con_Print("\n");
872                         pos = 0;
873                 }
874
875                 Con_Print(*list);
876                 for (i = 0; i < (maxlen - len); i++)
877                         Con_Print(" ");
878
879                 pos += maxlen;
880                 list++;
881         }
882
883         if (pos)
884                 Con_Print("\n\n");
885 }
886
887 /*
888         Con_CompleteCommandLine
889
890         New function for tab-completion system
891         Added by EvilTypeGuy
892         Thanks to Fett erich@heintz.com
893         Thanks to taniwha
894
895 */
896 void Con_CompleteCommandLine (void)
897 {
898         const char *cmd = "", *s;
899         const char **list[3] = {0, 0, 0};
900         int c, v, a, i, cmd_len;
901
902         s = key_lines[edit_line] + 1;
903         // Count number of possible matches
904         c = Cmd_CompleteCountPossible(s);
905         v = Cvar_CompleteCountPossible(s);
906         a = Cmd_CompleteAliasCountPossible(s);
907
908         if (!(c + v + a))       // No possible matches
909                 return;
910
911         if (c + v + a == 1) {
912                 if (c)
913                         list[0] = Cmd_CompleteBuildList(s);
914                 else if (v)
915                         list[0] = Cvar_CompleteBuildList(s);
916                 else
917                         list[0] = Cmd_CompleteAliasBuildList(s);
918                 cmd = *list[0];
919                 cmd_len = strlen (cmd);
920         } else {
921                 if (c)
922                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
923                 if (v)
924                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
925                 if (a)
926                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
927
928                 cmd_len = strlen (s);
929                 do {
930                         for (i = 0; i < 3; i++) {
931                                 char ch = cmd[cmd_len];
932                                 const char **l = list[i];
933                                 if (l) {
934                                         while (*l && (*l)[cmd_len] == ch)
935                                                 l++;
936                                         if (*l)
937                                                 break;
938                                 }
939                         }
940                         if (i == 3)
941                                 cmd_len++;
942                 } while (i == 3);
943                 // 'quakebar'
944                 Con_Print("\n\35");
945                 for (i = 0; i < con_linewidth - 4; i++)
946                         Con_Print("\36");
947                 Con_Print("\37\n");
948
949                 // Print Possible Commands
950                 if (c) {
951                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
952                         Con_DisplayList(list[0]);
953                 }
954
955                 if (v) {
956                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
957                         Con_DisplayList(list[1]);
958                 }
959
960                 if (a) {
961                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
962                         Con_DisplayList(list[2]);
963                 }
964         }
965
966         if (cmd) {
967                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
968                 key_linepos = cmd_len + 1;
969                 if (c + v + a == 1) {
970                         key_lines[edit_line][key_linepos] = ' ';
971                         key_linepos++;
972                 }
973                 key_lines[edit_line][key_linepos] = 0;
974         }
975         for (i = 0; i < 3; i++)
976                 if (list[i])
977                         Mem_Free((void *)list[i]);
978 }
979