/* console.c @description@ Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA $Id$ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef NeXT #include #endif #ifndef _MSC_VER #include #endif #include #include "va.h" #include "draw.h" #include "host.h" #include "sys.h" #include "qargs.h" #include "client.h" #include "console.h" #include "input.h" #include "screen.h" #include "keys.h" int con_linewidth; float con_cursorspeed = 4; #define CON_TEXTSIZE 16384 qboolean con_forcedup; // because no entities to refresh int con_totallines; // total lines in console scrollback int con_backscroll; // lines up from bottom to display int con_current; // where next message will be printed int con_x; // offset in current line for next print char *con_text=0; cvar_t *con_notifytime; #define NUM_CON_TIMES 4 float con_times[NUM_CON_TIMES]; // realtime time the line was generated // for transparent notify lines int con_vislines; qboolean con_debuglog; #define MAXCMDLINE 256 extern char key_lines[32][MAXCMDLINE]; extern int edit_line; extern int key_linepos; qboolean con_initialized; int con_notifylines; // scan lines to clear for notify lines extern void M_Menu_Main_f (void); /* ================ Con_ToggleConsole_f ================ */ void Con_ToggleConsole_f (void) { if (key_dest == key_console) { if (cls.state == ca_connected) { key_dest = key_game; key_lines[edit_line][1] = 0; // clear any typing key_linepos = 1; } else { M_Menu_Main_f (); } } else key_dest = key_console; memset (con_times, 0, sizeof(con_times)); } /* ================ Con_Clear_f ================ */ void Con_Clear_f (void) { if (con_text) memset (con_text, ' ', CON_TEXTSIZE); } /* ================ Con_ClearNotify ================ */ void Con_ClearNotify (void) { int i; for (i=0 ; i> 3) - 2; if (width == con_linewidth) return; if (width < 1) // video hasn't been initialized yet { width = 38; con_linewidth = width; con_totallines = CON_TEXTSIZE / con_linewidth; memset (con_text, ' ', CON_TEXTSIZE); } else { oldwidth = con_linewidth; con_linewidth = width; oldtotallines = con_totallines; con_totallines = CON_TEXTSIZE / con_linewidth; numlines = oldtotallines; if (con_totallines < numlines) numlines = con_totallines; numchars = oldwidth; if (con_linewidth < numchars) numchars = con_linewidth; memcpy (tbuf, con_text, CON_TEXTSIZE); memset (con_text, ' ', CON_TEXTSIZE); for (i=0 ; i con_linewidth) ) con_x = 0; txt++; if (cr) { con_current--; cr = false; } if (!con_x) { Con_Linefeed (); // mark time for transparent overlay if (con_current >= 0) con_times[con_current % NUM_CON_TIMES] = realtime; } switch (c) { case '\n': con_x = 0; break; case '\r': con_x = 0; cr = 1; break; default: // display character and advance y = con_current % con_totallines; con_text[y*con_linewidth+con_x] = c | mask; con_x++; if (con_x >= con_linewidth) con_x = 0; break; } } } /* ================ Con_DebugLog ================ */ void Con_DebugLog(char *file, char *fmt, ...) { va_list argptr; static char data[1024]; int fd; va_start(argptr, fmt); vsnprintf (data, sizeof(data), fmt, argptr); va_end(argptr); fd = open(file, O_WRONLY | O_CREAT | O_APPEND, 0666); write(fd, data, strlen(data)); close(fd); } /* ================ Con_Printf Handles cursor positioning, line wrapping, etc ================ */ #define MAXPRINTMSG 4096 void Con_Printf (char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; static qboolean inupdate; va_start (argptr,fmt); vsnprintf (msg, sizeof(msg), fmt,argptr); va_end (argptr); // also echo to debugging console Sys_Printf ("%s", msg); // also echo to debugging console // log all messages to file if (con_debuglog) Con_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg); if (!con_initialized) return; if (cls.state == ca_dedicated) return; // no graphics mode // write it to the scrollable buffer Con_Print (msg); // update the screen if the console is displayed if (cls.signon != SIGNONS && !scr_disabled_for_loading ) { // protect against infinite loop if something in SCR_UpdateScreen calls // Con_Printd if (!inupdate) { inupdate = true; SCR_UpdateScreen (); inupdate = false; } } } /* ================ Con_DPrintf A Con_Printf that only shows up if the "developer" cvar is set ================ */ void Con_DPrintf (char *fmt, ...) { va_list argptr; char msg[MAXPRINTMSG]; if (!developer->int_val) return; // don't confuse non-developers with techie stuff... va_start (argptr,fmt); vsnprintf (msg, sizeof(msg), fmt,argptr); va_end (argptr); Con_Printf ("%s", msg); } /* ================== Con_SafePrintf Okay to call even when the screen can't be updated ================== */ void Con_SafePrintf (char *fmt, ...) { va_list argptr; char msg[1024]; int temp; va_start (argptr,fmt); vsnprintf (msg, sizeof(msg), fmt,argptr); va_end (argptr); temp = scr_disabled_for_loading; scr_disabled_for_loading = true; Con_Printf ("%s", msg); scr_disabled_for_loading = temp; } /* ============================================================================== DRAWING ============================================================================== */ /* ================ Con_DrawInput The input line scrolls horizontally if typing goes beyond the right edge ================ */ void Con_DrawInput (void) { int y; int i; char *text; if (key_dest != key_console && !con_forcedup) return; // don't draw anything text = key_lines[edit_line]; // add the cursor frame text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1); // fill out remainder with spaces for (i=key_linepos+1 ; i< con_linewidth ; i++) text[i] = ' '; // prestep if horizontally scrolling if (key_linepos >= con_linewidth) text += 1 + key_linepos - con_linewidth; // draw it y = con_vislines-16; for (i=0 ; i con_notifytime->value) continue; text = con_text + (i % con_totallines)*con_linewidth; clearnotify = 0; scr_copytop = 1; for (x = 0 ; x < con_linewidth ; x++) Draw_Character8 ( (x+1)<<3, v, text[x]); v += 8; } if (key_dest == key_message) { clearnotify = 0; scr_copytop = 1; x = 0; Draw_String8 (8, v, "say:"); while(chat_buffer[x]) { Draw_Character8 ( (x+5)<<3, v, chat_buffer[x]); x++; } Draw_Character8 ( (x+5)<<3, v, 10+((int)(realtime*con_cursorspeed)&1)); v += 8; } if (v > con_notifylines) con_notifylines = v; } /* ================ Con_DrawConsole Draws the console with the solid background The typing input line at the bottom should only be drawn if typing is allowed ================ */ void Con_DrawConsole (int lines, qboolean drawinput) { int i, x, y; int rows; char *text; int j; if (lines <= 0) return; // draw the background Draw_ConsoleBackground (lines); // draw the text con_vislines = lines; rows = (lines-16)>>3; // rows of text to draw y = lines - 16 - (rows<<3); // may start slightly negative for (i= con_current - rows + 1 ; i<=con_current ; i++, y+=8 ) { j = i - con_backscroll; if (j<0) j = 0; text = con_text + (j % con_totallines)*con_linewidth; for (x=0 ; x