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)
454 mask = 128; // go to colored text
457 else if (txt[0] == 2)
459 mask = 128; // go to colored text
469 for (l=0 ; l< con_linewidth ; l++)
474 if (l != con_linewidth && (con_x + l > con_linewidth) )
489 // mark time for transparent overlay
490 if (con_current >= 0)
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;
512 default: // display character and advance
513 y = con_current % con_totallines;
514 con_text[y*con_linewidth+con_x] = c | mask;
516 if (con_x >= con_linewidth)
525 S_LocalSound ("sound/misc/talk.wav");
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', '{', '|', '}', '~', '<',
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', '{', '|', '}', '~', '<'
570 Prints to all appropriate console targets, and adds timestamps
573 extern cvar_t timestamps;
574 extern cvar_t timeformat;
575 extern qboolean sys_nostdout;
576 void Con_Print(const char *msg)
578 static int index = 0;
579 static char line[16384];
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
590 line[index++] = *msg++;
592 for (;*timestamp;index++, timestamp++)
593 if (index < sizeof(line) - 2)
594 line[index] = *timestamp;
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)
601 // terminate the 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
612 for (p = (unsigned char *) line;*p; p++)
613 *p = qfont_table[*p];
614 Sys_PrintToTerminal(line);
616 // empty the line buffer
623 // LordHavoc: increased from 4096 to 16384
624 #define MAXPRINTMSG 16384
630 Prints to all appropriate console targets
633 void Con_Printf(const char *fmt, ...)
636 char msg[MAXPRINTMSG];
638 va_start(argptr,fmt);
639 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
649 A Con_Print that only shows up if the "developer" cvar is set
652 void Con_DPrint(const char *msg)
654 if (!developer.integer)
655 return; // don't confuse non-developers with techie stuff...
663 A Con_Printf that only shows up if the "developer" cvar is set
666 void Con_DPrintf(const char *fmt, ...)
669 char msg[MAXPRINTMSG];
671 if (!developer.integer)
672 return; // don't confuse non-developers with techie stuff...
674 va_start(argptr,fmt);
675 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
683 ==============================================================================
687 ==============================================================================
690 static vec4_t _con_colors[] =
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},
702 #define _con_colors_count (sizeof(_con_colors) / sizeof(vec3_t))
703 #define _con_color_tag '^'
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 )
709 const char *first, *last;
712 color = _con_colors[0];
714 len = strlen( text );
716 len = min( maxlen, (signed) strlen( text ));
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++ )
723 // only draw the partial string if we have read anything
724 if( last != first ) {
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
737 if( len && '0' <= *last && *last <= '9' ) {
740 while( '0' <= *last && *last <= '9' && len ) {
741 index = index * 10 + *last - '0';
742 if( index < _con_colors_count ) {
751 color = _con_colors[index];
752 // we dont want to display the color tag and the color index
762 The input line scrolls horizontally if typing goes beyond the right edge
764 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
767 void Con_DrawInput (void)
771 char editlinecopy[257], *text;
773 if (!key_consoleactive)
774 return; // don't draw anything
776 text = strcpy(editlinecopy, key_lines[edit_line]);
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
785 // fill out remainder with spaces
786 for (i = y; i < 256; i++)
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
793 // text[key_linepos + 1] = 0;
795 // prestep if horizontally scrolling
796 if (key_linepos >= con_linewidth)
797 text += 1 + key_linepos - con_linewidth;
800 _Con_DrawString(0, con_vislines - 16, text, con_linewidth, 8, 8, 0);
803 // key_lines[edit_line][key_linepos] = 0;
811 Draws the last few lines of output transparently over the game top
814 void Con_DrawNotify (void)
820 extern char chat_buffer[];
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)
831 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
835 time = con_times[i % con_notify.integer];
838 time = cl.time - time;
839 if (time > con_notifytime.value)
841 text = con_text + (i % con_totallines)*con_linewidth;
843 if (gamemode == GAME_NEXUIZ) {
846 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
847 x = (vid.conwidth - linewidth * 8) / 2;
851 _Con_DrawString(x, v, text, con_linewidth, 8, 8, 0);
857 if (key_dest == key_message)
861 // LordHavoc: speedup, and other improvements
863 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
865 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
866 while (strlen(temptext) >= (size_t) con_linewidth)
868 _Con_DrawString (0, v, temptext, con_linewidth, 8, 8, 0);
869 strcpy(temptext, &temptext[con_linewidth]);
872 if (strlen(temptext) > 0)
874 _Con_DrawString (0, v, temptext, 0, 8, 8, 0);
884 Draws the console with the solid background
885 The typing input line at the bottom should only be drawn if typing is allowed
888 extern char engineversion[40];
889 void Con_DrawConsole (int lines)
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);
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);
905 con_vislines = lines;
907 rows = (lines-16)>>3; // rows of text to draw
908 y = lines - 16 - (rows<<3); // may start slightly negative
910 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
912 j = max(i - con_backscroll, 0);
913 text = con_text + (j % con_totallines)*con_linewidth;
915 _Con_DrawString(0, y, text, con_linewidth, 8, 8, 0);
918 // draw the input prompt, user text, and cursor if desired
925 New function for tab-completion system
927 MEGA Thanks to Taniwha
930 void Con_DisplayList(const char **list)
932 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
933 const char **walk = list;
945 if (pos + maxlen >= width) {
951 for (i = 0; i < (maxlen - len); i++)
963 Con_CompleteCommandLine
965 New function for tab-completion system
967 Thanks to Fett erich@heintz.com
971 void Con_CompleteCommandLine (void)
973 const char *cmd = "", *s;
974 const char **list[3] = {0, 0, 0};
975 int c, v, a, i, cmd_len;
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);
983 if (!(c + v + a)) // No possible matches
986 if (c + v + a == 1) {
988 list[0] = Cmd_CompleteBuildList(s);
990 list[0] = Cvar_CompleteBuildList(s);
992 list[0] = Cmd_CompleteAliasBuildList(s);
994 cmd_len = strlen (cmd);
997 cmd = *(list[0] = Cmd_CompleteBuildList(s));
999 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1001 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1003 cmd_len = strlen (s);
1005 for (i = 0; i < 3; i++) {
1006 char ch = cmd[cmd_len];
1007 const char **l = list[i];
1009 while (*l && (*l)[cmd_len] == ch)
1020 for (i = 0; i < con_linewidth - 4; i++)
1024 // Print Possible Commands
1026 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1027 Con_DisplayList(list[0]);
1031 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1032 Con_DisplayList(list[1]);
1036 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1037 Con_DisplayList(list[2]);
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] = ' ';
1048 key_lines[edit_line][key_linepos] = 0;
1050 for (i = 0; i < 3; i++)
1052 Mem_Free((void *)list[i]);