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)
452 if (txt[0] == 1 || txt[0] == 2)
454 if (gamemode == GAME_NEXUIZ)
457 mask = 128; // go to colored text
467 for (l=0 ; l< con_linewidth ; l++)
472 if (l != con_linewidth && (con_x + l > con_linewidth) )
487 // mark time for transparent overlay
488 if (con_current >= 0)
490 if (con_notify.integer < 0)
491 Cvar_SetValueQuick(&con_notify, 0);
492 if (con_notify.integer > MAX_NOTIFYLINES)
493 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
494 if (con_notify.integer > 0)
495 con_times[con_current % con_notify.integer] = cl.time;
510 default: // display character and advance
511 y = con_current % con_totallines;
512 con_text[y*con_linewidth+con_x] = c | mask;
514 if (con_x >= con_linewidth)
523 S_LocalSound ("sound/misc/talk.wav");
527 /* The translation table between the graphical font and plain ASCII --KB */
528 static char qfont_table[256] = {
529 '\0', '#', '#', '#', '#', '.', '#', '#',
530 '#', 9, 10, '#', ' ', 13, '.', '.',
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', '{', '|', '}', '~', '<',
546 '<', '=', '>', '#', '#', '.', '#', '#',
547 '#', '#', ' ', '#', ' ', '>', '.', '.',
548 '[', ']', '0', '1', '2', '3', '4', '5',
549 '6', '7', '8', '9', '.', '<', '=', '>',
550 ' ', '!', '"', '#', '$', '%', '&', '\'',
551 '(', ')', '*', '+', ',', '-', '.', '/',
552 '0', '1', '2', '3', '4', '5', '6', '7',
553 '8', '9', ':', ';', '<', '=', '>', '?',
554 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
555 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
556 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
557 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
558 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
559 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
560 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
561 'x', 'y', 'z', '{', '|', '}', '~', '<'
568 Prints to all appropriate console targets, and adds timestamps
571 extern cvar_t timestamps;
572 extern cvar_t timeformat;
573 extern qboolean sys_nostdout;
574 void Con_Print(const char *msg)
576 static int index = 0;
577 static char line[16384];
583 // if this is the beginning of a new line, print timestamp
584 char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
585 // special color codes for chat messages must always come first
586 // for Con_PrintToHistory to work properly
589 if (gamemode == GAME_NEXUIZ)
596 line[index++] = *msg++;
599 for (;*timestamp;index++, timestamp++)
600 if (index < sizeof(line) - 2)
601 line[index] = *timestamp;
603 // append the character
604 line[index++] = *msg;
605 // if this is a newline character, we have a complete line to print
606 if (*msg == '\n' || index >= 16000)
608 // terminate the line
612 // send to scrollable buffer
613 if (con_initialized && cls.state != ca_dedicated)
614 Con_PrintToHistory(line);
615 // send to terminal or dedicated server window
619 for (p = (unsigned char *) line;*p; p++)
620 *p = qfont_table[*p];
621 Sys_PrintToTerminal(line);
623 // empty the line buffer
630 // LordHavoc: increased from 4096 to 16384
631 #define MAXPRINTMSG 16384
637 Prints to all appropriate console targets
640 void Con_Printf(const char *fmt, ...)
643 char msg[MAXPRINTMSG];
645 va_start(argptr,fmt);
646 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
656 A Con_Print that only shows up if the "developer" cvar is set
659 void Con_DPrint(const char *msg)
661 if (!developer.integer)
662 return; // don't confuse non-developers with techie stuff...
670 A Con_Printf that only shows up if the "developer" cvar is set
673 void Con_DPrintf(const char *fmt, ...)
676 char msg[MAXPRINTMSG];
678 if (!developer.integer)
679 return; // don't confuse non-developers with techie stuff...
681 va_start(argptr,fmt);
682 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
690 ==============================================================================
694 ==============================================================================
697 static vec4_t _con_colors[] =
700 // LordHavoc: why on earth is cyan before magenta in Quake3?
701 // LordHavoc: note: Doom3 uses white for [0] and [7]
702 {0.0, 0.0, 0.0, 1.0}, // black
703 {1.0, 0.0, 0.0, 1.0}, // red
704 {0.0, 1.0, 0.0, 1.0}, // green
705 {1.0, 1.0, 0.0, 1.0}, // yellow
706 {0.0, 0.0, 1.0, 1.0}, // blue
707 {0.0, 1.0, 1.0, 1.0}, // cyan
708 {1.0, 0.0, 1.0, 1.0}, // magenta
709 {1.0, 1.0, 1.0, 1.0} // white
710 // Black's color table
711 //{1.0, 1.0, 1.0, 1.0},
712 //{1.0, 0.0, 0.0, 1.0},
713 //{0.0, 1.0, 0.0, 1.0},
714 //{0.0, 0.0, 1.0, 1.0},
715 //{1.0, 1.0, 0.0, 1.0},
716 //{0.0, 1.0, 1.0, 1.0},
717 //{1.0, 0.0, 1.0, 1.0},
718 //{0.1, 0.1, 0.1, 1.0}
721 #define _con_colors_count (sizeof(_con_colors) / sizeof(vec3_t))
722 #define _con_color_tag '^'
724 // color is read and changed in the end
725 static void _Con_DrawString( float x, float y, const char *text, int maxlen, float scalex, float scaley, int flags )
728 const char *first, *last;
731 color = _con_colors[7];
733 len = strlen( text );
735 len = min( maxlen, (signed) strlen( text ));
739 // iterate until we get the next color tag or reach the end of the text part to draw
740 for( ; len && *last != _con_color_tag ; len--, last++ )
742 // only draw the partial string if we have read anything
743 if( last != first ) {
745 DrawQ_String( x, y, first, last - first, scalex, scaley, color[0], color[1], color[2], color[3], flags );
746 // update x to be at the new start position
747 x += (last - first) * scalex;
748 // if we have reached the end, we have finished
756 if( len && '0' <= *last && *last <= '9' ) {
759 while( '0' <= *last && *last <= '9' && len ) {
760 index = index * 10 + *last - '0';
761 if( index < _con_colors_count ) {
770 color = _con_colors[index];
771 // we dont want to display the color tag and the color index
781 The input line scrolls horizontally if typing goes beyond the right edge
783 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
786 void Con_DrawInput (void)
790 char editlinecopy[257], *text;
792 if (!key_consoleactive)
793 return; // don't draw anything
795 text = strcpy(editlinecopy, key_lines[edit_line]);
797 // Advanced Console Editing by Radix radix@planetquake.com
798 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
799 // use strlen of edit_line instead of key_linepos to allow editing
800 // of early characters w/o erasing
804 // fill out remainder with spaces
805 for (i = y; i < 256; i++)
808 // add the cursor frame
809 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
810 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
812 // text[key_linepos + 1] = 0;
814 // prestep if horizontally scrolling
815 if (key_linepos >= con_linewidth)
816 text += 1 + key_linepos - con_linewidth;
819 _Con_DrawString(0, con_vislines - 16, text, con_linewidth, 8, 8, 0);
822 // key_lines[edit_line][key_linepos] = 0;
830 Draws the last few lines of output transparently over the game top
833 void Con_DrawNotify (void)
839 extern char chat_buffer[];
842 if (con_notify.integer < 0)
843 Cvar_SetValueQuick(&con_notify, 0);
844 if (con_notify.integer > MAX_NOTIFYLINES)
845 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
846 if (gamemode == GAME_TRANSFUSION)
850 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
854 time = con_times[i % con_notify.integer];
857 time = cl.time - time;
858 if (time > con_notifytime.value)
860 text = con_text + (i % con_totallines)*con_linewidth;
862 if (gamemode == GAME_NEXUIZ) {
865 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
866 x = (vid.conwidth - linewidth * 8) / 2;
870 _Con_DrawString(x, v, text, con_linewidth, 8, 8, 0);
876 if (key_dest == key_message)
880 // LordHavoc: speedup, and other improvements
882 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
884 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
885 while (strlen(temptext) >= (size_t) con_linewidth)
887 _Con_DrawString (0, v, temptext, con_linewidth, 8, 8, 0);
888 strcpy(temptext, &temptext[con_linewidth]);
891 if (strlen(temptext) > 0)
893 _Con_DrawString (0, v, temptext, 0, 8, 8, 0);
903 Draws the console with the solid background
904 The typing input line at the bottom should only be drawn if typing is allowed
907 extern char engineversion[40];
908 void Con_DrawConsole (int lines)
916 // draw the background
917 if (scr_conbrightness.value >= 0.01f)
918 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);
920 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
921 DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
924 con_vislines = lines;
926 rows = (lines-16)>>3; // rows of text to draw
927 y = lines - 16 - (rows<<3); // may start slightly negative
929 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
931 j = max(i - con_backscroll, 0);
932 text = con_text + (j % con_totallines)*con_linewidth;
934 _Con_DrawString(0, y, text, con_linewidth, 8, 8, 0);
937 // draw the input prompt, user text, and cursor if desired
944 New function for tab-completion system
946 MEGA Thanks to Taniwha
949 void Con_DisplayList(const char **list)
951 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
952 const char **walk = list;
964 if (pos + maxlen >= width) {
970 for (i = 0; i < (maxlen - len); i++)
982 Con_CompleteCommandLine
984 New function for tab-completion system
986 Thanks to Fett erich@heintz.com
990 void Con_CompleteCommandLine (void)
992 const char *cmd = "", *s;
993 const char **list[3] = {0, 0, 0};
994 int c, v, a, i, cmd_len;
996 s = key_lines[edit_line] + 1;
997 // Count number of possible matches
998 c = Cmd_CompleteCountPossible(s);
999 v = Cvar_CompleteCountPossible(s);
1000 a = Cmd_CompleteAliasCountPossible(s);
1002 if (!(c + v + a)) // No possible matches
1005 if (c + v + a == 1) {
1007 list[0] = Cmd_CompleteBuildList(s);
1009 list[0] = Cvar_CompleteBuildList(s);
1011 list[0] = Cmd_CompleteAliasBuildList(s);
1013 cmd_len = strlen (cmd);
1016 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1018 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1020 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1022 cmd_len = strlen (s);
1024 for (i = 0; i < 3; i++) {
1025 char ch = cmd[cmd_len];
1026 const char **l = list[i];
1028 while (*l && (*l)[cmd_len] == ch)
1039 for (i = 0; i < con_linewidth - 4; i++)
1043 // Print Possible Commands
1045 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1046 Con_DisplayList(list[0]);
1050 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1051 Con_DisplayList(list[1]);
1055 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1056 Con_DisplayList(list[2]);
1061 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
1062 key_linepos = cmd_len + 1;
1063 if (c + v + a == 1) {
1064 key_lines[edit_line][key_linepos] = ' ';
1067 key_lines[edit_line][key_linepos] = 0;
1069 for (i = 0; i < 3; i++)
1071 Mem_Free((void *)list[i]);