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 if (logfile != NULL || log_file.string[0] == '\0')
118 logfile = FS_Open (log_file.string, "ab", false, false);
121 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
122 FS_Print (logfile, Log_Timestamp ("Log started"));
132 void Log_Close (void)
137 FS_Print (logfile, Log_Timestamp ("Log stopped"));
138 FS_Print (logfile, "\n");
142 crt_log_file[0] = '\0';
151 void Log_Start (void)
155 // Dump the contents of the log queue into the log file and free it
156 if (logqueue != NULL)
158 if (logfile != NULL && logq_ind != 0)
159 FS_Write (logfile, logqueue, logq_ind);
173 void Log_ConPrint (const char *msg)
175 static qboolean inprogress = false;
177 // don't allow feedback loops with memory error reports
182 // Until the host is completely initialized, we maintain a log queue
183 // to store the messages, since the log can't be started before
184 if (logqueue != NULL)
186 size_t remain = logq_size - logq_ind;
187 size_t len = strlen (msg);
189 // If we need to enlarge the log queue
192 unsigned int factor = ((logq_ind + len) / logq_size) + 1;
196 newqueue = Mem_Alloc (tempmempool, logq_size);
197 memcpy (newqueue, logqueue, logq_ind);
200 remain = logq_size - logq_ind;
202 memcpy (&logqueue[logq_ind], msg, len);
209 // Check if log_file has changed
210 if (strcmp (crt_log_file, log_file.string) != 0)
216 // If a log file is available
218 FS_Print (logfile, msg);
228 void Log_Printf (const char *logfilename, const char *fmt, ...)
232 file = FS_Open (logfilename, "ab", true, false);
237 va_start (argptr, fmt);
238 FS_VPrintf (file, fmt, argptr);
247 ==============================================================================
251 ==============================================================================
259 void Con_ToggleConsole_f (void)
261 // toggle the 'user wants console' bit
262 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
263 memset (con_times, 0, sizeof(con_times));
271 void Con_Clear_f (void)
274 memset (con_text, ' ', CON_TEXTSIZE);
283 void Con_ClearNotify (void)
287 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
297 void Con_MessageMode_f (void)
299 key_dest = key_message;
309 void Con_MessageMode2_f (void)
311 key_dest = key_message;
320 If the line width has changed, reformat the buffer.
323 void Con_CheckResize (void)
325 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
326 char tbuf[CON_TEXTSIZE];
328 width = (vid_conwidth.integer >> 3);
330 if (width == con_linewidth)
333 oldwidth = con_linewidth;
334 con_linewidth = width;
335 oldtotallines = con_totallines;
336 con_totallines = CON_TEXTSIZE / con_linewidth;
337 numlines = oldtotallines;
339 if (con_totallines < numlines)
340 numlines = con_totallines;
344 if (con_linewidth < numchars)
345 numchars = con_linewidth;
347 memcpy (tbuf, con_text, CON_TEXTSIZE);
348 memset (con_text, ' ', CON_TEXTSIZE);
350 for (i=0 ; i<numlines ; i++)
352 for (j=0 ; j<numchars ; j++)
354 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
355 tbuf[((con_current - i + oldtotallines) %
356 oldtotallines) * oldwidth + j];
363 con_current = con_totallines - 1;
373 memset (con_text, ' ', CON_TEXTSIZE);
375 con_totallines = CON_TEXTSIZE / con_linewidth;
377 // Allocate a log queue
379 logqueue = Mem_Alloc (tempmempool, logq_size);
382 Cvar_RegisterVariable (&log_file);
384 // support for the classic Quake option
385 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
386 if (COM_CheckParm ("-condebug") != 0)
387 Cvar_SetQuick (&log_file, "qconsole.log");
390 void Con_Init_Commands (void)
392 // register our cvars
393 Cvar_RegisterVariable (&con_notifytime);
394 Cvar_RegisterVariable (&con_notify);
396 // register our commands
397 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
398 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
399 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
400 Cmd_AddCommand ("clear", Con_Clear_f);
402 con_initialized = true;
403 Con_Print("Console initialized.\n");
412 void Con_Linefeed (void)
419 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
426 Handles cursor positioning, line wrapping, etc
427 All console printing must go through this in order to be displayed
428 If no console is visible, the notify window will pop up.
431 void Con_PrintToHistory(const char *txt, int mask)
439 for (l=0 ; l< con_linewidth ; l++)
444 if (l != con_linewidth && (con_x + l > con_linewidth) )
459 // mark time for transparent overlay
460 if (con_current >= 0)
462 if (con_notify.integer < 0)
463 Cvar_SetValueQuick(&con_notify, 0);
464 if (con_notify.integer > MAX_NOTIFYLINES)
465 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
466 if (con_notify.integer > 0)
467 con_times[con_current % con_notify.integer] = cl.time;
482 default: // display character and advance
483 y = con_current % con_totallines;
484 con_text[y*con_linewidth+con_x] = c | mask;
486 if (con_x >= con_linewidth)
494 /* The translation table between the graphical font and plain ASCII --KB */
495 static char qfont_table[256] = {
496 '\0', '#', '#', '#', '#', '.', '#', '#',
497 '#', 9, 10, '#', ' ', 13, '.', '.',
498 '[', ']', '0', '1', '2', '3', '4', '5',
499 '6', '7', '8', '9', '.', '<', '=', '>',
500 ' ', '!', '"', '#', '$', '%', '&', '\'',
501 '(', ')', '*', '+', ',', '-', '.', '/',
502 '0', '1', '2', '3', '4', '5', '6', '7',
503 '8', '9', ':', ';', '<', '=', '>', '?',
504 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
505 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
506 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
507 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
508 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
509 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
510 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
511 'x', 'y', 'z', '{', '|', '}', '~', '<',
513 '<', '=', '>', '#', '#', '.', '#', '#',
514 '#', '#', ' ', '#', ' ', '>', '.', '.',
515 '[', ']', '0', '1', '2', '3', '4', '5',
516 '6', '7', '8', '9', '.', '<', '=', '>',
517 ' ', '!', '"', '#', '$', '%', '&', '\'',
518 '(', ')', '*', '+', ',', '-', '.', '/',
519 '0', '1', '2', '3', '4', '5', '6', '7',
520 '8', '9', ':', ';', '<', '=', '>', '?',
521 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
522 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
523 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
524 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
525 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
526 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
527 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
528 'x', 'y', 'z', '{', '|', '}', '~', '<'
535 Prints to all appropriate console targets, and adds timestamps
538 extern cvar_t timestamps;
539 extern cvar_t timeformat;
540 extern qboolean sys_nostdout;
541 void Con_Print(const char *msg)
544 static int index = 0;
545 static char line[16384];
551 // if this is the beginning of a new line, print timestamp
552 char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
553 // special color codes for chat messages must always come first
554 // for Con_PrintToHistory to work properly
560 S_LocalSound ("sound/misc/talk.wav");
562 if (gamemode == GAME_NEXUIZ)
569 // go to colored text
575 for (;*timestamp;index++, timestamp++)
576 if (index < sizeof(line) - 2)
577 line[index] = *timestamp;
579 // append the character
580 line[index++] = *msg;
581 // if this is a newline character, we have a complete line to print
582 if (*msg == '\n' || index >= 16000)
584 // terminate the line
588 // send to scrollable buffer
589 if (con_initialized && cls.state != ca_dedicated)
590 Con_PrintToHistory(line, mask);
591 // send to terminal or dedicated server window
595 for (p = (unsigned char *) line;*p; p++)
596 *p = qfont_table[*p];
597 Sys_PrintToTerminal(line);
599 // empty the line buffer
606 // LordHavoc: increased from 4096 to 16384
607 #define MAXPRINTMSG 16384
613 Prints to all appropriate console targets
616 void Con_Printf(const char *fmt, ...)
619 char msg[MAXPRINTMSG];
621 va_start(argptr,fmt);
622 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
632 A Con_Print that only shows up if the "developer" cvar is set
635 void Con_DPrint(const char *msg)
637 if (!developer.integer)
638 return; // don't confuse non-developers with techie stuff...
646 A Con_Printf that only shows up if the "developer" cvar is set
649 void Con_DPrintf(const char *fmt, ...)
652 char msg[MAXPRINTMSG];
654 if (!developer.integer)
655 return; // don't confuse non-developers with techie stuff...
657 va_start(argptr,fmt);
658 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
666 ==============================================================================
670 ==============================================================================
673 static vec4_t _con_colors[] =
676 // LordHavoc: why on earth is cyan before magenta in Quake3?
677 // LordHavoc: note: Doom3 uses white for [0] and [7]
678 {0.0, 0.0, 0.0, 1.0}, // black
679 {1.0, 0.0, 0.0, 1.0}, // red
680 {0.0, 1.0, 0.0, 1.0}, // green
681 {1.0, 1.0, 0.0, 1.0}, // yellow
682 {0.0, 0.0, 1.0, 1.0}, // blue
683 {0.0, 1.0, 1.0, 1.0}, // cyan
684 {1.0, 0.0, 1.0, 1.0}, // magenta
685 {1.0, 1.0, 1.0, 1.0} // white
686 // Black's color table
687 //{1.0, 1.0, 1.0, 1.0},
688 //{1.0, 0.0, 0.0, 1.0},
689 //{0.0, 1.0, 0.0, 1.0},
690 //{0.0, 0.0, 1.0, 1.0},
691 //{1.0, 1.0, 0.0, 1.0},
692 //{0.0, 1.0, 1.0, 1.0},
693 //{1.0, 0.0, 1.0, 1.0},
694 //{0.1, 0.1, 0.1, 1.0}
697 #define _con_colors_count (sizeof(_con_colors) / sizeof(vec3_t))
698 #define _con_color_tag '^'
700 // color is read and changed in the end
701 static void _Con_DrawString( float x, float y, const char *text, int maxlen, float scalex, float scaley, int flags )
704 const char *first, *last;
707 color = _con_colors[7];
709 len = strlen( text );
711 len = min( maxlen, (signed) strlen( text ));
715 // iterate until we get the next color tag or reach the end of the text part to draw
716 for( ; len && *last != _con_color_tag ; len--, last++ )
718 // only draw the partial string if we have read anything
719 if( last != first ) {
721 DrawQ_String( x, y, first, last - first, scalex, scaley, color[0], color[1], color[2], color[3], flags );
722 // update x to be at the new start position
723 x += (last - first) * scalex;
724 // if we have reached the end, we have finished
732 if( len && '0' <= *last && *last <= '9' ) {
735 while( '0' <= *last && *last <= '9' && len ) {
736 index = index * 10 + *last - '0';
737 if( index < _con_colors_count ) {
746 color = _con_colors[index];
747 // we dont want to display the color tag and the color index
757 The input line scrolls horizontally if typing goes beyond the right edge
759 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
762 void Con_DrawInput (void)
766 char editlinecopy[257], *text;
768 if (!key_consoleactive)
769 return; // don't draw anything
771 text = strcpy(editlinecopy, key_lines[edit_line]);
773 // Advanced Console Editing by Radix radix@planetquake.com
774 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
775 // use strlen of edit_line instead of key_linepos to allow editing
776 // of early characters w/o erasing
780 // fill out remainder with spaces
781 for (i = y; i < 256; i++)
784 // add the cursor frame
785 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
786 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
788 // text[key_linepos + 1] = 0;
790 // prestep if horizontally scrolling
791 if (key_linepos >= con_linewidth)
792 text += 1 + key_linepos - con_linewidth;
795 _Con_DrawString(0, con_vislines - 16, text, con_linewidth, 8, 8, 0);
798 // key_lines[edit_line][key_linepos] = 0;
806 Draws the last few lines of output transparently over the game top
809 void Con_DrawNotify (void)
815 extern char chat_buffer[];
818 if (con_notify.integer < 0)
819 Cvar_SetValueQuick(&con_notify, 0);
820 if (con_notify.integer > MAX_NOTIFYLINES)
821 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
822 if (gamemode == GAME_TRANSFUSION)
826 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
830 time = con_times[i % con_notify.integer];
833 time = cl.time - time;
834 if (time > con_notifytime.value)
836 text = con_text + (i % con_totallines)*con_linewidth;
838 if (gamemode == GAME_NEXUIZ) {
841 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
842 x = (vid_conwidth.integer - linewidth * 8) / 2;
846 _Con_DrawString(x, v, text, con_linewidth, 8, 8, 0);
852 if (key_dest == key_message)
856 // LordHavoc: speedup, and other improvements
858 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
860 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
861 while (strlen(temptext) >= (size_t) con_linewidth)
863 _Con_DrawString (0, v, temptext, con_linewidth, 8, 8, 0);
864 strcpy(temptext, &temptext[con_linewidth]);
867 if (strlen(temptext) > 0)
869 _Con_DrawString (0, v, temptext, 0, 8, 8, 0);
879 Draws the console with the solid background
880 The typing input line at the bottom should only be drawn if typing is allowed
883 extern char engineversion[40];
884 void Con_DrawConsole (int lines)
892 // draw the background
893 if (scr_conbrightness.value >= 0.01f)
894 DrawQ_Pic(0, lines - vid_conheight.integer, "gfx/conback", vid_conwidth.integer, vid_conheight.integer, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, scr_conalpha.value, 0);
896 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
897 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
900 con_vislines = lines;
902 rows = (lines-16)>>3; // rows of text to draw
903 y = lines - 16 - (rows<<3); // may start slightly negative
905 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
907 j = max(i - con_backscroll, 0);
908 text = con_text + (j % con_totallines)*con_linewidth;
910 _Con_DrawString(0, y, text, con_linewidth, 8, 8, 0);
913 // draw the input prompt, user text, and cursor if desired
920 New function for tab-completion system
922 MEGA Thanks to Taniwha
925 void Con_DisplayList(const char **list)
927 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
928 const char **walk = list;
940 if (pos + maxlen >= width) {
946 for (i = 0; i < (maxlen - len); i++)
958 Con_CompleteCommandLine
960 New function for tab-completion system
962 Thanks to Fett erich@heintz.com
966 void Con_CompleteCommandLine (void)
968 const char *cmd = "", *s;
969 const char **list[3] = {0, 0, 0};
970 int c, v, a, i, cmd_len;
972 s = key_lines[edit_line] + 1;
973 // Count number of possible matches
974 c = Cmd_CompleteCountPossible(s);
975 v = Cvar_CompleteCountPossible(s);
976 a = Cmd_CompleteAliasCountPossible(s);
978 if (!(c + v + a)) // No possible matches
981 if (c + v + a == 1) {
983 list[0] = Cmd_CompleteBuildList(s);
985 list[0] = Cvar_CompleteBuildList(s);
987 list[0] = Cmd_CompleteAliasBuildList(s);
989 cmd_len = strlen (cmd);
992 cmd = *(list[0] = Cmd_CompleteBuildList(s));
994 cmd = *(list[1] = Cvar_CompleteBuildList(s));
996 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
998 cmd_len = strlen (s);
1000 for (i = 0; i < 3; i++) {
1001 char ch = cmd[cmd_len];
1002 const char **l = list[i];
1004 while (*l && (*l)[cmd_len] == ch)
1015 for (i = 0; i < con_linewidth - 4; i++)
1019 // Print Possible Commands
1021 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1022 Con_DisplayList(list[0]);
1026 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1027 Con_DisplayList(list[1]);
1031 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1032 Con_DisplayList(list[2]);
1037 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
1038 key_linepos = cmd_len + 1;
1039 if (c + v + a == 1) {
1040 key_lines[edit_line][key_linepos] = ' ';
1043 key_lines[edit_line][key_linepos] = 0;
1045 for (i = 0; i < 3; i++)
1047 Mem_Free((void *)list[i]);