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);
132 unlink (va("%s/qconsole.log", fs_gamedir));
144 if (logfile != NULL || log_file.string[0] == '\0')
147 logfile = FS_Open (log_file.string, "at", false);
150 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
151 FS_Print (logfile, Log_Timestamp ("Log started"));
161 void Log_Close (void)
166 FS_Print (logfile, Log_Timestamp ("Log stopped"));
167 FS_Print (logfile, "\n");
171 crt_log_file[0] = '\0';
180 void Log_Start (void)
184 // Dump the contents of the log queue into the log file and free it
185 if (logqueue != NULL)
187 if (logfile != NULL && logq_ind != 0)
188 FS_Write (logfile, logqueue, logq_ind);
202 void Log_ConPrint (const char *msg)
204 static qboolean inprogress = false;
205 // don't allow feedback loops with memory error reports
209 // Until the host is completely initialized, we maintain a log queue
210 // to store the messages, since the log can't be started before
211 if (logqueue != NULL)
213 size_t remain = logq_size - logq_ind;
214 size_t len = strlen (msg);
216 // If we need to enlarge the log queue
219 unsigned int factor = ((logq_ind + len) / logq_size) + 1;
223 newqueue = Mem_Alloc (tempmempool, logq_size);
224 memcpy (newqueue, logqueue, logq_ind);
227 remain = logq_size - logq_ind;
229 memcpy (&logqueue[logq_ind], msg, len);
236 // Check if log_file has changed
237 if (strcmp (crt_log_file, log_file.string) != 0)
243 // If a log file is available
246 FS_Print (logfile, msg);
247 if (log_sync.integer)
259 void Log_Print (const char *logfilename, const char *msg)
262 file = FS_Open(logfilename, "at", true);
275 void Log_Printf (const char *logfilename, const char *fmt, ...)
279 file = FS_Open (logfilename, "at", true);
284 va_start (argptr, fmt);
285 FS_VPrintf (file, fmt, argptr);
294 ==============================================================================
298 ==============================================================================
306 void Con_ToggleConsole_f (void)
308 // toggle the 'user wants console' bit
309 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
310 memset (con_times, 0, sizeof(con_times));
318 void Con_Clear_f (void)
321 memset (con_text, ' ', CON_TEXTSIZE);
330 void Con_ClearNotify (void)
334 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
344 void Con_MessageMode_f (void)
346 key_dest = key_message;
356 void Con_MessageMode2_f (void)
358 key_dest = key_message;
367 If the line width has changed, reformat the buffer.
370 void Con_CheckResize (void)
372 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
373 char tbuf[CON_TEXTSIZE];
375 width = (vid.conwidth >> 3);
377 if (width == con_linewidth)
380 if (width < 1) // video hasn't been initialized yet
383 con_linewidth = width;
384 con_totallines = CON_TEXTSIZE / con_linewidth;
385 memset (con_text, ' ', CON_TEXTSIZE);
389 oldwidth = con_linewidth;
390 con_linewidth = width;
391 oldtotallines = con_totallines;
392 con_totallines = CON_TEXTSIZE / con_linewidth;
393 numlines = oldtotallines;
395 if (con_totallines < numlines)
396 numlines = con_totallines;
400 if (con_linewidth < numchars)
401 numchars = con_linewidth;
403 memcpy (tbuf, con_text, CON_TEXTSIZE);
404 memset (con_text, ' ', CON_TEXTSIZE);
406 for (i=0 ; i<numlines ; i++)
408 for (j=0 ; j<numchars ; j++)
410 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
411 tbuf[((con_current - i + oldtotallines) %
412 oldtotallines) * oldwidth + j];
420 con_current = con_totallines - 1;
430 console_mempool = Mem_AllocPool("console", 0, NULL);
431 con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
432 memset (con_text, ' ', CON_TEXTSIZE);
436 Con_Print("Console initialized.\n");
438 // register our cvars
439 Cvar_RegisterVariable (&con_notifytime);
440 Cvar_RegisterVariable (&con_notify);
442 // register our commands
443 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
444 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
445 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
446 Cmd_AddCommand ("clear", Con_Clear_f);
447 con_initialized = true;
456 void Con_Linefeed (void)
463 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
470 Handles cursor positioning, line wrapping, etc
471 All console printing must go through this in order to be displayed
472 If no console is visible, the notify window will pop up.
475 void Con_PrintToHistory(const char *txt)
482 mask = 128; // go to colored text
483 S_LocalSound ("sound/misc/talk.wav");
487 else if (txt[0] == 2)
489 mask = 128; // go to colored text
499 for (l=0 ; l< con_linewidth ; l++)
504 if (l != con_linewidth && (con_x + l > con_linewidth) )
519 // mark time for transparent overlay
520 if (con_current >= 0)
522 if (con_notify.integer < 0)
523 Cvar_SetValueQuick(&con_notify, 0);
524 if (con_notify.integer > MAX_NOTIFYLINES)
525 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
526 if (con_notify.integer > 0)
527 con_times[con_current % con_notify.integer] = cl.time;
542 default: // display character and advance
543 y = con_current % con_totallines;
544 con_text[y*con_linewidth+con_x] = c | mask;
546 if (con_x >= con_linewidth)
554 /* The translation table between the graphical font and plain ASCII --KB */
555 static char qfont_table[256] = {
556 '\0', '#', '#', '#', '#', '.', '#', '#',
557 '#', 9, 10, '#', ' ', 13, '.', '.',
558 '[', ']', '0', '1', '2', '3', '4', '5',
559 '6', '7', '8', '9', '.', '<', '=', '>',
560 ' ', '!', '"', '#', '$', '%', '&', '\'',
561 '(', ')', '*', '+', ',', '-', '.', '/',
562 '0', '1', '2', '3', '4', '5', '6', '7',
563 '8', '9', ':', ';', '<', '=', '>', '?',
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', '[', '\\', ']', '^', '_',
568 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
569 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
570 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
571 'x', 'y', 'z', '{', '|', '}', '~', '<',
573 '<', '=', '>', '#', '#', '.', '#', '#',
574 '#', '#', ' ', '#', ' ', '>', '.', '.',
575 '[', ']', '0', '1', '2', '3', '4', '5',
576 '6', '7', '8', '9', '.', '<', '=', '>',
577 ' ', '!', '"', '#', '$', '%', '&', '\'',
578 '(', ')', '*', '+', ',', '-', '.', '/',
579 '0', '1', '2', '3', '4', '5', '6', '7',
580 '8', '9', ':', ';', '<', '=', '>', '?',
581 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
582 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
583 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
584 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
585 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
586 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
587 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
588 'x', 'y', 'z', '{', '|', '}', '~', '<'
595 Prints to all appropriate console targets, and adds timestamps
598 extern cvar_t timestamps;
599 extern cvar_t timeformat;
600 extern qboolean sys_nostdout;
601 void Con_Print(const char *msg)
603 static int index = 0;
604 static char line[16384];
610 // if this is the beginning of a new line, print timestamp
611 char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
612 // special color codes for chat messages must always come first
613 // for Con_PrintToHistory to work properly
615 line[index++] = *msg++;
617 for (;*timestamp;index++, timestamp++)
618 if (index < sizeof(line) - 2)
619 line[index] = *timestamp;
621 // append the character
622 line[index++] = *msg;
623 // if this is a newline character, we have a complete line to print
624 if (*msg == '\n' || index >= 16000)
626 // terminate the line
630 // send to scrollable buffer
631 if (con_initialized && cls.state != ca_dedicated)
632 Con_PrintToHistory(line);
633 // send to terminal or dedicated server window
637 for (p = (unsigned char *) line;*p; p++)
638 *p = qfont_table[*p];
639 Sys_PrintToTerminal(line);
641 // empty the line buffer
648 // LordHavoc: increased from 4096 to 16384
649 #define MAXPRINTMSG 16384
655 Prints to all appropriate console targets
658 void Con_Printf(const char *fmt, ...)
661 char msg[MAXPRINTMSG];
663 va_start(argptr,fmt);
664 vsprintf(msg,fmt,argptr);
674 A Con_Print that only shows up if the "developer" cvar is set
677 void Con_DPrint(const char *msg)
679 if (!developer.integer)
680 return; // don't confuse non-developers with techie stuff...
688 A Con_Printf that only shows up if the "developer" cvar is set
691 void Con_DPrintf(const char *fmt, ...)
694 char msg[MAXPRINTMSG];
696 if (!developer.integer)
697 return; // don't confuse non-developers with techie stuff...
699 va_start(argptr,fmt);
700 vsprintf(msg,fmt,argptr);
711 Okay to call even when the screen can't be updated
714 void Con_SafePrint(const char *msg)
723 Okay to call even when the screen can't be updated
726 void Con_SafePrintf(const char *fmt, ...)
729 char msg[MAXPRINTMSG];
731 va_start(argptr,fmt);
732 vsprintf(msg,fmt,argptr);
740 ==============================================================================
744 ==============================================================================
752 The input line scrolls horizontally if typing goes beyond the right edge
754 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
757 void Con_DrawInput (void)
761 char editlinecopy[257], *text;
763 if (!key_consoleactive)
764 return; // don't draw anything
766 text = strcpy(editlinecopy, key_lines[edit_line]);
768 // Advanced Console Editing by Radix radix@planetquake.com
769 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
770 // use strlen of edit_line instead of key_linepos to allow editing
771 // of early characters w/o erasing
775 // fill out remainder with spaces
776 for (i = y; i < 256; i++)
779 // add the cursor frame
780 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
781 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
783 // text[key_linepos + 1] = 0;
785 // prestep if horizontally scrolling
786 if (key_linepos >= con_linewidth)
787 text += 1 + key_linepos - con_linewidth;
790 DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
793 // key_lines[edit_line][key_linepos] = 0;
801 Draws the last few lines of output transparently over the game top
804 void Con_DrawNotify (void)
810 extern char chat_buffer[];
813 if (con_notify.integer < 0)
814 Cvar_SetValueQuick(&con_notify, 0);
815 if (con_notify.integer > MAX_NOTIFYLINES)
816 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
817 if (gamemode == GAME_TRANSFUSION)
821 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
825 time = con_times[i % con_notify.integer];
828 time = cl.time - time;
829 if (time > con_notifytime.value)
831 text = con_text + (i % con_totallines)*con_linewidth;
833 DrawQ_String(0, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
839 if (key_dest == key_message)
843 // LordHavoc: speedup, and other improvements
845 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
847 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
848 while (strlen(temptext) >= (size_t) con_linewidth)
850 DrawQ_String (0, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
851 strcpy(temptext, &temptext[con_linewidth]);
854 if (strlen(temptext) > 0)
856 DrawQ_String (0, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
866 Draws the console with the solid background
867 The typing input line at the bottom should only be drawn if typing is allowed
870 extern char engineversion[40];
871 void Con_DrawConsole (int lines)
879 // draw the background
880 if (scr_conbrightness.value >= 0.01f)
881 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);
883 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
884 DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
887 con_vislines = lines;
889 rows = (lines-16)>>3; // rows of text to draw
890 y = lines - 16 - (rows<<3); // may start slightly negative
892 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
894 j = max(i - con_backscroll, 0);
895 text = con_text + (j % con_totallines)*con_linewidth;
897 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
900 // draw the input prompt, user text, and cursor if desired
907 New function for tab-completion system
909 MEGA Thanks to Taniwha
912 void Con_DisplayList(const char **list)
914 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
915 const char **walk = list;
927 if (pos + maxlen >= width) {
933 for (i = 0; i < (maxlen - len); i++)
945 Con_CompleteCommandLine
947 New function for tab-completion system
949 Thanks to Fett erich@heintz.com
953 void Con_CompleteCommandLine (void)
955 const char *cmd = "", *s;
956 const char **list[3] = {0, 0, 0};
957 int c, v, a, i, cmd_len;
959 s = key_lines[edit_line] + 1;
960 // Count number of possible matches
961 c = Cmd_CompleteCountPossible(s);
962 v = Cvar_CompleteCountPossible(s);
963 a = Cmd_CompleteAliasCountPossible(s);
965 if (!(c + v + a)) // No possible matches
968 if (c + v + a == 1) {
970 list[0] = Cmd_CompleteBuildList(s);
972 list[0] = Cvar_CompleteBuildList(s);
974 list[0] = Cmd_CompleteAliasBuildList(s);
976 cmd_len = strlen (cmd);
979 cmd = *(list[0] = Cmd_CompleteBuildList(s));
981 cmd = *(list[1] = Cvar_CompleteBuildList(s));
983 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
985 cmd_len = strlen (s);
987 for (i = 0; i < 3; i++) {
988 char ch = cmd[cmd_len];
989 const char **l = list[i];
991 while (*l && (*l)[cmd_len] == ch)
1002 for (i = 0; i < con_linewidth - 4; i++)
1006 // Print Possible Commands
1008 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1009 Con_DisplayList(list[0]);
1013 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1014 Con_DisplayList(list[1]);
1018 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1019 Con_DisplayList(list[2]);
1024 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
1025 key_linepos = cmd_len + 1;
1026 if (c + v + a == 1) {
1027 key_lines[edit_line][key_linepos] = ' ';
1030 key_lines[edit_line][key_linepos] = 0;
1032 for (i = 0; i < 3; i++)
1034 Mem_Free((void *)list[i]);