| /* |
| 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 the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| |
| */ |
| // console.c |
| |
| #include "quakedef.h" |
| |
| int con_ormask; |
| console_t con_main; |
| console_t con_chat; |
| console_t *con; // point to either con_main or con_chat |
| |
| int con_linewidth; // characters across screen |
| int con_totallines; // total lines in console scrollback |
| |
| float con_cursorspeed = 4; |
| |
| |
| cvar_t con_notifytime = CVAR2("con_notifytime","3"); //seconds |
| |
| #define NUM_CON_TIMES 4 |
| float con_times[NUM_CON_TIMES]; // realtime time the line was generated |
| // for transparent notify lines |
| |
| int con_vislines; |
| int con_notifylines; // scan lines to clear for notify lines |
| |
| qboolean con_debuglog; |
| |
| #define MAXCMDLINE 256 |
| extern char key_lines[32][MAXCMDLINE]; |
| extern int edit_line; |
| extern int key_linepos; |
| |
| |
| qboolean con_initialized; |
| |
| |
| void Key_ClearTyping (void) |
| { |
| key_lines[edit_line][1] = 0; // clear any typing |
| key_linepos = 1; |
| } |
| |
| /* |
| ================ |
| Con_ToggleConsole_f |
| ================ |
| */ |
| void Con_ToggleConsole_f (void) |
| { |
| Key_ClearTyping (); |
| |
| if (key_dest == key_console) |
| { |
| if (cls.state == ca_active) |
| key_dest = key_game; |
| } |
| else |
| key_dest = key_console; |
| |
| Con_ClearNotify (); |
| } |
| |
| /* |
| ================ |
| Con_ToggleChat_f |
| ================ |
| */ |
| void Con_ToggleChat_f (void) |
| { |
| Key_ClearTyping (); |
| |
| if (key_dest == key_console) |
| { |
| if (cls.state == ca_active) |
| key_dest = key_game; |
| } |
| else |
| key_dest = key_console; |
| |
| Con_ClearNotify (); |
| } |
| |
| /* |
| ================ |
| Con_Clear_f |
| ================ |
| */ |
| void Con_Clear_f (void) |
| { |
| Q_memset (con_main.text, ' ', CON_TEXTSIZE); |
| Q_memset (con_chat.text, ' ', CON_TEXTSIZE); |
| } |
| |
| |
| /* |
| ================ |
| Con_ClearNotify |
| ================ |
| */ |
| void Con_ClearNotify (void) |
| { |
| int i; |
| |
| for (i=0 ; i<NUM_CON_TIMES ; i++) |
| con_times[i] = 0; |
| } |
| |
| |
| /* |
| ================ |
| Con_MessageMode_f |
| ================ |
| */ |
| void Con_MessageMode_f (void) |
| { |
| chat_team = false; |
| key_dest = key_message; |
| } |
| |
| /* |
| ================ |
| Con_MessageMode2_f |
| ================ |
| */ |
| void Con_MessageMode2_f (void) |
| { |
| chat_team = true; |
| key_dest = key_message; |
| } |
| |
| /* |
| ================ |
| Con_Resize |
| |
| ================ |
| */ |
| void Con_Resize (console_t *con) |
| { |
| int i, j, width, oldwidth, oldtotallines, numlines, numchars; |
| char tbuf[CON_TEXTSIZE]; |
| |
| width = (vid.width >> 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; |
| Q_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; |
| |
| Q_memcpy (tbuf, con->text, CON_TEXTSIZE); |
| Q_memset (con->text, ' ', CON_TEXTSIZE); |
| |
| for (i=0 ; i<numlines ; i++) |
| { |
| for (j=0 ; j<numchars ; j++) |
| { |
| con->text[(con_totallines - 1 - i) * con_linewidth + j] = |
| tbuf[((con->current - i + oldtotallines) % |
| oldtotallines) * oldwidth + j]; |
| } |
| } |
| |
| Con_ClearNotify (); |
| } |
| |
| con->current = con_totallines - 1; |
| con->display = con->current; |
| } |
| |
| |
| /* |
| ================ |
| Con_CheckResize |
| |
| If the line width has changed, reformat the buffer. |
| ================ |
| */ |
| void Con_CheckResize (void) |
| { |
| Con_Resize (&con_main); |
| Con_Resize (&con_chat); |
| } |
| |
| |
| /* |
| ================ |
| Con_Init |
| ================ |
| */ |
| void Con_Init (void) |
| { |
| con_debuglog = COM_CheckParm("-condebug"); |
| |
| con = &con_main; |
| con_linewidth = -1; |
| Con_CheckResize (); |
| |
| Con_Printf ("Console initialized.\n"); |
| |
| // |
| // register our commands |
| // |
| Cvar_RegisterVariable (&con_notifytime); |
| |
| Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f); |
| Cmd_AddCommand ("togglechat", Con_ToggleChat_f); |
| Cmd_AddCommand ("messagemode", Con_MessageMode_f); |
| Cmd_AddCommand ("messagemode2", Con_MessageMode2_f); |
| Cmd_AddCommand ("clear", Con_Clear_f); |
| con_initialized = true; |
| } |
| |
| |
| /* |
| =============== |
| Con_Linefeed |
| =============== |
| */ |
| void Con_Linefeed (void) |
| { |
| con->x = 0; |
| if (con->display == con->current) |
| con->display++; |
| con->current++; |
| Q_memset (&con->text[(con->current%con_totallines)*con_linewidth] |
| , ' ', con_linewidth); |
| } |
| |
| /* |
| ================ |
| Con_Print |
| |
| Handles cursor positioning, line wrapping, etc |
| All console printing must go through this in order to be logged to disk |
| If no console is visible, the notify window will pop up. |
| ================ |
| */ |
| void Con_Print (char *txt) |
| { |
| int y; |
| int c, l; |
| static int cr; |
| int mask; |
| |
| if (txt[0] == 1 || txt[0] == 2) |
| { |
| mask = 128; // go to colored text |
| txt++; |
| } |
| else |
| mask = 0; |
| |
| |
| while ( (c = *txt) ) |
| { |
| // count word length |
| for (l=0 ; l< con_linewidth ; l++) |
| if ( txt[l] <= ' ') |
| break; |
| |
| // word wrap |
| if (l != con_linewidth && (con->x + l > 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_ormask; |
| con->x++; |
| if (con->x >= con_linewidth) |
| con->x = 0; |
| break; |
| } |
| |
| } |
| } |
| |
| |
| /* |
| ================ |
| Con_Printf |
| |
| Handles cursor positioning, line wrapping, etc |
| ================ |
| */ |
| #define MAXPRINTMSG 4096 |
| // FIXME: make a buffer size safe vsprintf? |
| void Con_Printf (char *fmt, ...) |
| { |
| va_list argptr; |
| char msg[MAXPRINTMSG]; |
| static qboolean inupdate; |
| |
| va_start (argptr,fmt); |
| vsprintf (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) |
| Sys_DebugLog(va("%s/qconsole.log",com_gamedir), "%s", msg); |
| |
| if (!con_initialized) |
| return; |
| |
| // write it to the scrollable buffer |
| Con_Print (msg); |
| |
| // update the screen immediately if the console is displayed |
| if (cls.state != ca_active) |
| { |
| // 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.value) |
| return; // don't confuse non-developers with techie stuff... |
| |
| va_start (argptr,fmt); |
| vsprintf (msg,fmt,argptr); |
| va_end (argptr); |
| |
| Con_Printf ("%s", msg); |
| } |
| |
| /* |
| ============================================================================== |
| |
| 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 && cls.state == ca_active) |
| return; // don't draw anything (allways draw if not active) |
| |
| 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-22; |
| |
| for (i=0 ; i<con_linewidth ; i++) |
| Draw_Character ( (i+1)<<3, con_vislines - 22, text[i]); |
| |
| // remove cursor |
| key_lines[edit_line][key_linepos] = 0; |
| } |
| |
| |
| /* |
| ================ |
| Con_DrawNotify |
| |
| Draws the last few lines of output transparently over the game top |
| ================ |
| */ |
| void Con_DrawNotify (void) |
| { |
| int x, v; |
| char *text; |
| int i; |
| float time; |
| char *s; |
| int skip; |
| |
| v = 0; |
| for (i= con->current-NUM_CON_TIMES+1 ; i<=con->current ; i++) |
| { |
| if (i < 0) |
| continue; |
| time = con_times[i % NUM_CON_TIMES]; |
| if (time == 0) |
| continue; |
| time = realtime - time; |
| if (time > 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_Character ( (x+1)<<3, v, text[x]); |
| |
| v += 8; |
| } |
| |
| |
| if (key_dest == key_message) |
| { |
| clearnotify = 0; |
| scr_copytop = 1; |
| |
| if (chat_team) |
| { |
| Draw_String (8, v, "say_team:"); |
| skip = 11; |
| } |
| else |
| { |
| Draw_String (8, v, "say:"); |
| skip = 5; |
| } |
| |
| s = chat_buffer; |
| if (chat_bufferlen > (int) ((vid.width>>3)-(skip+1))) |
| s += chat_bufferlen - ((vid.width>>3)-(skip+1)); |
| x = 0; |
| while(s[x]) |
| { |
| Draw_Character ( (x+skip)<<3, v, s[x]); |
| x++; |
| } |
| Draw_Character ( (x+skip)<<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 |
| ================ |
| */ |
| void Con_DrawConsole (int lines) |
| { |
| int i, j, x, y, n; |
| int rows; |
| char *text; |
| int row; |
| char dlbar[1024]; |
| |
| if (lines <= 0) |
| return; |
| |
| // draw the background |
| Draw_ConsoleBackground (lines); |
| |
| // draw the text |
| con_vislines = lines; |
| |
| // changed to line things up better |
| rows = (lines-22)>>3; // rows of text to draw |
| |
| y = lines - 30; |
| |
| // draw from the bottom up |
| if (con->display != con->current) |
| { |
| // draw arrows to show the buffer is backscrolled |
| for (x=0 ; x<con_linewidth ; x+=4) |
| Draw_Character ( (x+1)<<3, y, '^'); |
| |
| y -= 8; |
| rows--; |
| } |
| |
| row = con->display; |
| for (i=0 ; i<rows ; i++, y-=8, row--) |
| { |
| if (row < 0) |
| break; |
| if (con->current - row >= con_totallines) |
| break; // past scrollback wrap point |
| |
| text = con->text + (row % con_totallines)*con_linewidth; |
| |
| for (x=0 ; x<con_linewidth ; x++) |
| Draw_Character ( (x+1)<<3, y, text[x]); |
| } |
| |
| // draw the download bar |
| // figure out width |
| if (cls.download) { |
| if ((text = strrchr(cls.downloadname, '/')) != NULL) |
| text++; |
| else |
| text = cls.downloadname; |
| |
| x = con_linewidth - ((con_linewidth * 7) / 40); |
| y = x - strlen(text) - 8; |
| i = con_linewidth/3; |
| if ((int) strlen(text) > i) { |
| y = x - i - 11; |
| strncpy(dlbar, text, i); |
| dlbar[i] = 0; |
| strcat(dlbar, "..."); |
| } else |
| strcpy(dlbar, text); |
| strcat(dlbar, ": "); |
| i = strlen(dlbar); |
| dlbar[i++] = '\x80'; |
| // where's the dot go? |
| if (cls.downloadpercent == 0) |
| n = 0; |
| else |
| n = y * cls.downloadpercent / 100; |
| |
| for (j = 0; j < y; j++) |
| if (j == n) |
| dlbar[i++] = '\x83'; |
| else |
| dlbar[i++] = '\x81'; |
| dlbar[i++] = '\x82'; |
| dlbar[i] = 0; |
| |
| sprintf(dlbar + strlen(dlbar), " %02d%%", cls.downloadpercent); |
| |
| // draw it |
| y = con_vislines-22 + 8; |
| for (i = 0; i < (int) strlen(dlbar); i++) |
| Draw_Character ( (i+1)<<3, y, dlbar[i]); |
| } |
| |
| |
| // draw the input prompt, user text, and cursor if desired |
| Con_DrawInput (); |
| } |
| |
| |
| /* |
| ================== |
| Con_NotifyBox |
| ================== |
| */ |
| void Con_NotifyBox (char *text) |
| { |
| double t1, t2; |
| |
| // during startup for sound / cd warnings |
| Con_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); |
| |
| Con_Printf (text); |
| |
| Con_Printf ("Press a key.\n"); |
| Con_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n"); |
| |
| key_count = -2; // wait for a key down and up |
| key_dest = key_console; |
| |
| do |
| { |
| t1 = Sys_DoubleTime (); |
| SCR_UpdateScreen (); |
| Sys_SendKeyEvents (); |
| t2 = Sys_DoubleTime (); |
| realtime += t2-t1; // make the cursor blink |
| } while (key_count < 0); |
| |
| Con_Printf ("\n"); |
| key_dest = key_game; |
| realtime = 0; // put the cursor back to invisible |
| } |
| |
| |
| /* |
| ================== |
| 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); |
| vsprintf (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; |
| } |
| |