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 qboolean con_initialized;
58 ==============================================================================
62 ==============================================================================
65 cvar_t log_file = {0, "log_file",""};
66 char crt_log_file [MAX_OSPATH] = "";
67 qfile_t* logfile = NULL;
69 unsigned char* logqueue = NULL;
73 void Log_ConPrint (const char *msg);
80 const char* Log_Timestamp (const char *desc)
82 static char timestamp [128];
84 const struct tm *crt_tm;
87 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
89 crt_tm = localtime (&crt_time);
90 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
93 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
95 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
108 if (logfile != NULL || log_file.string[0] == '\0')
111 logfile = FS_Open (log_file.string, "ab", false, false);
114 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
115 FS_Print (logfile, Log_Timestamp ("Log started"));
125 void Log_Close (void)
130 FS_Print (logfile, Log_Timestamp ("Log stopped"));
131 FS_Print (logfile, "\n");
135 crt_log_file[0] = '\0';
144 void Log_Start (void)
148 // Dump the contents of the log queue into the log file and free it
149 if (logqueue != NULL)
151 if (logfile != NULL && logq_ind != 0)
152 FS_Write (logfile, logqueue, logq_ind);
166 void Log_ConPrint (const char *msg)
168 static qboolean inprogress = false;
170 // don't allow feedback loops with memory error reports
175 // Until the host is completely initialized, we maintain a log queue
176 // to store the messages, since the log can't be started before
177 if (logqueue != NULL)
179 size_t remain = logq_size - logq_ind;
180 size_t len = strlen (msg);
182 // If we need to enlarge the log queue
185 size_t factor = ((logq_ind + len) / logq_size) + 1;
186 unsigned char* newqueue;
189 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
190 memcpy (newqueue, logqueue, logq_ind);
193 remain = logq_size - logq_ind;
195 memcpy (&logqueue[logq_ind], msg, len);
202 // Check if log_file has changed
203 if (strcmp (crt_log_file, log_file.string) != 0)
209 // If a log file is available
211 FS_Print (logfile, msg);
221 void Log_Printf (const char *logfilename, const char *fmt, ...)
225 file = FS_Open (logfilename, "ab", true, false);
230 va_start (argptr, fmt);
231 FS_VPrintf (file, fmt, argptr);
240 ==============================================================================
244 ==============================================================================
252 void Con_ToggleConsole_f (void)
254 // toggle the 'user wants console' bit
255 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
256 memset (con_times, 0, sizeof(con_times));
264 void Con_Clear_f (void)
267 memset (con_text, ' ', CON_TEXTSIZE);
276 void Con_ClearNotify (void)
280 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
290 void Con_MessageMode_f (void)
292 key_dest = key_message;
302 void Con_MessageMode2_f (void)
304 key_dest = key_message;
313 If the line width has changed, reformat the buffer.
316 void Con_CheckResize (void)
318 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
319 char tbuf[CON_TEXTSIZE];
321 width = (vid_conwidth.integer >> 3);
323 if (width == con_linewidth)
326 oldwidth = con_linewidth;
327 con_linewidth = width;
328 oldtotallines = con_totallines;
329 con_totallines = CON_TEXTSIZE / con_linewidth;
330 numlines = oldtotallines;
332 if (con_totallines < numlines)
333 numlines = con_totallines;
337 if (con_linewidth < numchars)
338 numchars = con_linewidth;
340 memcpy (tbuf, con_text, CON_TEXTSIZE);
341 memset (con_text, ' ', CON_TEXTSIZE);
343 for (i=0 ; i<numlines ; i++)
345 for (j=0 ; j<numchars ; j++)
347 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
348 tbuf[((con_current - i + oldtotallines) %
349 oldtotallines) * oldwidth + j];
356 con_current = con_totallines - 1;
366 memset (con_text, ' ', CON_TEXTSIZE);
368 con_totallines = CON_TEXTSIZE / con_linewidth;
370 // Allocate a log queue
371 logq_size = MAX_INPUTLINE;
372 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
375 Cvar_RegisterVariable (&log_file);
377 // support for the classic Quake option
378 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
379 if (COM_CheckParm ("-condebug") != 0)
380 Cvar_SetQuick (&log_file, "qconsole.log");
383 void Con_Init_Commands (void)
385 // register our cvars
386 Cvar_RegisterVariable (&con_notifytime);
387 Cvar_RegisterVariable (&con_notify);
389 // register our commands
390 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
391 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
392 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
393 Cmd_AddCommand ("clear", Con_Clear_f);
395 con_initialized = true;
396 Con_Print("Console initialized.\n");
405 void Con_Linefeed (void)
412 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
419 Handles cursor positioning, line wrapping, etc
420 All console printing must go through this in order to be displayed
421 If no console is visible, the notify window will pop up.
424 void Con_PrintToHistory(const char *txt, int mask)
432 for (l=0 ; l< con_linewidth ; l++)
437 if (l != con_linewidth && (con_x + l > con_linewidth) )
452 // mark time for transparent overlay
453 if (con_current >= 0)
455 if (con_notify.integer < 0)
456 Cvar_SetValueQuick(&con_notify, 0);
457 if (con_notify.integer > MAX_NOTIFYLINES)
458 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
459 if (con_notify.integer > 0)
460 con_times[con_current % con_notify.integer] = cl.time;
475 default: // display character and advance
476 y = con_current % con_totallines;
477 con_text[y*con_linewidth+con_x] = c | mask;
479 if (con_x >= con_linewidth)
487 /* The translation table between the graphical font and plain ASCII --KB */
488 static char qfont_table[256] = {
489 '\0', '#', '#', '#', '#', '.', '#', '#',
490 '#', 9, 10, '#', ' ', 13, '.', '.',
491 '[', ']', '0', '1', '2', '3', '4', '5',
492 '6', '7', '8', '9', '.', '<', '=', '>',
493 ' ', '!', '"', '#', '$', '%', '&', '\'',
494 '(', ')', '*', '+', ',', '-', '.', '/',
495 '0', '1', '2', '3', '4', '5', '6', '7',
496 '8', '9', ':', ';', '<', '=', '>', '?',
497 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
498 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
499 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
500 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
501 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
502 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
503 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
504 'x', 'y', 'z', '{', '|', '}', '~', '<',
506 '<', '=', '>', '#', '#', '.', '#', '#',
507 '#', '#', ' ', '#', ' ', '>', '.', '.',
508 '[', ']', '0', '1', '2', '3', '4', '5',
509 '6', '7', '8', '9', '.', '<', '=', '>',
510 ' ', '!', '"', '#', '$', '%', '&', '\'',
511 '(', ')', '*', '+', ',', '-', '.', '/',
512 '0', '1', '2', '3', '4', '5', '6', '7',
513 '8', '9', ':', ';', '<', '=', '>', '?',
514 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
515 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
516 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
517 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
518 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
519 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
520 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
521 'x', 'y', 'z', '{', '|', '}', '~', '<'
528 Prints to all appropriate console targets, and adds timestamps
531 extern cvar_t timestamps;
532 extern cvar_t timeformat;
533 extern qboolean sys_nostdout;
534 void Con_Print(const char *msg)
537 static int index = 0;
538 static char line[MAX_INPUTLINE];
544 // if this is the beginning of a new line, print timestamp
545 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
547 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
548 line[index++] = STRING_COLOR_TAG;
549 // assert( STRING_COLOR_DEFAULT < 10 )
550 line[index++] = STRING_COLOR_DEFAULT + '0';
551 // special color codes for chat messages must always come first
552 // for Con_PrintToHistory to work properly
558 S_LocalSound ("sound/misc/talk.wav");
560 if (gamemode == GAME_NEXUIZ)
567 // go to colored text
573 for (;*timestamp;index++, timestamp++)
574 if (index < (int)sizeof(line) - 2)
575 line[index] = *timestamp;
577 // append the character
578 line[index++] = *msg;
579 // if this is a newline character, we have a complete line to print
580 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
582 // terminate the line
586 // send to scrollable buffer
587 if (con_initialized && cls.state != ca_dedicated)
588 Con_PrintToHistory(line, mask);
589 // send to terminal or dedicated server window
593 for (p = (unsigned char *) line;*p; p++)
594 *p = qfont_table[*p];
595 Sys_PrintToTerminal(line);
597 // empty the line buffer
608 Prints to all appropriate console targets
611 void Con_Printf(const char *fmt, ...)
614 char msg[MAX_INPUTLINE];
616 va_start(argptr,fmt);
617 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
627 A Con_Print that only shows up if the "developer" cvar is set
630 void Con_DPrint(const char *msg)
632 if (!developer.integer)
633 return; // don't confuse non-developers with techie stuff...
641 A Con_Printf that only shows up if the "developer" cvar is set
644 void Con_DPrintf(const char *fmt, ...)
647 char msg[MAX_INPUTLINE];
649 if (!developer.integer)
650 return; // don't confuse non-developers with techie stuff...
652 va_start(argptr,fmt);
653 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
661 ==============================================================================
665 ==============================================================================
672 The input line scrolls horizontally if typing goes beyond the right edge
674 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
677 void Con_DrawInput (void)
681 char editlinecopy[MAX_INPUTLINE+1], *text;
683 if (!key_consoleactive)
684 return; // don't draw anything
686 text = strcpy(editlinecopy, key_lines[edit_line]);
688 // Advanced Console Editing by Radix radix@planetquake.com
689 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
690 // use strlen of edit_line instead of key_linepos to allow editing
691 // of early characters w/o erasing
693 y = (int)strlen(text);
695 // fill out remainder with spaces
696 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
699 // add the cursor frame
700 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
701 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
703 // text[key_linepos + 1] = 0;
705 // prestep if horizontally scrolling
706 if (key_linepos >= con_linewidth)
707 text += 1 + key_linepos - con_linewidth;
710 DrawQ_ColoredString(0, con_vislines - 16, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, NULL );
713 // key_lines[edit_line][key_linepos] = 0;
721 Draws the last few lines of output transparently over the game top
724 void Con_DrawNotify (void)
730 char temptext[MAX_INPUTLINE];
731 int colorindex = -1; //-1 for default
733 if (con_notify.integer < 0)
734 Cvar_SetValueQuick(&con_notify, 0);
735 if (con_notify.integer > MAX_NOTIFYLINES)
736 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
737 if (gamemode == GAME_TRANSFUSION)
741 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
746 time = con_times[i % con_notify.integer];
749 time = cl.time - time;
750 if (time > con_notifytime.value)
752 text = con_text + (i % con_totallines)*con_linewidth;
754 if (gamemode == GAME_NEXUIZ) {
757 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
758 x = (vid_conwidth.integer - linewidth * 8) / 2;
762 DrawQ_ColoredString( x, v, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
768 if (key_dest == key_message)
774 // LordHavoc: speedup, and other improvements
776 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
778 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
779 while ((int)strlen(temptext) >= con_linewidth)
781 DrawQ_ColoredString( 0, v, temptext, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
782 strcpy(temptext, &temptext[con_linewidth]);
785 if (strlen(temptext) > 0)
787 DrawQ_ColoredString( 0, v, temptext, 0, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
797 Draws the console with the solid background
798 The typing input line at the bottom should only be drawn if typing is allowed
801 void Con_DrawConsole (int lines)
810 // draw the background
811 if (scr_conbrightness.value >= 0.01f)
812 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);
814 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
815 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
818 con_vislines = lines;
820 rows = (lines-16)>>3; // rows of text to draw
821 y = lines - 16 - (rows<<3); // may start slightly negative
823 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
825 j = max(i - con_backscroll, 0);
826 text = con_text + (j % con_totallines)*con_linewidth;
828 DrawQ_ColoredString( 0, y, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
831 // draw the input prompt, user text, and cursor if desired
838 New function for tab-completion system
840 MEGA Thanks to Taniwha
843 void Con_DisplayList(const char **list)
845 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
846 const char **walk = list;
849 len = (int)strlen(*walk);
857 len = (int)strlen(*list);
858 if (pos + maxlen >= width) {
864 for (i = 0; i < (maxlen - len); i++)
876 Con_CompleteCommandLine
878 New function for tab-completion system
880 Thanks to Fett erich@heintz.com
884 void Con_CompleteCommandLine (void)
886 const char *cmd = "", *s;
887 const char **list[3] = {0, 0, 0};
888 int c, v, a, i, cmd_len;
890 s = key_lines[edit_line] + 1;
891 // Count number of possible matches
892 c = Cmd_CompleteCountPossible(s);
893 v = Cvar_CompleteCountPossible(s);
894 a = Cmd_CompleteAliasCountPossible(s);
896 if (!(c + v + a)) // No possible matches
899 if (c + v + a == 1) {
901 list[0] = Cmd_CompleteBuildList(s);
903 list[0] = Cvar_CompleteBuildList(s);
905 list[0] = Cmd_CompleteAliasBuildList(s);
907 cmd_len = (int)strlen (cmd);
910 cmd = *(list[0] = Cmd_CompleteBuildList(s));
912 cmd = *(list[1] = Cvar_CompleteBuildList(s));
914 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
916 cmd_len = (int)strlen (s);
918 for (i = 0; i < 3; i++) {
919 char ch = cmd[cmd_len];
920 const char **l = list[i];
922 while (*l && (*l)[cmd_len] == ch)
933 for (i = 0; i < con_linewidth - 4; i++)
937 // Print Possible Commands
939 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
940 Con_DisplayList(list[0]);
944 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
945 Con_DisplayList(list[1]);
949 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
950 Con_DisplayList(list[2]);
955 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
956 key_linepos = cmd_len + 1;
957 if (c + v + a == 1) {
958 key_lines[edit_line][key_linepos] = ' ';
961 key_lines[edit_line][key_linepos] = 0;
963 for (i = 0; i < 3; i++)
965 Mem_Free((void *)list[i]);