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) : "";
554 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
555 line[index++] = STRING_COLOR_TAG;
556 // assert( STRING_COLOR_DEFAULT < 10 )
557 line[index++] = STRING_COLOR_DEFAULT + '0';
558 // special color codes for chat messages must always come first
559 // for Con_PrintToHistory to work properly
565 S_LocalSound ("sound/misc/talk.wav");
567 if (gamemode == GAME_NEXUIZ)
574 // go to colored text
580 for (;*timestamp;index++, timestamp++)
581 if (index < sizeof(line) - 2)
582 line[index] = *timestamp;
584 // append the character
585 line[index++] = *msg;
586 // if this is a newline character, we have a complete line to print
587 if (*msg == '\n' || index >= 16000)
589 // terminate the line
593 // send to scrollable buffer
594 if (con_initialized && cls.state != ca_dedicated)
595 Con_PrintToHistory(line, mask);
596 // send to terminal or dedicated server window
600 for (p = (unsigned char *) line;*p; p++)
601 *p = qfont_table[*p];
602 Sys_PrintToTerminal(line);
604 // empty the line buffer
611 // LordHavoc: increased from 4096 to 16384
612 #define MAXPRINTMSG 16384
618 Prints to all appropriate console targets
621 void Con_Printf(const char *fmt, ...)
624 char msg[MAXPRINTMSG];
626 va_start(argptr,fmt);
627 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
637 A Con_Print that only shows up if the "developer" cvar is set
640 void Con_DPrint(const char *msg)
642 if (!developer.integer)
643 return; // don't confuse non-developers with techie stuff...
651 A Con_Printf that only shows up if the "developer" cvar is set
654 void Con_DPrintf(const char *fmt, ...)
657 char msg[MAXPRINTMSG];
659 if (!developer.integer)
660 return; // don't confuse non-developers with techie stuff...
662 va_start(argptr,fmt);
663 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
671 ==============================================================================
675 ==============================================================================
682 The input line scrolls horizontally if typing goes beyond the right edge
684 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
687 void Con_DrawInput (void)
691 char editlinecopy[257], *text;
693 if (!key_consoleactive)
694 return; // don't draw anything
696 text = strcpy(editlinecopy, key_lines[edit_line]);
698 // Advanced Console Editing by Radix radix@planetquake.com
699 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
700 // use strlen of edit_line instead of key_linepos to allow editing
701 // of early characters w/o erasing
705 // fill out remainder with spaces
706 for (i = y; i < 256; i++)
709 // add the cursor frame
710 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
711 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
713 // text[key_linepos + 1] = 0;
715 // prestep if horizontally scrolling
716 if (key_linepos >= con_linewidth)
717 text += 1 + key_linepos - con_linewidth;
720 DrawQ_ColoredString(0, con_vislines - 16, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, NULL );
723 // key_lines[edit_line][key_linepos] = 0;
731 Draws the last few lines of output transparently over the game top
734 void Con_DrawNotify (void)
740 extern char chat_buffer[];
742 int colorindex = -1; //-1 for default
744 if (con_notify.integer < 0)
745 Cvar_SetValueQuick(&con_notify, 0);
746 if (con_notify.integer > MAX_NOTIFYLINES)
747 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
748 if (gamemode == GAME_TRANSFUSION)
752 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
757 time = con_times[i % con_notify.integer];
760 time = cl.time - time;
761 if (time > con_notifytime.value)
763 text = con_text + (i % con_totallines)*con_linewidth;
765 if (gamemode == GAME_NEXUIZ) {
768 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
769 x = (vid_conwidth.integer - linewidth * 8) / 2;
773 DrawQ_ColoredString( x, v, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
779 if (key_dest == key_message)
785 // LordHavoc: speedup, and other improvements
787 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
789 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
790 while (strlen(temptext) >= (size_t) con_linewidth)
792 DrawQ_ColoredString( 0, v, temptext, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
793 strcpy(temptext, &temptext[con_linewidth]);
796 if (strlen(temptext) > 0)
798 DrawQ_ColoredString( 0, v, temptext, 0, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
808 Draws the console with the solid background
809 The typing input line at the bottom should only be drawn if typing is allowed
812 extern char engineversion[40];
813 void Con_DrawConsole (int lines)
822 // draw the background
823 if (scr_conbrightness.value >= 0.01f)
824 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);
826 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
827 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
830 con_vislines = lines;
832 rows = (lines-16)>>3; // rows of text to draw
833 y = lines - 16 - (rows<<3); // may start slightly negative
835 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
837 j = max(i - con_backscroll, 0);
838 text = con_text + (j % con_totallines)*con_linewidth;
840 DrawQ_ColoredString( 0, y, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
843 // draw the input prompt, user text, and cursor if desired
850 New function for tab-completion system
852 MEGA Thanks to Taniwha
855 void Con_DisplayList(const char **list)
857 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
858 const char **walk = list;
870 if (pos + maxlen >= width) {
876 for (i = 0; i < (maxlen - len); i++)
888 Con_CompleteCommandLine
890 New function for tab-completion system
892 Thanks to Fett erich@heintz.com
896 void Con_CompleteCommandLine (void)
898 const char *cmd = "", *s;
899 const char **list[3] = {0, 0, 0};
900 int c, v, a, i, cmd_len;
902 s = key_lines[edit_line] + 1;
903 // Count number of possible matches
904 c = Cmd_CompleteCountPossible(s);
905 v = Cvar_CompleteCountPossible(s);
906 a = Cmd_CompleteAliasCountPossible(s);
908 if (!(c + v + a)) // No possible matches
911 if (c + v + a == 1) {
913 list[0] = Cmd_CompleteBuildList(s);
915 list[0] = Cvar_CompleteBuildList(s);
917 list[0] = Cmd_CompleteAliasBuildList(s);
919 cmd_len = strlen (cmd);
922 cmd = *(list[0] = Cmd_CompleteBuildList(s));
924 cmd = *(list[1] = Cvar_CompleteBuildList(s));
926 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
928 cmd_len = strlen (s);
930 for (i = 0; i < 3; i++) {
931 char ch = cmd[cmd_len];
932 const char **l = list[i];
934 while (*l && (*l)[cmd_len] == ch)
945 for (i = 0; i < con_linewidth - 4; i++)
949 // Print Possible Commands
951 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
952 Con_DisplayList(list[0]);
956 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
957 Con_DisplayList(list[1]);
961 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
962 Con_DisplayList(list[2]);
967 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
968 key_linepos = cmd_len + 1;
969 if (c + v + a == 1) {
970 key_lines[edit_line][key_linepos] = ' ';
973 key_lines[edit_line][key_linepos] = 0;
975 for (i = 0; i < 3; i++)
977 Mem_Free((void *)list[i]);