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