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!
557 // special color codes for chat messages must always come first
558 // for Con_PrintToHistory to work properly
564 S_LocalSound ("sound/misc/talk.wav");
566 if (gamemode == GAME_NEXUIZ)
573 // go to colored text
579 for (;*timestamp;index++, timestamp++)
580 if (index < sizeof(line) - 2)
581 line[index] = *timestamp;
583 // append the character
584 line[index++] = *msg;
585 // if this is a newline character, we have a complete line to print
586 if (*msg == '\n' || index >= 16000)
588 // terminate the line
592 // send to scrollable buffer
593 if (con_initialized && cls.state != ca_dedicated)
594 Con_PrintToHistory(line, mask);
595 // send to terminal or dedicated server window
599 for (p = (unsigned char *) line;*p; p++)
600 *p = qfont_table[*p];
601 Sys_PrintToTerminal(line);
603 // empty the line buffer
610 // LordHavoc: increased from 4096 to 16384
611 #define MAXPRINTMSG 16384
617 Prints to all appropriate console targets
620 void Con_Printf(const char *fmt, ...)
623 char msg[MAXPRINTMSG];
625 va_start(argptr,fmt);
626 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
636 A Con_Print that only shows up if the "developer" cvar is set
639 void Con_DPrint(const char *msg)
641 if (!developer.integer)
642 return; // don't confuse non-developers with techie stuff...
650 A Con_Printf that only shows up if the "developer" cvar is set
653 void Con_DPrintf(const char *fmt, ...)
656 char msg[MAXPRINTMSG];
658 if (!developer.integer)
659 return; // don't confuse non-developers with techie stuff...
661 va_start(argptr,fmt);
662 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
670 ==============================================================================
674 ==============================================================================
681 The input line scrolls horizontally if typing goes beyond the right edge
683 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
686 void Con_DrawInput (void)
690 char editlinecopy[257], *text;
692 if (!key_consoleactive)
693 return; // don't draw anything
695 text = strcpy(editlinecopy, key_lines[edit_line]);
697 // Advanced Console Editing by Radix radix@planetquake.com
698 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
699 // use strlen of edit_line instead of key_linepos to allow editing
700 // of early characters w/o erasing
704 // fill out remainder with spaces
705 for (i = y; i < 256; i++)
708 // add the cursor frame
709 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
710 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
712 // text[key_linepos + 1] = 0;
714 // prestep if horizontally scrolling
715 if (key_linepos >= con_linewidth)
716 text += 1 + key_linepos - con_linewidth;
719 DrawQ_ColoredString(0, con_vislines - 16, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, NULL );
722 // key_lines[edit_line][key_linepos] = 0;
730 Draws the last few lines of output transparently over the game top
733 void Con_DrawNotify (void)
739 extern char chat_buffer[];
741 int colorindex = -1; //-1 for default
743 if (con_notify.integer < 0)
744 Cvar_SetValueQuick(&con_notify, 0);
745 if (con_notify.integer > MAX_NOTIFYLINES)
746 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
747 if (gamemode == GAME_TRANSFUSION)
751 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
756 time = con_times[i % con_notify.integer];
759 time = cl.time - time;
760 if (time > con_notifytime.value)
762 text = con_text + (i % con_totallines)*con_linewidth;
764 if (gamemode == GAME_NEXUIZ) {
767 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
768 x = (vid_conwidth.integer - linewidth * 8) / 2;
772 DrawQ_ColoredString( x, v, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
778 if (key_dest == key_message)
784 // LordHavoc: speedup, and other improvements
786 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
788 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
789 while (strlen(temptext) >= (size_t) con_linewidth)
791 DrawQ_ColoredString( 0, v, temptext, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
792 strcpy(temptext, &temptext[con_linewidth]);
795 if (strlen(temptext) > 0)
797 DrawQ_ColoredString( 0, v, temptext, 0, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
807 Draws the console with the solid background
808 The typing input line at the bottom should only be drawn if typing is allowed
811 extern char engineversion[40];
812 void Con_DrawConsole (int lines)
821 // draw the background
822 if (scr_conbrightness.value >= 0.01f)
823 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);
825 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
826 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
829 con_vislines = lines;
831 rows = (lines-16)>>3; // rows of text to draw
832 y = lines - 16 - (rows<<3); // may start slightly negative
834 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
836 j = max(i - con_backscroll, 0);
837 text = con_text + (j % con_totallines)*con_linewidth;
839 DrawQ_ColoredString( 0, y, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
842 // draw the input prompt, user text, and cursor if desired
849 New function for tab-completion system
851 MEGA Thanks to Taniwha
854 void Con_DisplayList(const char **list)
856 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
857 const char **walk = list;
869 if (pos + maxlen >= width) {
875 for (i = 0; i < (maxlen - len); i++)
887 Con_CompleteCommandLine
889 New function for tab-completion system
891 Thanks to Fett erich@heintz.com
895 void Con_CompleteCommandLine (void)
897 const char *cmd = "", *s;
898 const char **list[3] = {0, 0, 0};
899 int c, v, a, i, cmd_len;
901 s = key_lines[edit_line] + 1;
902 // Count number of possible matches
903 c = Cmd_CompleteCountPossible(s);
904 v = Cvar_CompleteCountPossible(s);
905 a = Cmd_CompleteAliasCountPossible(s);
907 if (!(c + v + a)) // No possible matches
910 if (c + v + a == 1) {
912 list[0] = Cmd_CompleteBuildList(s);
914 list[0] = Cvar_CompleteBuildList(s);
916 list[0] = Cmd_CompleteAliasBuildList(s);
918 cmd_len = strlen (cmd);
921 cmd = *(list[0] = Cmd_CompleteBuildList(s));
923 cmd = *(list[1] = Cvar_CompleteBuildList(s));
925 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
927 cmd_len = strlen (s);
929 for (i = 0; i < 3; i++) {
930 char ch = cmd[cmd_len];
931 const char **l = list[i];
933 while (*l && (*l)[cmd_len] == ch)
944 for (i = 0; i < con_linewidth - 4; i++)
948 // Print Possible Commands
950 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
951 Con_DisplayList(list[0]);
955 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
956 Con_DisplayList(list[1]);
960 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
961 Con_DisplayList(list[2]);
966 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
967 key_linepos = cmd_len + 1;
968 if (c + v + a == 1) {
969 key_lines[edit_line][key_linepos] = ' ';
972 key_lines[edit_line][key_linepos] = 0;
974 for (i = 0; i < 3; i++)
976 Mem_Free((void *)list[i]);