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