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 cvar_t log_sync = {0, "log_sync","0"};
76 char crt_log_file [MAX_OSPATH] = "";
77 qfile_t* logfile = NULL;
79 qbyte* logqueue = NULL;
83 void Log_ConPrint (const char *msg);
90 const char* Log_Timestamp (const char *desc)
92 static char timestamp [128];
94 const struct tm *crt_tm;
97 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
99 crt_tm = localtime (&crt_time);
100 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
103 snprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
105 snprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
118 // Allocate a log queue
120 logqueue = Mem_Alloc (tempmempool, logq_size);
123 Cvar_RegisterVariable (&log_file);
124 Cvar_RegisterVariable (&log_sync);
126 // support for the classic Quake option
127 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log with sync on (so it keeps every message up to a crash), see also log_file and log_sync
128 if (COM_CheckParm ("-condebug") != 0)
130 Cvar_SetQuick (&log_file, "qconsole.log");
131 Cvar_SetValueQuick (&log_sync, 1);
143 if (logfile != NULL || log_file.string[0] == '\0')
146 logfile = FS_Open (log_file.string, "at", false);
149 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
150 FS_Print (logfile, Log_Timestamp ("Log started"));
160 void Log_Close (void)
165 FS_Print (logfile, Log_Timestamp ("Log stopped"));
166 FS_Print (logfile, "\n");
170 crt_log_file[0] = '\0';
179 void Log_Start (void)
183 // Dump the contents of the log queue into the log file and free it
184 if (logqueue != NULL)
186 if (logfile != NULL && logq_ind != 0)
187 FS_Write (logfile, logqueue, logq_ind);
201 void Log_ConPrint (const char *msg)
203 static qboolean inprogress = false;
204 // don't allow feedback loops with memory error reports
208 // Until the host is completely initialized, we maintain a log queue
209 // to store the messages, since the log can't be started before
210 if (logqueue != NULL)
212 size_t remain = logq_size - logq_ind;
213 size_t len = strlen (msg);
215 // If we need to enlarge the log queue
218 unsigned int factor = ((logq_ind + len) / logq_size) + 1;
222 newqueue = Mem_Alloc (tempmempool, logq_size);
223 memcpy (newqueue, logqueue, logq_ind);
226 remain = logq_size - logq_ind;
228 memcpy (&logqueue[logq_ind], msg, len);
235 // Check if log_file has changed
236 if (strcmp (crt_log_file, log_file.string) != 0)
242 // If a log file is available
245 FS_Print (logfile, msg);
246 if (log_sync.integer)
258 void Log_Print (const char *logfilename, const char *msg)
261 file = FS_Open(logfilename, "at", true);
274 void Log_Printf (const char *logfilename, const char *fmt, ...)
278 file = FS_Open (logfilename, "at", true);
283 va_start (argptr, fmt);
284 FS_VPrintf (file, fmt, argptr);
293 ==============================================================================
297 ==============================================================================
305 void Con_ToggleConsole_f (void)
307 // toggle the 'user wants console' bit
308 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
309 memset (con_times, 0, sizeof(con_times));
317 void Con_Clear_f (void)
320 memset (con_text, ' ', CON_TEXTSIZE);
329 void Con_ClearNotify (void)
333 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
343 void Con_MessageMode_f (void)
345 key_dest = key_message;
355 void Con_MessageMode2_f (void)
357 key_dest = key_message;
366 If the line width has changed, reformat the buffer.
369 void Con_CheckResize (void)
371 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
372 char tbuf[CON_TEXTSIZE];
374 width = (vid.conwidth >> 3);
376 if (width == con_linewidth)
379 if (width < 1) // video hasn't been initialized yet
382 con_linewidth = width;
383 con_totallines = CON_TEXTSIZE / con_linewidth;
384 memset (con_text, ' ', CON_TEXTSIZE);
388 oldwidth = con_linewidth;
389 con_linewidth = width;
390 oldtotallines = con_totallines;
391 con_totallines = CON_TEXTSIZE / con_linewidth;
392 numlines = oldtotallines;
394 if (con_totallines < numlines)
395 numlines = con_totallines;
399 if (con_linewidth < numchars)
400 numchars = con_linewidth;
402 memcpy (tbuf, con_text, CON_TEXTSIZE);
403 memset (con_text, ' ', CON_TEXTSIZE);
405 for (i=0 ; i<numlines ; i++)
407 for (j=0 ; j<numchars ; j++)
409 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
410 tbuf[((con_current - i + oldtotallines) %
411 oldtotallines) * oldwidth + j];
419 con_current = con_totallines - 1;
429 console_mempool = Mem_AllocPool("console", 0, NULL);
430 con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
431 memset (con_text, ' ', CON_TEXTSIZE);
435 // register our cvars
436 Cvar_RegisterVariable (&con_notifytime);
437 Cvar_RegisterVariable (&con_notify);
439 // register our commands
440 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
441 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
442 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
443 Cmd_AddCommand ("clear", Con_Clear_f);
445 con_initialized = true;
446 Con_Print("Console initialized.\n");
455 void Con_Linefeed (void)
462 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
469 Handles cursor positioning, line wrapping, etc
470 All console printing must go through this in order to be displayed
471 If no console is visible, the notify window will pop up.
474 void Con_PrintToHistory(const char *txt)
481 mask = 128; // go to colored text
482 S_LocalSound ("sound/misc/talk.wav");
486 else if (txt[0] == 2)
488 mask = 128; // go to colored text
498 for (l=0 ; l< con_linewidth ; l++)
503 if (l != con_linewidth && (con_x + l > con_linewidth) )
518 // mark time for transparent overlay
519 if (con_current >= 0)
521 if (con_notify.integer < 0)
522 Cvar_SetValueQuick(&con_notify, 0);
523 if (con_notify.integer > MAX_NOTIFYLINES)
524 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
525 if (con_notify.integer > 0)
526 con_times[con_current % con_notify.integer] = cl.time;
541 default: // display character and advance
542 y = con_current % con_totallines;
543 con_text[y*con_linewidth+con_x] = c | mask;
545 if (con_x >= con_linewidth)
553 /* The translation table between the graphical font and plain ASCII --KB */
554 static char qfont_table[256] = {
555 '\0', '#', '#', '#', '#', '.', '#', '#',
556 '#', 9, 10, '#', ' ', 13, '.', '.',
557 '[', ']', '0', '1', '2', '3', '4', '5',
558 '6', '7', '8', '9', '.', '<', '=', '>',
559 ' ', '!', '"', '#', '$', '%', '&', '\'',
560 '(', ')', '*', '+', ',', '-', '.', '/',
561 '0', '1', '2', '3', '4', '5', '6', '7',
562 '8', '9', ':', ';', '<', '=', '>', '?',
563 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
564 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
565 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
566 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
567 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
568 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
569 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
570 'x', 'y', 'z', '{', '|', '}', '~', '<',
572 '<', '=', '>', '#', '#', '.', '#', '#',
573 '#', '#', ' ', '#', ' ', '>', '.', '.',
574 '[', ']', '0', '1', '2', '3', '4', '5',
575 '6', '7', '8', '9', '.', '<', '=', '>',
576 ' ', '!', '"', '#', '$', '%', '&', '\'',
577 '(', ')', '*', '+', ',', '-', '.', '/',
578 '0', '1', '2', '3', '4', '5', '6', '7',
579 '8', '9', ':', ';', '<', '=', '>', '?',
580 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
581 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
582 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
583 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
584 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
585 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
586 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
587 'x', 'y', 'z', '{', '|', '}', '~', '<'
594 Prints to all appropriate console targets, and adds timestamps
597 extern cvar_t timestamps;
598 extern cvar_t timeformat;
599 extern qboolean sys_nostdout;
600 void Con_Print(const char *msg)
602 static int index = 0;
603 static char line[16384];
609 // if this is the beginning of a new line, print timestamp
610 char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
611 // special color codes for chat messages must always come first
612 // for Con_PrintToHistory to work properly
614 line[index++] = *msg++;
616 for (;*timestamp;index++, timestamp++)
617 if (index < sizeof(line) - 2)
618 line[index] = *timestamp;
620 // append the character
621 line[index++] = *msg;
622 // if this is a newline character, we have a complete line to print
623 if (*msg == '\n' || index >= 16000)
625 // terminate the line
629 // send to scrollable buffer
630 if (con_initialized && cls.state != ca_dedicated)
631 Con_PrintToHistory(line);
632 // send to terminal or dedicated server window
636 for (p = (unsigned char *) line;*p; p++)
637 *p = qfont_table[*p];
638 Sys_PrintToTerminal(line);
640 // empty the line buffer
647 // LordHavoc: increased from 4096 to 16384
648 #define MAXPRINTMSG 16384
654 Prints to all appropriate console targets
657 void Con_Printf(const char *fmt, ...)
660 char msg[MAXPRINTMSG];
662 va_start(argptr,fmt);
663 vsprintf(msg,fmt,argptr);
673 A Con_Print that only shows up if the "developer" cvar is set
676 void Con_DPrint(const char *msg)
678 if (!developer.integer)
679 return; // don't confuse non-developers with techie stuff...
687 A Con_Printf that only shows up if the "developer" cvar is set
690 void Con_DPrintf(const char *fmt, ...)
693 char msg[MAXPRINTMSG];
695 if (!developer.integer)
696 return; // don't confuse non-developers with techie stuff...
698 va_start(argptr,fmt);
699 vsprintf(msg,fmt,argptr);
707 ==============================================================================
711 ==============================================================================
719 The input line scrolls horizontally if typing goes beyond the right edge
721 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
724 void Con_DrawInput (void)
728 char editlinecopy[257], *text;
730 if (!key_consoleactive)
731 return; // don't draw anything
733 text = strcpy(editlinecopy, key_lines[edit_line]);
735 // Advanced Console Editing by Radix radix@planetquake.com
736 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
737 // use strlen of edit_line instead of key_linepos to allow editing
738 // of early characters w/o erasing
742 // fill out remainder with spaces
743 for (i = y; i < 256; i++)
746 // add the cursor frame
747 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
748 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
750 // text[key_linepos + 1] = 0;
752 // prestep if horizontally scrolling
753 if (key_linepos >= con_linewidth)
754 text += 1 + key_linepos - con_linewidth;
757 DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
760 // key_lines[edit_line][key_linepos] = 0;
768 Draws the last few lines of output transparently over the game top
771 void Con_DrawNotify (void)
777 extern char chat_buffer[];
780 if (con_notify.integer < 0)
781 Cvar_SetValueQuick(&con_notify, 0);
782 if (con_notify.integer > MAX_NOTIFYLINES)
783 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
784 if (gamemode == GAME_TRANSFUSION)
788 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
792 time = con_times[i % con_notify.integer];
795 time = cl.time - time;
796 if (time > con_notifytime.value)
798 text = con_text + (i % con_totallines)*con_linewidth;
800 DrawQ_String(0, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
806 if (key_dest == key_message)
810 // LordHavoc: speedup, and other improvements
812 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
814 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
815 while (strlen(temptext) >= (size_t) con_linewidth)
817 DrawQ_String (0, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
818 strcpy(temptext, &temptext[con_linewidth]);
821 if (strlen(temptext) > 0)
823 DrawQ_String (0, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
833 Draws the console with the solid background
834 The typing input line at the bottom should only be drawn if typing is allowed
837 extern char engineversion[40];
838 void Con_DrawConsole (int lines)
846 // draw the background
847 if (scr_conbrightness.value >= 0.01f)
848 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);
850 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
851 DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
854 con_vislines = lines;
856 rows = (lines-16)>>3; // rows of text to draw
857 y = lines - 16 - (rows<<3); // may start slightly negative
859 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
861 j = max(i - con_backscroll, 0);
862 text = con_text + (j % con_totallines)*con_linewidth;
864 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
867 // draw the input prompt, user text, and cursor if desired
874 New function for tab-completion system
876 MEGA Thanks to Taniwha
879 void Con_DisplayList(const char **list)
881 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
882 const char **walk = list;
894 if (pos + maxlen >= width) {
900 for (i = 0; i < (maxlen - len); i++)
912 Con_CompleteCommandLine
914 New function for tab-completion system
916 Thanks to Fett erich@heintz.com
920 void Con_CompleteCommandLine (void)
922 const char *cmd = "", *s;
923 const char **list[3] = {0, 0, 0};
924 int c, v, a, i, cmd_len;
926 s = key_lines[edit_line] + 1;
927 // Count number of possible matches
928 c = Cmd_CompleteCountPossible(s);
929 v = Cvar_CompleteCountPossible(s);
930 a = Cmd_CompleteAliasCountPossible(s);
932 if (!(c + v + a)) // No possible matches
935 if (c + v + a == 1) {
937 list[0] = Cmd_CompleteBuildList(s);
939 list[0] = Cvar_CompleteBuildList(s);
941 list[0] = Cmd_CompleteAliasBuildList(s);
943 cmd_len = strlen (cmd);
946 cmd = *(list[0] = Cmd_CompleteBuildList(s));
948 cmd = *(list[1] = Cvar_CompleteBuildList(s));
950 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
952 cmd_len = strlen (s);
954 for (i = 0; i < 3; i++) {
955 char ch = cmd[cmd_len];
956 const char **l = list[i];
958 while (*l && (*l)[cmd_len] == ch)
969 for (i = 0; i < con_linewidth - 4; i++)
973 // Print Possible Commands
975 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
976 Con_DisplayList(list[0]);
980 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
981 Con_DisplayList(list[1]);
985 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
986 Con_DisplayList(list[2]);
991 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
992 key_linepos = cmd_len + 1;
993 if (c + v + a == 1) {
994 key_lines[edit_line][key_linepos] = ' ';
997 key_lines[edit_line][key_linepos] = 0;
999 for (i = 0; i < 3; i++)
1001 Mem_Free((void *)list[i]);