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