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 // Until the host is completely initialized, we maintain a log queue
205 // to store the messages, since the log can't be started before
206 if (logqueue != NULL)
208 size_t remain = logq_size - logq_ind;
209 size_t len = strlen (msg);
211 // If we need to enlarge the log queue
214 unsigned int factor = ((logq_ind + len) / logq_size) + 1;
218 newqueue = Mem_Alloc (tempmempool, logq_size);
219 memcpy (newqueue, logqueue, logq_ind);
222 remain = logq_size - logq_ind;
224 memcpy (&logqueue[logq_ind], msg, len);
230 // Check if log_file has changed
231 if (strcmp (crt_log_file, log_file.string) != 0)
237 // If a log file is available
240 FS_Print (logfile, msg);
241 if (log_sync.integer)
252 void Log_Print (const char *logfilename, const char *msg)
255 file = FS_Open(logfilename, "at", true);
268 void Log_Printf (const char *logfilename, const char *fmt, ...)
272 file = FS_Open (logfilename, "at", true);
277 va_start (argptr, fmt);
278 FS_VPrintf (file, fmt, argptr);
287 ==============================================================================
291 ==============================================================================
299 void Con_ToggleConsole_f (void)
301 // toggle the 'user wants console' bit
302 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
303 memset (con_times, 0, sizeof(con_times));
311 void Con_Clear_f (void)
314 memset (con_text, ' ', CON_TEXTSIZE);
323 void Con_ClearNotify (void)
327 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
337 void Con_MessageMode_f (void)
339 key_dest = key_message;
349 void Con_MessageMode2_f (void)
351 key_dest = key_message;
360 If the line width has changed, reformat the buffer.
363 void Con_CheckResize (void)
365 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
366 char tbuf[CON_TEXTSIZE];
368 width = (vid.conwidth >> 3);
370 if (width == con_linewidth)
373 if (width < 1) // video hasn't been initialized yet
376 con_linewidth = width;
377 con_totallines = CON_TEXTSIZE / con_linewidth;
378 memset (con_text, ' ', CON_TEXTSIZE);
382 oldwidth = con_linewidth;
383 con_linewidth = width;
384 oldtotallines = con_totallines;
385 con_totallines = CON_TEXTSIZE / con_linewidth;
386 numlines = oldtotallines;
388 if (con_totallines < numlines)
389 numlines = con_totallines;
393 if (con_linewidth < numchars)
394 numchars = con_linewidth;
396 memcpy (tbuf, con_text, CON_TEXTSIZE);
397 memset (con_text, ' ', CON_TEXTSIZE);
399 for (i=0 ; i<numlines ; i++)
401 for (j=0 ; j<numchars ; j++)
403 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
404 tbuf[((con_current - i + oldtotallines) %
405 oldtotallines) * oldwidth + j];
413 con_current = con_totallines - 1;
423 console_mempool = Mem_AllocPool("console", 0, NULL);
424 con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
425 memset (con_text, ' ', CON_TEXTSIZE);
429 Con_Print("Console initialized.\n");
431 // register our cvars
432 Cvar_RegisterVariable (&con_notifytime);
433 Cvar_RegisterVariable (&con_notify);
435 // register our commands
436 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
437 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
438 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
439 Cmd_AddCommand ("clear", Con_Clear_f);
440 con_initialized = true;
449 void Con_Linefeed (void)
456 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
463 Handles cursor positioning, line wrapping, etc
464 All console printing must go through this in order to be displayed
465 If no console is visible, the notify window will pop up.
468 void Con_PrintToHistory(const char *txt)
475 mask = 128; // go to colored text
476 S_LocalSound ("misc/talk.wav", true);
480 else if (txt[0] == 2)
482 mask = 128; // go to colored text
492 for (l=0 ; l< con_linewidth ; l++)
497 if (l != con_linewidth && (con_x + l > con_linewidth) )
512 // mark time for transparent overlay
513 if (con_current >= 0)
515 if (con_notify.integer < 0)
516 Cvar_SetValueQuick(&con_notify, 0);
517 if (con_notify.integer > MAX_NOTIFYLINES)
518 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
519 if (con_notify.integer > 0)
520 con_times[con_current % con_notify.integer] = cl.time;
535 default: // display character and advance
536 y = con_current % con_totallines;
537 con_text[y*con_linewidth+con_x] = c | mask;
539 if (con_x >= con_linewidth)
551 Prints to all appropriate console targets
554 void Con_Print(const char *msg)
556 // also echo to debugging console
559 // log all messages to file
562 if (!con_initialized)
565 if (cls.state == ca_dedicated)
566 return; // no graphics mode
568 // write it to the scrollable buffer
569 Con_PrintToHistory(msg);
573 // LordHavoc: increased from 4096 to 16384
574 #define MAXPRINTMSG 16384
580 Prints to all appropriate console targets
583 void Con_Printf(const char *fmt, ...)
586 char msg[MAXPRINTMSG];
588 va_start(argptr,fmt);
589 vsprintf(msg,fmt,argptr);
599 A Con_Print that only shows up if the "developer" cvar is set
602 void Con_DPrint(const char *msg)
604 if (!developer.integer)
605 return; // don't confuse non-developers with techie stuff...
613 A Con_Printf that only shows up if the "developer" cvar is set
616 void Con_DPrintf(const char *fmt, ...)
619 char msg[MAXPRINTMSG];
621 if (!developer.integer)
622 return; // don't confuse non-developers with techie stuff...
624 va_start(argptr,fmt);
625 vsprintf(msg,fmt,argptr);
636 Okay to call even when the screen can't be updated
639 void Con_SafePrint(const char *msg)
648 Okay to call even when the screen can't be updated
651 void Con_SafePrintf(const char *fmt, ...)
654 char msg[MAXPRINTMSG];
656 va_start(argptr,fmt);
657 vsprintf(msg,fmt,argptr);
665 ==============================================================================
669 ==============================================================================
677 The input line scrolls horizontally if typing goes beyond the right edge
679 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
682 void Con_DrawInput (void)
686 char editlinecopy[257], *text;
688 if (!key_consoleactive)
689 return; // don't draw anything
691 text = strcpy(editlinecopy, key_lines[edit_line]);
693 // Advanced Console Editing by Radix radix@planetquake.com
694 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
695 // use strlen of edit_line instead of key_linepos to allow editing
696 // of early characters w/o erasing
700 // fill out remainder with spaces
701 for (i = y; i < 256; i++)
704 // add the cursor frame
705 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
706 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
708 // text[key_linepos + 1] = 0;
710 // prestep if horizontally scrolling
711 if (key_linepos >= con_linewidth)
712 text += 1 + key_linepos - con_linewidth;
715 DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
718 // key_lines[edit_line][key_linepos] = 0;
726 Draws the last few lines of output transparently over the game top
729 void Con_DrawNotify (void)
735 extern char chat_buffer[];
738 if (con_notify.integer < 0)
739 Cvar_SetValueQuick(&con_notify, 0);
740 if (con_notify.integer > MAX_NOTIFYLINES)
741 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
742 if (gamemode == GAME_TRANSFUSION)
746 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;
760 DrawQ_String(0, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
766 if (key_dest == key_message)
772 // LordHavoc: speedup, and other improvements
774 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
776 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
777 while (strlen(temptext) >= (size_t) con_linewidth)
779 DrawQ_String (0, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
780 strcpy(temptext, &temptext[con_linewidth]);
783 if (strlen(temptext) > 0)
785 DrawQ_String (0, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
795 Draws the console with the solid background
796 The typing input line at the bottom should only be drawn if typing is allowed
799 extern char engineversion[40];
800 void Con_DrawConsole (int lines)
808 // draw the background
809 if (scr_conbrightness.value >= 0.01f)
810 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);
812 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
813 DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
816 con_vislines = lines;
818 rows = (lines-16)>>3; // rows of text to draw
819 y = lines - 16 - (rows<<3); // may start slightly negative
821 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
823 j = max(i - con_backscroll, 0);
824 text = con_text + (j % con_totallines)*con_linewidth;
826 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
829 // draw the input prompt, user text, and cursor if desired
836 New function for tab-completion system
838 MEGA Thanks to Taniwha
841 void Con_DisplayList(const char **list)
843 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
844 const char **walk = list;
856 if (pos + maxlen >= width) {
862 for (i = 0; i < (maxlen - len); i++)
874 Con_CompleteCommandLine
876 New function for tab-completion system
878 Thanks to Fett erich@heintz.com
882 void Con_CompleteCommandLine (void)
884 const char *cmd = "", *s;
885 const char **list[3] = {0, 0, 0};
886 int c, v, a, i, cmd_len;
888 s = key_lines[edit_line] + 1;
889 // Count number of possible matches
890 c = Cmd_CompleteCountPossible(s);
891 v = Cvar_CompleteCountPossible(s);
892 a = Cmd_CompleteAliasCountPossible(s);
894 if (!(c + v + a)) // No possible matches
897 if (c + v + a == 1) {
899 list[0] = Cmd_CompleteBuildList(s);
901 list[0] = Cvar_CompleteBuildList(s);
903 list[0] = Cmd_CompleteAliasBuildList(s);
905 cmd_len = strlen (cmd);
908 cmd = *(list[0] = Cmd_CompleteBuildList(s));
910 cmd = *(list[1] = Cvar_CompleteBuildList(s));
912 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
914 cmd_len = strlen (s);
916 for (i = 0; i < 3; i++) {
917 char ch = cmd[cmd_len];
918 const char **l = list[i];
920 while (*l && (*l)[cmd_len] == ch)
931 for (i = 0; i < con_linewidth - 4; i++)
935 // Print Possible Commands
937 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
938 Con_DisplayList(list[0]);
942 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
943 Con_DisplayList(list[1]);
947 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
948 Con_DisplayList(list[2]);
953 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
954 key_linepos = cmd_len + 1;
955 if (c + v + a == 1) {
956 key_lines[edit_line][key_linepos] = ' ';
959 key_lines[edit_line][key_linepos] = 0;
961 for (i = 0; i < 3; i++)
963 Mem_Free((void *)list[i]);