2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
22 #if !defined(WIN32) || defined(__MINGW32__)
30 float con_cursorspeed = 4;
32 #define CON_TEXTSIZE 131072
34 // total lines in console scrollback
36 // lines up from bottom to display
38 // where next message will be printed
40 // offset in current line for next print
42 char con_text[CON_TEXTSIZE];
45 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3"};
46 cvar_t con_notify = {CVAR_SAVE, "con_notify","4"};
48 #define MAX_NOTIFYLINES 32
49 // cl.time time the line was generated for transparent notify lines
50 float con_times[MAX_NOTIFYLINES];
54 #define MAXCMDLINE 256
55 extern char key_lines[32][MAXCMDLINE];
57 extern int key_linepos;
58 extern int key_insert;
61 qboolean con_initialized;
65 ==============================================================================
69 ==============================================================================
72 cvar_t log_file = {0, "log_file",""};
73 char crt_log_file [MAX_OSPATH] = "";
74 qfile_t* logfile = NULL;
76 qbyte* logqueue = NULL;
80 void Log_ConPrint (const char *msg);
87 const char* Log_Timestamp (const char *desc)
89 static char timestamp [128];
91 const struct tm *crt_tm;
94 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
96 crt_tm = localtime (&crt_time);
97 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
100 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
102 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
115 // Allocate a log queue
117 logqueue = Mem_Alloc (tempmempool, logq_size);
120 Cvar_RegisterVariable (&log_file);
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");
136 if (logfile != NULL || log_file.string[0] == '\0')
139 logfile = FS_Open (log_file.string, "ab", false, false);
142 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
143 FS_Print (logfile, Log_Timestamp ("Log started"));
153 void Log_Close (void)
158 FS_Print (logfile, Log_Timestamp ("Log stopped"));
159 FS_Print (logfile, "\n");
163 crt_log_file[0] = '\0';
172 void Log_Start (void)
176 // Dump the contents of the log queue into the log file and free it
177 if (logqueue != NULL)
179 if (logfile != NULL && logq_ind != 0)
180 FS_Write (logfile, logqueue, logq_ind);
194 void Log_ConPrint (const char *msg)
196 static qboolean inprogress = false;
198 // don't allow feedback loops with memory error reports
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)
207 size_t remain = logq_size - logq_ind;
208 size_t len = strlen (msg);
210 // If we need to enlarge the log queue
213 unsigned int factor = ((logq_ind + len) / logq_size) + 1;
217 newqueue = Mem_Alloc (tempmempool, logq_size);
218 memcpy (newqueue, logqueue, logq_ind);
221 remain = logq_size - logq_ind;
223 memcpy (&logqueue[logq_ind], msg, len);
230 // Check if log_file has changed
231 if (strcmp (crt_log_file, log_file.string) != 0)
237 // If a log file is available
239 FS_Print (logfile, msg);
249 void Log_Printf (const char *logfilename, const char *fmt, ...)
253 file = FS_Open (logfilename, "ab", true, false);
258 va_start (argptr, fmt);
259 FS_VPrintf (file, fmt, argptr);
268 ==============================================================================
272 ==============================================================================
280 void Con_ToggleConsole_f (void)
282 // toggle the 'user wants console' bit
283 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
284 memset (con_times, 0, sizeof(con_times));
292 void Con_Clear_f (void)
295 memset (con_text, ' ', CON_TEXTSIZE);
304 void Con_ClearNotify (void)
308 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
318 void Con_MessageMode_f (void)
320 key_dest = key_message;
330 void Con_MessageMode2_f (void)
332 key_dest = key_message;
341 If the line width has changed, reformat the buffer.
344 void Con_CheckResize (void)
346 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
347 char tbuf[CON_TEXTSIZE];
349 width = (vid.conwidth >> 3);
351 if (width == con_linewidth)
354 if (width < 1) // video hasn't been initialized yet
357 con_linewidth = width;
358 con_totallines = CON_TEXTSIZE / con_linewidth;
359 memset (con_text, ' ', CON_TEXTSIZE);
363 oldwidth = con_linewidth;
364 con_linewidth = width;
365 oldtotallines = con_totallines;
366 con_totallines = CON_TEXTSIZE / con_linewidth;
367 numlines = oldtotallines;
369 if (con_totallines < numlines)
370 numlines = con_totallines;
374 if (con_linewidth < numchars)
375 numchars = con_linewidth;
377 memcpy (tbuf, con_text, CON_TEXTSIZE);
378 memset (con_text, ' ', CON_TEXTSIZE);
380 for (i=0 ; i<numlines ; i++)
382 for (j=0 ; j<numchars ; j++)
384 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
385 tbuf[((con_current - i + oldtotallines) %
386 oldtotallines) * oldwidth + j];
394 con_current = con_totallines - 1;
404 memset (con_text, ' ', CON_TEXTSIZE);
408 // register our cvars
409 Cvar_RegisterVariable (&con_notifytime);
410 Cvar_RegisterVariable (&con_notify);
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);
418 con_initialized = true;
419 Con_Print("Console initialized.\n");
428 void Con_Linefeed (void)
435 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
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.
447 void Con_PrintToHistory(const char *txt, int mask)
455 for (l=0 ; l< con_linewidth ; l++)
460 if (l != con_linewidth && (con_x + l > con_linewidth) )
475 // mark time for transparent overlay
476 if (con_current >= 0)
478 if (con_notify.integer < 0)
479 Cvar_SetValueQuick(&con_notify, 0);
480 if (con_notify.integer > MAX_NOTIFYLINES)
481 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
482 if (con_notify.integer > 0)
483 con_times[con_current % con_notify.integer] = cl.time;
498 default: // display character and advance
499 y = con_current % con_totallines;
500 con_text[y*con_linewidth+con_x] = c | mask;
502 if (con_x >= con_linewidth)
510 /* The translation table between the graphical font and plain ASCII --KB */
511 static char qfont_table[256] = {
512 '\0', '#', '#', '#', '#', '.', '#', '#',
513 '#', 9, 10, '#', ' ', 13, '.', '.',
514 '[', ']', '0', '1', '2', '3', '4', '5',
515 '6', '7', '8', '9', '.', '<', '=', '>',
516 ' ', '!', '"', '#', '$', '%', '&', '\'',
517 '(', ')', '*', '+', ',', '-', '.', '/',
518 '0', '1', '2', '3', '4', '5', '6', '7',
519 '8', '9', ':', ';', '<', '=', '>', '?',
520 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
521 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
522 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
523 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
524 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
525 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
526 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
527 'x', 'y', 'z', '{', '|', '}', '~', '<',
529 '<', '=', '>', '#', '#', '.', '#', '#',
530 '#', '#', ' ', '#', ' ', '>', '.', '.',
531 '[', ']', '0', '1', '2', '3', '4', '5',
532 '6', '7', '8', '9', '.', '<', '=', '>',
533 ' ', '!', '"', '#', '$', '%', '&', '\'',
534 '(', ')', '*', '+', ',', '-', '.', '/',
535 '0', '1', '2', '3', '4', '5', '6', '7',
536 '8', '9', ':', ';', '<', '=', '>', '?',
537 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
538 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
539 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
540 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
541 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
542 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
543 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
544 'x', 'y', 'z', '{', '|', '}', '~', '<'
551 Prints to all appropriate console targets, and adds timestamps
554 extern cvar_t timestamps;
555 extern cvar_t timeformat;
556 extern qboolean sys_nostdout;
557 void Con_Print(const char *msg)
560 static int index = 0;
561 static char line[16384];
567 // if this is the beginning of a new line, print timestamp
568 char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
569 // special color codes for chat messages must always come first
570 // for Con_PrintToHistory to work properly
576 S_LocalSound ("sound/misc/talk.wav");
578 if (gamemode == GAME_NEXUIZ)
585 // go to colored text
591 for (;*timestamp;index++, timestamp++)
592 if (index < sizeof(line) - 2)
593 line[index] = *timestamp;
595 // append the character
596 line[index++] = *msg;
597 // if this is a newline character, we have a complete line to print
598 if (*msg == '\n' || index >= 16000)
600 // terminate the line
604 // send to terminal or dedicated server window
608 for (p = (unsigned char *) line;*p; p++)
609 *p = qfont_table[*p];
610 Sys_PrintToTerminal(line);
612 // send to scrollable buffer
613 if (con_initialized && cls.state != ca_dedicated)
614 Con_PrintToHistory(line, mask);
615 // empty the line buffer
622 // LordHavoc: increased from 4096 to 16384
623 #define MAXPRINTMSG 16384
629 Prints to all appropriate console targets
632 void Con_Printf(const char *fmt, ...)
635 char msg[MAXPRINTMSG];
637 va_start(argptr,fmt);
638 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
648 A Con_Print that only shows up if the "developer" cvar is set
651 void Con_DPrint(const char *msg)
653 if (!developer.integer)
654 return; // don't confuse non-developers with techie stuff...
662 A Con_Printf that only shows up if the "developer" cvar is set
665 void Con_DPrintf(const char *fmt, ...)
668 char msg[MAXPRINTMSG];
670 if (!developer.integer)
671 return; // don't confuse non-developers with techie stuff...
673 va_start(argptr,fmt);
674 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
682 ==============================================================================
686 ==============================================================================
689 static vec4_t _con_colors[] =
692 // LordHavoc: why on earth is cyan before magenta in Quake3?
693 // LordHavoc: note: Doom3 uses white for [0] and [7]
694 {0.0, 0.0, 0.0, 1.0}, // black
695 {1.0, 0.0, 0.0, 1.0}, // red
696 {0.0, 1.0, 0.0, 1.0}, // green
697 {1.0, 1.0, 0.0, 1.0}, // yellow
698 {0.0, 0.0, 1.0, 1.0}, // blue
699 {0.0, 1.0, 1.0, 1.0}, // cyan
700 {1.0, 0.0, 1.0, 1.0}, // magenta
701 {1.0, 1.0, 1.0, 1.0} // white
702 // Black's color table
703 //{1.0, 1.0, 1.0, 1.0},
704 //{1.0, 0.0, 0.0, 1.0},
705 //{0.0, 1.0, 0.0, 1.0},
706 //{0.0, 0.0, 1.0, 1.0},
707 //{1.0, 1.0, 0.0, 1.0},
708 //{0.0, 1.0, 1.0, 1.0},
709 //{1.0, 0.0, 1.0, 1.0},
710 //{0.1, 0.1, 0.1, 1.0}
713 #define _con_colors_count (sizeof(_con_colors) / sizeof(vec3_t))
714 #define _con_color_tag '^'
716 // color is read and changed in the end
717 static void _Con_DrawString( float x, float y, const char *text, int maxlen, float scalex, float scaley, int flags )
720 const char *first, *last;
723 color = _con_colors[7];
725 len = strlen( text );
727 len = min( maxlen, (signed) strlen( text ));
731 // iterate until we get the next color tag or reach the end of the text part to draw
732 for( ; len && *last != _con_color_tag ; len--, last++ )
734 // only draw the partial string if we have read anything
735 if( last != first ) {
737 DrawQ_String( x, y, first, last - first, scalex, scaley, color[0], color[1], color[2], color[3], flags );
738 // update x to be at the new start position
739 x += (last - first) * scalex;
740 // if we have reached the end, we have finished
748 if( len && '0' <= *last && *last <= '9' ) {
751 while( '0' <= *last && *last <= '9' && len ) {
752 index = index * 10 + *last - '0';
753 if( index < _con_colors_count ) {
762 color = _con_colors[index];
763 // we dont want to display the color tag and the color index
773 The input line scrolls horizontally if typing goes beyond the right edge
775 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
778 void Con_DrawInput (void)
782 char editlinecopy[257], *text;
784 if (!key_consoleactive)
785 return; // don't draw anything
787 text = strcpy(editlinecopy, key_lines[edit_line]);
789 // Advanced Console Editing by Radix radix@planetquake.com
790 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
791 // use strlen of edit_line instead of key_linepos to allow editing
792 // of early characters w/o erasing
796 // fill out remainder with spaces
797 for (i = y; i < 256; i++)
800 // add the cursor frame
801 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
802 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
804 // text[key_linepos + 1] = 0;
806 // prestep if horizontally scrolling
807 if (key_linepos >= con_linewidth)
808 text += 1 + key_linepos - con_linewidth;
811 _Con_DrawString(0, con_vislines - 16, text, con_linewidth, 8, 8, 0);
814 // key_lines[edit_line][key_linepos] = 0;
822 Draws the last few lines of output transparently over the game top
825 void Con_DrawNotify (void)
831 extern char chat_buffer[];
834 if (con_notify.integer < 0)
835 Cvar_SetValueQuick(&con_notify, 0);
836 if (con_notify.integer > MAX_NOTIFYLINES)
837 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
838 if (gamemode == GAME_TRANSFUSION)
842 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
846 time = con_times[i % con_notify.integer];
849 time = cl.time - time;
850 if (time > con_notifytime.value)
852 text = con_text + (i % con_totallines)*con_linewidth;
854 if (gamemode == GAME_NEXUIZ) {
857 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
858 x = (vid.conwidth - linewidth * 8) / 2;
862 _Con_DrawString(x, v, text, con_linewidth, 8, 8, 0);
868 if (key_dest == key_message)
872 // LordHavoc: speedup, and other improvements
874 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
876 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
877 while (strlen(temptext) >= (size_t) con_linewidth)
879 _Con_DrawString (0, v, temptext, con_linewidth, 8, 8, 0);
880 strcpy(temptext, &temptext[con_linewidth]);
883 if (strlen(temptext) > 0)
885 _Con_DrawString (0, v, temptext, 0, 8, 8, 0);
895 Draws the console with the solid background
896 The typing input line at the bottom should only be drawn if typing is allowed
899 extern char engineversion[40];
900 void Con_DrawConsole (int lines)
908 // draw the background
909 if (scr_conbrightness.value >= 0.01f)
910 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);
912 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
913 DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
916 con_vislines = lines;
918 rows = (lines-16)>>3; // rows of text to draw
919 y = lines - 16 - (rows<<3); // may start slightly negative
921 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
923 j = max(i - con_backscroll, 0);
924 text = con_text + (j % con_totallines)*con_linewidth;
926 _Con_DrawString(0, y, text, con_linewidth, 8, 8, 0);
929 // draw the input prompt, user text, and cursor if desired
936 New function for tab-completion system
938 MEGA Thanks to Taniwha
941 void Con_DisplayList(const char **list)
943 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
944 const char **walk = list;
956 if (pos + maxlen >= width) {
962 for (i = 0; i < (maxlen - len); i++)
974 Con_CompleteCommandLine
976 New function for tab-completion system
978 Thanks to Fett erich@heintz.com
982 void Con_CompleteCommandLine (void)
984 const char *cmd = "", *s;
985 const char **list[3] = {0, 0, 0};
986 int c, v, a, i, cmd_len;
988 s = key_lines[edit_line] + 1;
989 // Count number of possible matches
990 c = Cmd_CompleteCountPossible(s);
991 v = Cvar_CompleteCountPossible(s);
992 a = Cmd_CompleteAliasCountPossible(s);
994 if (!(c + v + a)) // No possible matches
997 if (c + v + a == 1) {
999 list[0] = Cmd_CompleteBuildList(s);
1001 list[0] = Cvar_CompleteBuildList(s);
1003 list[0] = Cmd_CompleteAliasBuildList(s);
1005 cmd_len = strlen (cmd);
1008 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1010 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1012 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1014 cmd_len = strlen (s);
1016 for (i = 0; i < 3; i++) {
1017 char ch = cmd[cmd_len];
1018 const char **l = list[i];
1020 while (*l && (*l)[cmd_len] == ch)
1031 for (i = 0; i < con_linewidth - 4; i++)
1035 // Print Possible Commands
1037 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1038 Con_DisplayList(list[0]);
1042 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1043 Con_DisplayList(list[1]);
1047 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1048 Con_DisplayList(list[2]);
1053 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
1054 key_linepos = cmd_len + 1;
1055 if (c + v + a == 1) {
1056 key_lines[edit_line][key_linepos] = ' ';
1059 key_lines[edit_line][key_linepos] = 0;
1061 for (i = 0; i < 3; i++)
1063 Mem_Free((void *)list[i]);