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 qbyte* 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;
189 newqueue = (qbyte *)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
372 logqueue = (qbyte *)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[16384];
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 >= 16000)
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
604 // LordHavoc: increased from 4096 to 16384
605 #define MAXPRINTMSG 16384
611 Prints to all appropriate console targets
614 void Con_Printf(const char *fmt, ...)
617 char msg[MAXPRINTMSG];
619 va_start(argptr,fmt);
620 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
630 A Con_Print that only shows up if the "developer" cvar is set
633 void Con_DPrint(const char *msg)
635 if (!developer.integer)
636 return; // don't confuse non-developers with techie stuff...
644 A Con_Printf that only shows up if the "developer" cvar is set
647 void Con_DPrintf(const char *fmt, ...)
650 char msg[MAXPRINTMSG];
652 if (!developer.integer)
653 return; // don't confuse non-developers with techie stuff...
655 va_start(argptr,fmt);
656 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
664 ==============================================================================
668 ==============================================================================
675 The input line scrolls horizontally if typing goes beyond the right edge
677 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
680 void Con_DrawInput (void)
684 char editlinecopy[257], *text;
686 if (!key_consoleactive)
687 return; // don't draw anything
689 text = strcpy(editlinecopy, key_lines[edit_line]);
691 // Advanced Console Editing by Radix radix@planetquake.com
692 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
693 // use strlen of edit_line instead of key_linepos to allow editing
694 // of early characters w/o erasing
696 y = (int)strlen(text);
698 // fill out remainder with spaces
699 for (i = y; i < 256; i++)
702 // add the cursor frame
703 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
704 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
706 // text[key_linepos + 1] = 0;
708 // prestep if horizontally scrolling
709 if (key_linepos >= con_linewidth)
710 text += 1 + key_linepos - con_linewidth;
713 DrawQ_ColoredString(0, con_vislines - 16, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, NULL );
716 // key_lines[edit_line][key_linepos] = 0;
724 Draws the last few lines of output transparently over the game top
727 void Con_DrawNotify (void)
733 extern char chat_buffer[256];
735 int colorindex = -1; //-1 for default
737 if (con_notify.integer < 0)
738 Cvar_SetValueQuick(&con_notify, 0);
739 if (con_notify.integer > MAX_NOTIFYLINES)
740 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
741 if (gamemode == GAME_TRANSFUSION)
745 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
750 time = con_times[i % con_notify.integer];
753 time = cl.time - time;
754 if (time > con_notifytime.value)
756 text = con_text + (i % con_totallines)*con_linewidth;
758 if (gamemode == GAME_NEXUIZ) {
761 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
762 x = (vid_conwidth.integer - linewidth * 8) / 2;
766 DrawQ_ColoredString( x, v, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
772 if (key_dest == key_message)
778 // LordHavoc: speedup, and other improvements
780 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
782 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
783 while ((int)strlen(temptext) >= con_linewidth)
785 DrawQ_ColoredString( 0, v, temptext, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
786 strcpy(temptext, &temptext[con_linewidth]);
789 if (strlen(temptext) > 0)
791 DrawQ_ColoredString( 0, v, temptext, 0, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
801 Draws the console with the solid background
802 The typing input line at the bottom should only be drawn if typing is allowed
805 void Con_DrawConsole (int lines)
814 // draw the background
815 if (scr_conbrightness.value >= 0.01f)
816 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);
818 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
819 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
822 con_vislines = lines;
824 rows = (lines-16)>>3; // rows of text to draw
825 y = lines - 16 - (rows<<3); // may start slightly negative
827 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
829 j = max(i - con_backscroll, 0);
830 text = con_text + (j % con_totallines)*con_linewidth;
832 DrawQ_ColoredString( 0, y, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
835 // draw the input prompt, user text, and cursor if desired
842 New function for tab-completion system
844 MEGA Thanks to Taniwha
847 void Con_DisplayList(const char **list)
849 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
850 const char **walk = list;
853 len = (int)strlen(*walk);
861 len = (int)strlen(*list);
862 if (pos + maxlen >= width) {
868 for (i = 0; i < (maxlen - len); i++)
880 Con_CompleteCommandLine
882 New function for tab-completion system
884 Thanks to Fett erich@heintz.com
888 void Con_CompleteCommandLine (void)
890 const char *cmd = "", *s;
891 const char **list[3] = {0, 0, 0};
892 int c, v, a, i, cmd_len;
894 s = key_lines[edit_line] + 1;
895 // Count number of possible matches
896 c = Cmd_CompleteCountPossible(s);
897 v = Cvar_CompleteCountPossible(s);
898 a = Cmd_CompleteAliasCountPossible(s);
900 if (!(c + v + a)) // No possible matches
903 if (c + v + a == 1) {
905 list[0] = Cmd_CompleteBuildList(s);
907 list[0] = Cvar_CompleteBuildList(s);
909 list[0] = Cmd_CompleteAliasBuildList(s);
911 cmd_len = (int)strlen (cmd);
914 cmd = *(list[0] = Cmd_CompleteBuildList(s));
916 cmd = *(list[1] = Cvar_CompleteBuildList(s));
918 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
920 cmd_len = (int)strlen (s);
922 for (i = 0; i < 3; i++) {
923 char ch = cmd[cmd_len];
924 const char **l = list[i];
926 while (*l && (*l)[cmd_len] == ch)
937 for (i = 0; i < con_linewidth - 4; i++)
941 // Print Possible Commands
943 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
944 Con_DisplayList(list[0]);
948 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
949 Con_DisplayList(list[1]);
953 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
954 Con_DisplayList(list[2]);
959 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
960 key_linepos = cmd_len + 1;
961 if (c + v + a == 1) {
962 key_lines[edit_line][key_linepos] = ' ';
965 key_lines[edit_line][key_linepos] = 0;
967 for (i = 0; i < 3; i++)
969 Mem_Free((void *)list[i]);