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
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;
63 mempool_t *console_mempool;
67 ==============================================================================
71 ==============================================================================
74 cvar_t log_file = {0, "log_file",""};
75 char crt_log_file [MAX_OSPATH] = "";
76 qfile_t* logfile = NULL;
78 qbyte* logqueue = NULL;
82 void Log_ConPrint (const char *msg);
89 const char* Log_Timestamp (const char *desc)
91 static char timestamp [128];
93 const struct tm *crt_tm;
96 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
98 crt_tm = localtime (&crt_time);
99 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
102 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
104 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
117 // Allocate a log queue
119 logqueue = Mem_Alloc (tempmempool, logq_size);
122 Cvar_RegisterVariable (&log_file);
124 // support for the classic Quake option
125 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
126 if (COM_CheckParm ("-condebug") != 0)
127 Cvar_SetQuick (&log_file, "qconsole.log");
138 if (logfile != NULL || log_file.string[0] == '\0')
141 logfile = FS_Open (log_file.string, "ab", false, false);
144 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
145 FS_Print (logfile, Log_Timestamp ("Log started"));
155 void Log_Close (void)
160 FS_Print (logfile, Log_Timestamp ("Log stopped"));
161 FS_Print (logfile, "\n");
165 crt_log_file[0] = '\0';
174 void Log_Start (void)
178 // Dump the contents of the log queue into the log file and free it
179 if (logqueue != NULL)
181 if (logfile != NULL && logq_ind != 0)
182 FS_Write (logfile, logqueue, logq_ind);
196 void Log_ConPrint (const char *msg)
198 static qboolean inprogress = false;
200 // don't allow feedback loops with memory error reports
205 // Until the host is completely initialized, we maintain a log queue
206 // to store the messages, since the log can't be started before
207 if (logqueue != NULL)
209 size_t remain = logq_size - logq_ind;
210 size_t len = strlen (msg);
212 // If we need to enlarge the log queue
215 unsigned int factor = ((logq_ind + len) / logq_size) + 1;
219 newqueue = Mem_Alloc (tempmempool, logq_size);
220 memcpy (newqueue, logqueue, logq_ind);
223 remain = logq_size - logq_ind;
225 memcpy (&logqueue[logq_ind], msg, len);
232 // Check if log_file has changed
233 if (strcmp (crt_log_file, log_file.string) != 0)
239 // If a log file is available
241 FS_Print (logfile, msg);
251 void Log_Printf (const char *logfilename, const char *fmt, ...)
255 file = FS_Open (logfilename, "ab", true, false);
260 va_start (argptr, fmt);
261 FS_VPrintf (file, fmt, argptr);
270 ==============================================================================
274 ==============================================================================
282 void Con_ToggleConsole_f (void)
284 // toggle the 'user wants console' bit
285 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
286 memset (con_times, 0, sizeof(con_times));
294 void Con_Clear_f (void)
297 memset (con_text, ' ', CON_TEXTSIZE);
306 void Con_ClearNotify (void)
310 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
320 void Con_MessageMode_f (void)
322 key_dest = key_message;
332 void Con_MessageMode2_f (void)
334 key_dest = key_message;
343 If the line width has changed, reformat the buffer.
346 void Con_CheckResize (void)
348 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
349 char tbuf[CON_TEXTSIZE];
351 width = (vid.conwidth >> 3);
353 if (width == con_linewidth)
356 if (width < 1) // video hasn't been initialized yet
359 con_linewidth = width;
360 con_totallines = CON_TEXTSIZE / con_linewidth;
361 memset (con_text, ' ', CON_TEXTSIZE);
365 oldwidth = con_linewidth;
366 con_linewidth = width;
367 oldtotallines = con_totallines;
368 con_totallines = CON_TEXTSIZE / con_linewidth;
369 numlines = oldtotallines;
371 if (con_totallines < numlines)
372 numlines = con_totallines;
376 if (con_linewidth < numchars)
377 numchars = con_linewidth;
379 memcpy (tbuf, con_text, CON_TEXTSIZE);
380 memset (con_text, ' ', CON_TEXTSIZE);
382 for (i=0 ; i<numlines ; i++)
384 for (j=0 ; j<numchars ; j++)
386 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
387 tbuf[((con_current - i + oldtotallines) %
388 oldtotallines) * oldwidth + j];
396 con_current = con_totallines - 1;
406 console_mempool = Mem_AllocPool("console", 0, NULL);
407 con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
408 memset (con_text, ' ', CON_TEXTSIZE);
412 // register our cvars
413 Cvar_RegisterVariable (&con_notifytime);
414 Cvar_RegisterVariable (&con_notify);
416 // register our commands
417 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
418 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
419 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
420 Cmd_AddCommand ("clear", Con_Clear_f);
422 con_initialized = true;
423 Con_Print("Console initialized.\n");
432 void Con_Linefeed (void)
439 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
446 Handles cursor positioning, line wrapping, etc
447 All console printing must go through this in order to be displayed
448 If no console is visible, the notify window will pop up.
451 void Con_PrintToHistory(const char *txt)
458 mask = 128; // go to colored text
461 else if (txt[0] == 2)
463 mask = 128; // go to colored text
473 for (l=0 ; l< con_linewidth ; l++)
478 if (l != con_linewidth && (con_x + l > con_linewidth) )
493 // mark time for transparent overlay
494 if (con_current >= 0)
496 if (con_notify.integer < 0)
497 Cvar_SetValueQuick(&con_notify, 0);
498 if (con_notify.integer > MAX_NOTIFYLINES)
499 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
500 if (con_notify.integer > 0)
501 con_times[con_current % con_notify.integer] = cl.time;
516 default: // display character and advance
517 y = con_current % con_totallines;
518 con_text[y*con_linewidth+con_x] = c | mask;
520 if (con_x >= con_linewidth)
529 S_LocalSound ("sound/misc/talk.wav");
533 /* The translation table between the graphical font and plain ASCII --KB */
534 static char qfont_table[256] = {
535 '\0', '#', '#', '#', '#', '.', '#', '#',
536 '#', 9, 10, '#', ' ', 13, '.', '.',
537 '[', ']', '0', '1', '2', '3', '4', '5',
538 '6', '7', '8', '9', '.', '<', '=', '>',
539 ' ', '!', '"', '#', '$', '%', '&', '\'',
540 '(', ')', '*', '+', ',', '-', '.', '/',
541 '0', '1', '2', '3', '4', '5', '6', '7',
542 '8', '9', ':', ';', '<', '=', '>', '?',
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 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
548 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
549 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
550 'x', 'y', 'z', '{', '|', '}', '~', '<',
552 '<', '=', '>', '#', '#', '.', '#', '#',
553 '#', '#', ' ', '#', ' ', '>', '.', '.',
554 '[', ']', '0', '1', '2', '3', '4', '5',
555 '6', '7', '8', '9', '.', '<', '=', '>',
556 ' ', '!', '"', '#', '$', '%', '&', '\'',
557 '(', ')', '*', '+', ',', '-', '.', '/',
558 '0', '1', '2', '3', '4', '5', '6', '7',
559 '8', '9', ':', ';', '<', '=', '>', '?',
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 '`', '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', '{', '|', '}', '~', '<'
574 Prints to all appropriate console targets, and adds timestamps
577 extern cvar_t timestamps;
578 extern cvar_t timeformat;
579 extern qboolean sys_nostdout;
580 void Con_Print(const char *msg)
582 static int index = 0;
583 static char line[16384];
589 // if this is the beginning of a new line, print timestamp
590 char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
591 // special color codes for chat messages must always come first
592 // for Con_PrintToHistory to work properly
594 line[index++] = *msg++;
596 for (;*timestamp;index++, timestamp++)
597 if (index < sizeof(line) - 2)
598 line[index] = *timestamp;
600 // append the character
601 line[index++] = *msg;
602 // if this is a newline character, we have a complete line to print
603 if (*msg == '\n' || index >= 16000)
605 // terminate the line
609 // send to scrollable buffer
610 if (con_initialized && cls.state != ca_dedicated)
611 Con_PrintToHistory(line);
612 // send to terminal or dedicated server window
616 for (p = (unsigned char *) line;*p; p++)
617 *p = qfont_table[*p];
618 Sys_PrintToTerminal(line);
620 // empty the line buffer
627 // LordHavoc: increased from 4096 to 16384
628 #define MAXPRINTMSG 16384
634 Prints to all appropriate console targets
637 void Con_Printf(const char *fmt, ...)
640 char msg[MAXPRINTMSG];
642 va_start(argptr,fmt);
643 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
653 A Con_Print that only shows up if the "developer" cvar is set
656 void Con_DPrint(const char *msg)
658 if (!developer.integer)
659 return; // don't confuse non-developers with techie stuff...
667 A Con_Printf that only shows up if the "developer" cvar is set
670 void Con_DPrintf(const char *fmt, ...)
673 char msg[MAXPRINTMSG];
675 if (!developer.integer)
676 return; // don't confuse non-developers with techie stuff...
678 va_start(argptr,fmt);
679 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
687 ==============================================================================
691 ==============================================================================
694 static vec4_t _con_colors[] =
696 {1.0, 1.0, 1.0, 1.0},
697 {1.0, 0.0, 0.0, 1.0},
698 {0.0, 1.0, 0.0, 1.0},
699 {0.0, 0.0, 1.0, 1.0},
700 {1.0, 1.0, 0.0, 1.0},
701 {0.0, 1.0, 1.0, 1.0},
702 {1.0, 0.0, 1.0, 1.0},
706 #define _con_colors_count (sizeof(_con_colors) / sizeof(vec3_t))
707 #define _con_color_tag '^'
709 // color is read and changed in the end
710 static void _Con_DrawString( float x, float y, const char *text, int maxlen, float scalex, float scaley, int flags )
713 const char *first, *last;
716 color = _con_colors[0];
718 len = strlen( text );
720 len = min( maxlen, (signed) strlen( text ));
724 // iterate until we get the next color tag or reach the end of the text part to draw
725 for( ; len && *last != _con_color_tag ; len--, last++ )
727 // only draw the partial string if we have read anything
728 if( last != first ) {
730 DrawQ_String( x, y, first, last - first, scalex, scaley, color[0], color[1], color[2], color[3], flags );
731 // update x to be at the new start position
732 x += (last - first) * scalex;
733 // if we have reached the end, we have finished
741 if( len && '0' <= *last && *last <= '9' ) {
744 while( '0' <= *last && *last <= '9' && len ) {
745 index = index * 10 + *last - '0';
746 if( index < _con_colors_count ) {
755 color = _con_colors[index];
756 // we dont want to display the color tag and the color index
766 The input line scrolls horizontally if typing goes beyond the right edge
768 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
771 void Con_DrawInput (void)
775 char editlinecopy[257], *text;
777 if (!key_consoleactive)
778 return; // don't draw anything
780 text = strcpy(editlinecopy, key_lines[edit_line]);
782 // Advanced Console Editing by Radix radix@planetquake.com
783 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
784 // use strlen of edit_line instead of key_linepos to allow editing
785 // of early characters w/o erasing
789 // fill out remainder with spaces
790 for (i = y; i < 256; i++)
793 // add the cursor frame
794 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
795 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
797 // text[key_linepos + 1] = 0;
799 // prestep if horizontally scrolling
800 if (key_linepos >= con_linewidth)
801 text += 1 + key_linepos - con_linewidth;
804 _Con_DrawString(0, con_vislines - 16, text, con_linewidth, 8, 8, 0);
807 // key_lines[edit_line][key_linepos] = 0;
815 Draws the last few lines of output transparently over the game top
818 void Con_DrawNotify (void)
824 extern char chat_buffer[];
827 if (con_notify.integer < 0)
828 Cvar_SetValueQuick(&con_notify, 0);
829 if (con_notify.integer > MAX_NOTIFYLINES)
830 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
831 if (gamemode == GAME_TRANSFUSION)
835 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
839 time = con_times[i % con_notify.integer];
842 time = cl.time - time;
843 if (time > con_notifytime.value)
845 text = con_text + (i % con_totallines)*con_linewidth;
847 if (gamemode == GAME_NEXUIZ) {
850 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
851 x = (vid.conwidth - linewidth * 8) / 2;
855 _Con_DrawString(x, v, text, con_linewidth, 8, 8, 0);
861 if (key_dest == key_message)
865 // LordHavoc: speedup, and other improvements
867 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
869 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
870 while (strlen(temptext) >= (size_t) con_linewidth)
872 _Con_DrawString (0, v, temptext, con_linewidth, 8, 8, 0);
873 strcpy(temptext, &temptext[con_linewidth]);
876 if (strlen(temptext) > 0)
878 _Con_DrawString (0, v, temptext, 0, 8, 8, 0);
888 Draws the console with the solid background
889 The typing input line at the bottom should only be drawn if typing is allowed
892 extern char engineversion[40];
893 void Con_DrawConsole (int lines)
901 // draw the background
902 if (scr_conbrightness.value >= 0.01f)
903 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);
905 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
906 DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
909 con_vislines = lines;
911 rows = (lines-16)>>3; // rows of text to draw
912 y = lines - 16 - (rows<<3); // may start slightly negative
914 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
916 j = max(i - con_backscroll, 0);
917 text = con_text + (j % con_totallines)*con_linewidth;
919 _Con_DrawString(0, y, text, con_linewidth, 8, 8, 0);
922 // draw the input prompt, user text, and cursor if desired
929 New function for tab-completion system
931 MEGA Thanks to Taniwha
934 void Con_DisplayList(const char **list)
936 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
937 const char **walk = list;
949 if (pos + maxlen >= width) {
955 for (i = 0; i < (maxlen - len); i++)
967 Con_CompleteCommandLine
969 New function for tab-completion system
971 Thanks to Fett erich@heintz.com
975 void Con_CompleteCommandLine (void)
977 const char *cmd = "", *s;
978 const char **list[3] = {0, 0, 0};
979 int c, v, a, i, cmd_len;
981 s = key_lines[edit_line] + 1;
982 // Count number of possible matches
983 c = Cmd_CompleteCountPossible(s);
984 v = Cvar_CompleteCountPossible(s);
985 a = Cmd_CompleteAliasCountPossible(s);
987 if (!(c + v + a)) // No possible matches
990 if (c + v + a == 1) {
992 list[0] = Cmd_CompleteBuildList(s);
994 list[0] = Cvar_CompleteBuildList(s);
996 list[0] = Cmd_CompleteAliasBuildList(s);
998 cmd_len = strlen (cmd);
1001 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1003 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1005 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1007 cmd_len = strlen (s);
1009 for (i = 0; i < 3; i++) {
1010 char ch = cmd[cmd_len];
1011 const char **l = list[i];
1013 while (*l && (*l)[cmd_len] == ch)
1024 for (i = 0; i < con_linewidth - 4; i++)
1028 // Print Possible Commands
1030 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1031 Con_DisplayList(list[0]);
1035 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1036 Con_DisplayList(list[1]);
1040 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1041 Con_DisplayList(list[2]);
1046 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
1047 key_linepos = cmd_len + 1;
1048 if (c + v + a == 1) {
1049 key_lines[edit_line][key_linepos] = ' ';
1052 key_lines[edit_line][key_linepos] = 0;
1054 for (i = 0; i < 3; i++)
1056 Mem_Free((void *)list[i]);