Skip to content
Snippets Groups Projects
console.c 41.5 KiB
Newer Older
Alam Ed Arias's avatar
Alam Ed Arias committed
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2018 by Sonic Team Junior.
Alam Ed Arias's avatar
Alam Ed Arias committed
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  console.c
/// \brief Console drawing and input

#ifdef __GNUC__
#include <unistd.h>
#ifdef _XBOX
#include <openxdk/debug.h>
#endif
#endif

#include "doomdef.h"
#include "console.h"
#include "g_game.h"
#include "g_input.h"
#include "hu_stuff.h"
#include "keys.h"
#include "r_defs.h"
#include "sounds.h"
#include "st_stuff.h"
#include "s_sound.h"
#include "v_video.h"
#include "i_video.h"
#include "z_zone.h"
#include "i_system.h"
#include "i_threads.h"
Alam Ed Arias's avatar
Alam Ed Arias committed
#include "d_main.h"
#include "m_menu.h"
toaster's avatar
toaster committed
#include "filesrch.h"
Alam Ed Arias's avatar
Alam Ed Arias committed

#ifdef _WINDOWS
#include "win32/win_main.h"
#endif

#ifdef HWRENDER
#include "hardware/hw_main.h"
#endif

#define MAXHUDLINES 20

#ifdef HAVE_THREADS
I_mutex con_mutex;

#  define Lock_state()    I_lock_mutex(&con_mutex)
#  define Unlock_state() I_unlock_mutex(con_mutex)
#else/*HAVE_THREADS*/
#  define Lock_state()
#  define Unlock_state()
#endif/*HAVE_THREADS*/

Alam Ed Arias's avatar
Alam Ed Arias committed
static boolean con_started = false; // console has been initialised
       boolean con_startup = false; // true at game startup, screen need refreshing
static boolean con_forcepic = true; // at startup toggle console translucency when first off
       boolean con_recalc;          // set true when screen size has changed

static tic_t con_tick; // console ticker for anim or blinking prompt cursor
                        // con_scrollup should use time (currenttime - lasttime)..

static boolean consoletoggle; // true when console key pushed, ticker will handle
static boolean consoleready;  // console prompt is ready

       INT32 con_destlines; // vid lines used by console at final position
static INT32 con_curlines;  // vid lines currently used by console

Lactozilla's avatar
Lactozilla committed
       INT32 con_clipviewtop; // (useless)
Alam Ed Arias's avatar
Alam Ed Arias committed

static INT32 con_hudlines;        // number of console heads up message lines
static INT32 con_hudtime[MAXHUDLINES];      // remaining time of display for hud msg lines

       INT32 con_clearlines;      // top screen lines to refresh when view reduced
       boolean con_hudupdate;   // when messages scroll, we need a backgrnd refresh

// console text output
static char *con_line;          // console text output current line
static size_t con_cx;           // cursor position in current line
static size_t con_cy;           // cursor line number in con_buffer, is always
                                // increasing, and wrapped around in the text
                                // buffer using modulo.

static size_t con_totallines;      // lines of console text into the console buffer
static size_t con_width;           // columns of chars, depend on vid mode width

static size_t con_scrollup;        // how many rows of text to scroll up (pgup/pgdn)
UINT32 con_scalefactor;            // text size scale factor

// hold 32 last lines of input for history
#define CON_MAXPROMPTCHARS 256
#define CON_PROMPTCHAR '$'
Alam Ed Arias's avatar
Alam Ed Arias committed

static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines

static INT32 inputline;    // current input line number
static INT32 inputhist;    // line number of history input line to restore
static size_t input_cur; // position of cursor in line
static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
static size_t input_len; // length of current line, used to bound cursor and such
// notice: input does NOT include the "$" at the start of the line. - 11/3/16
Alam Ed Arias's avatar
Alam Ed Arias committed

// protos.
static void CON_InputInit(void);
static void CON_RecalcSize(void);
static void CON_ChangeHeight(void);
Alam Ed Arias's avatar
Alam Ed Arias committed

static void CONS_hudlines_Change(void);
static void CONS_backcolor_Change(void);
Alam Ed Arias's avatar
Alam Ed Arias committed

//======================================================================
//                   CONSOLE VARS AND COMMANDS
//======================================================================
#ifdef macintosh
#define CON_BUFFERSIZE 4096 // my compiler can't handle local vars >32k
#else
#define CON_BUFFERSIZE 16384
#endif

static char con_buffer[CON_BUFFERSIZE];

// how many seconds the hud messages lasts on the screen
static consvar_t cons_msgtimeout = {"con_hudtime", "5", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};

// number of lines displayed on the HUD
static consvar_t cons_hudlines = {"con_hudlines", "5", CV_CALL|CV_SAVE, CV_Unsigned, CONS_hudlines_Change, 0, NULL, NULL, 0, 0, NULL};

// number of lines console move per frame
// (con_speed needs a limit, apparently)
static CV_PossibleValue_t speed_cons_t[] = {{0, "MIN"}, {64, "MAX"}, {0, NULL}};
static consvar_t cons_speed = {"con_speed", "8", CV_SAVE, speed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};

// percentage of screen height to use for console
static consvar_t cons_height = {"con_height", "50", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};

static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}, {0, NULL}};
// whether to use console background picture, or translucent mode
static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};

static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, 		{1, "Black"},		{2, "Sepia"},
												{3, "Brown"},		{4, "Pink"},		{5, "Raspberry"},
												{6, "Red"},			{7, "Creamsicle"},	{8, "Orange"},
												{9, "Gold"},		{10,"Yellow"},		{11,"Emerald"},
												{12,"Green"},		{13,"Cyan"},		{14,"Steel"},
												{15,"Periwinkle"},	{16,"Blue"},		{17,"Purple"},
												{18,"Lavender"},
consvar_t cons_backcolor = {"con_backcolor", "Black", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
Alam Ed Arias's avatar
Alam Ed Arias committed

toaster's avatar
toaster committed
static CV_PossibleValue_t menuhighlight_cons_t[] =
{
toaster's avatar
toaster committed
	{V_YELLOWMAP, "Always yellow"},
	{V_PURPLEMAP, "Always purple"},
	{V_GREENMAP, "Always green"},
	{V_BLUEMAP, "Always blue"},
	{V_REDMAP, "Always red"},
	{V_GRAYMAP, "Always gray"},
	{V_ORANGEMAP, "Always orange"},
	{V_SKYMAP, "Always sky-blue"},
	{V_GOLDMAP, "Always gold"},
	{V_LAVENDERMAP, "Always lavender"},
	{V_TEAMAP, "Always tea-green"},
Sal's avatar
Sal committed
	{V_STEELMAP, "Always steel-blue"},
	{V_BROWNMAP, "Always brown"},
toaster's avatar
toaster committed
	{0, NULL}
};
consvar_t cons_menuhighlight = {"menuhighlight", "Game type", CV_SAVE, menuhighlight_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
toaster's avatar
toaster committed

Alam Ed Arias's avatar
Alam Ed Arias committed
static void CON_Print(char *msg);

//
//
static void CONS_hudlines_Change(void)
{
	INT32 i;

Alam Ed Arias's avatar
Alam Ed Arias committed
	// Clear the currently displayed lines
	for (i = 0; i < con_hudlines; i++)
		con_hudtime[i] = 0;

	if (cons_hudlines.value < 1)
		cons_hudlines.value = 1;
	else if (cons_hudlines.value > MAXHUDLINES)
		cons_hudlines.value = MAXHUDLINES;

	con_hudlines = cons_hudlines.value;

Alam Ed Arias's avatar
Alam Ed Arias committed
	CONS_Printf(M_GetText("Number of console HUD lines is now %d\n"), con_hudlines);
}

// Clear console text buffer
//
static void CONS_Clear_f(void)
{
Alam Ed Arias's avatar
Alam Ed Arias committed
	memset(con_buffer, 0, CON_BUFFERSIZE);

	con_cx = 0;
	con_cy = con_totallines-1;
	con_line = &con_buffer[con_cy*con_width];
	con_scrollup = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Choose english keymap
//
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	shiftxform = english_shiftxform;
	CONS_Printf(M_GetText("%s keymap.\n"), M_GetText("English"));
Alam Ed Arias's avatar
Alam Ed Arias committed

static char *bindtable[NUMINPUTS];

static void CONS_Bind_f(void)
{
	size_t na;
	INT32 key;

	na = COM_Argc();

	if (na != 2 && na != 3)
	{
		CONS_Printf(M_GetText("bind <keyname> [<command>]: create shortcut keys to command(s)\n"));
		CONS_Printf("\x82%s", M_GetText("Bind table :\n"));
		na = 0;
		for (key = 0; key < NUMINPUTS; key++)
			if (bindtable[key])
			{
				CONS_Printf("%s : \"%s\"\n", G_KeynumToString(key), bindtable[key]);
				na = 1;
			}
		if (!na)
			CONS_Printf(M_GetText("(empty)\n"));
		return;
	}

	key = G_KeyStringtoNum(COM_Argv(1));
	if (key <= 0 || key >= NUMINPUTS)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n"));
		return;
	}

	Z_Free(bindtable[key]);
	bindtable[key] = NULL;

	if (na == 3)
		bindtable[key] = Z_StrDup(COM_Argv(2));
}

//======================================================================
//                          CONSOLE SETUP
//======================================================================

// Console BG color
UINT8 *consolebgmap = NULL;
Alam Ed Arias's avatar
Alam Ed Arias committed

void CON_SetupBackColormap(void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	UINT16 i, palsum;
	UINT8 j, palindex;
	UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);

	if (!consolebgmap)
		consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);

	switch (cons_backcolor.value)
	{
		case 0:		palindex = 15; 	break; 	// White
		case 1:		palindex = 31;	break; 	// Gray
		case 2:		palindex = 47;	break;	// Sepia
		case 3:		palindex = 63;	break; 	// Brown
		case 4:		palindex = 150; shift = 7; 	break; 	// Pink
		case 5:		palindex = 127; shift = 7;	break; 	// Raspberry
		case 6:		palindex = 143;	break; 	// Red
		case 7:		palindex = 86;	shift = 7;	break;	// Creamsicle
		case 8:		palindex = 95;	break; 	// Orange
		case 9:		palindex = 119; shift = 7;	break; 	// Gold
		case 10:	palindex = 111;	break; 	// Yellow
		case 11:	palindex = 191; shift = 7; 	break; 	// Emerald
		case 12:	palindex = 175;	break; 	// Green
		case 13:	palindex = 219;	break; 	// Cyan
		case 14:	palindex = 207; shift = 7;	break; 	// Steel
		case 15:	palindex = 230;	shift = 7; 	break; 	// Periwinkle
		case 16:	palindex = 239;	break; 	// Blue
		case 17:	palindex = 199; shift = 7; 	break; 	// Purple
		case 18:	palindex = 255; shift = 7; 	break; 	// Lavender
		// Default green
		default:	palindex = 175; break;
Alam Ed Arias's avatar
Alam Ed Arias committed

	// setup background colormap
	for (i = 0, j = 0; i < 768; i += 3, j++)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
		consolebgmap[j] = (UINT8)(palindex - palsum);
static void CONS_backcolor_Change(void)
Alam Ed Arias's avatar
Alam Ed Arias committed
{
	CON_SetupBackColormap();
}
Alam Ed Arias's avatar
Alam Ed Arias committed

// Font colormap colors
// TODO: This could probably be improved somehow...
// These colormaps are 99% identical, with just a few changed bytes
UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *orangemap,\
 *skymap, *goldmap, *lavendermap, *teamap, *steelmap, *pinkmap, *brownmap, *peachmap;
static void CON_SetupColormaps(void)
{
	INT32 i;
	UINT8 *memorysrc = (UINT8 *)Z_Malloc((256*15), PU_STATIC, NULL);

	purplemap   = memorysrc;
	yellowmap   = (purplemap+256);
	greenmap    = (yellowmap+256);
	bluemap     = (greenmap+256);
	redmap      = (bluemap+256);
	graymap     = (redmap+256);
	orangemap   = (graymap+256);
	skymap      = (orangemap+256);
	lavendermap = (skymap+256);
	goldmap     = (lavendermap+256);
	teamap      = (goldmap+256);
	steelmap    = (teamap+256);
	pinkmap     = (steelmap+256);
	brownmap    = (pinkmap+256);
	peachmap    = (brownmap+256);
Alam Ed Arias's avatar
Alam Ed Arias committed

	// setup the other colormaps, for console text

	// these don't need to be aligned, unless you convert the
	// V_DrawMappedPatch() into optimised asm.

	for (i = 0; i < (256*15); i++, ++memorysrc)
toaster's avatar
toaster committed
		*memorysrc = (UINT8)(i & 0xFF); // remap each color to itself...
Alam Ed Arias's avatar
Alam Ed Arias committed

Sal's avatar
Sal committed
	// SRB2Kart: Different console font, new colors
	purplemap[120]   = (UINT8)194;
	yellowmap[120]   = (UINT8)103;
	greenmap[120]    = (UINT8)162;
	bluemap[120]     = (UINT8)228;
	redmap[120]      = (UINT8)126; // battle
	graymap[120]     =  (UINT8)10;
	orangemap[120]   =  (UINT8)85; // record attack
	skymap[120]      = (UINT8)214; // race
	lavendermap[120] = (UINT8)248;
	goldmap[120]     = (UINT8)114;
	teamap[120]      = (UINT8)177;
	steelmap[120]    = (UINT8)201;
	pinkmap[120]     = (UINT8)145;
	brownmap[120]    =  (UINT8)48;

	// Init back colormap
	CON_SetupBackColormap();
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Setup the console text buffer
//
// for WII, libogc already has a CON_Init function, we must rename it here
#ifdef _WII
void CON_InitWii(void)
#else
void CON_Init(void)
#endif
{
	INT32 i;

	for (i = 0; i < NUMINPUTS; i++)
		bindtable[i] = NULL;

Alam Ed Arias's avatar
Alam Ed Arias committed
	// clear all lines
	memset(con_buffer, 0, CON_BUFFERSIZE);

	// make sure it is ready for the loading screen
	con_width = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
	CON_RecalcSize();

	CON_SetupColormaps();
Alam Ed Arias's avatar
Alam Ed Arias committed

Alam Ed Arias's avatar
Alam Ed Arias committed
	//note: CON_Ticker should always execute at least once before D_Display()
	con_clipviewtop = -1; // -1 does not clip

	con_hudlines = atoi(cons_hudlines.defaultvalue);

Alam Ed Arias's avatar
Alam Ed Arias committed
	// setup console input filtering
	CON_InputInit();

	// register our commands
	//
	COM_AddCommand("cls", CONS_Clear_f);
	//COM_AddCommand("english", CONS_English_f);
Alam Ed Arias's avatar
Alam Ed Arias committed
	// set console full screen for game startup MAKE SURE VID_Init() done !!!
Alam Ed Arias's avatar
Alam Ed Arias committed
	con_destlines = vid.height;
	con_curlines = vid.height;

	Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed

	if (!dedicated)
	{
Alam Ed Arias's avatar
Alam Ed Arias committed
		con_started = true;
		con_startup = true; // need explicit screen refresh until we are in Doom loop
		consoletoggle = false;
Alam Ed Arias's avatar
Alam Ed Arias committed
		CV_RegisterVar(&cons_msgtimeout);
		CV_RegisterVar(&cons_hudlines);
		CV_RegisterVar(&cons_speed);
		CV_RegisterVar(&cons_height);
		CV_RegisterVar(&cons_backpic);
		CV_RegisterVar(&cons_backcolor);
toaster's avatar
toaster committed
		CV_RegisterVar(&cons_menuhighlight);
Alam Ed Arias's avatar
Alam Ed Arias committed
		COM_AddCommand("bind", CONS_Bind_f);
	}
	else
	{
Alam Ed Arias's avatar
Alam Ed Arias committed
		con_started = true;
		con_startup = false; // need explicit screen refresh until we are in Doom loop
		consoletoggle = true;
Alam Ed Arias's avatar
Alam Ed Arias committed
	}
}
// Console input initialization
//
static void CON_InputInit(void)
{
Alam Ed Arias's avatar
Alam Ed Arias committed
	// prepare the first prompt line
	memset(inputlines, 0, sizeof (inputlines));
	inputline = 0;
	input_cur = input_sel = input_len = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

//======================================================================
//                        CONSOLE EXECUTION
//======================================================================

// Called at screen size change to set the rows and line size of the
// console text buffer.
//
static void CON_RecalcSize(void)
{
	size_t conw, oldcon_width, oldnumlines, i, oldcon_cy;
	char *tmp_buffer;
	char *string;

Alam Ed Arias's avatar
Alam Ed Arias committed
	switch (cv_constextsize.value)
	{
	case V_NOSCALEPATCH:
		con_scalefactor = 1;
		break;
	case V_SMALLSCALEPATCH:
		con_scalefactor = vid.smalldupx;
		break;
	case V_MEDSCALEPATCH:
		con_scalefactor = vid.meddupx;
		break;
	default:	// Full scaling
		con_scalefactor = vid.dupx;
		break;
	}

	con_recalc = false;

	if (dedicated)
		conw = 1;
	else
		conw = (vid.width>>3) / con_scalefactor - 2;

	if (con_curlines == vid.height) // first init
	{
		con_curlines = vid.height;
		con_destlines = vid.height;
	}

	if (con_destlines > 0) // Resize console if already open
	{
		CON_ChangeHeight();
		con_curlines = con_destlines;
	}

Alam Ed Arias's avatar
Alam Ed Arias committed
	// check for change of video width
	if (conw == con_width)
	{
		Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed
		return; // didn't change
Alam Ed Arias's avatar
Alam Ed Arias committed

	tmp_buffer = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL);
	string = Z_Malloc(CON_BUFFERSIZE, PU_STATIC, NULL); // BP: it is a line but who know

Alam Ed Arias's avatar
Alam Ed Arias committed
	oldcon_width = con_width;
	oldnumlines = con_totallines;
	oldcon_cy = con_cy;
	M_Memcpy(tmp_buffer, con_buffer, CON_BUFFERSIZE);

	if (conw < 1)
		con_width = (BASEVIDWIDTH>>3) - 2;
	else
		con_width = conw;

	con_width += 11; // Graue 06-19-2004 up to 11 control chars per line

	con_totallines = CON_BUFFERSIZE / con_width;
	memset(con_buffer, ' ', CON_BUFFERSIZE);

	con_cx = 0;
	con_cy = con_totallines-1;
	con_line = &con_buffer[con_cy*con_width];
	con_scrollup = 0;

Alam Ed Arias's avatar
Alam Ed Arias committed
	// re-arrange console text buffer to keep text
	if (oldcon_width) // not the first time
	{
		for (i = oldcon_cy + 1; i < oldcon_cy + oldnumlines; i++)
		{
			if (tmp_buffer[(i%oldnumlines)*oldcon_width])
			{
				M_Memcpy(string, &tmp_buffer[(i%oldnumlines)*oldcon_width], oldcon_width);
				conw = oldcon_width - 1;
				while (string[conw] == ' ' && conw)
					conw--;
				string[conw+1] = '\n';
				string[conw+2] = '\0';
				CON_Print(string);
			}
		}
	}

	Z_Free(string);
	Z_Free(tmp_buffer);
}

static void CON_ChangeHeight(void)
{
	INT32 minheight;

	Lock_state();

	minheight = 20 * con_scalefactor;	// 20 = 8+8+4

	// toggle console in
	con_destlines = (cons_height.value*vid.height)/100;
	if (con_destlines < minheight)
		con_destlines = minheight;
	else if (con_destlines > vid.height)
		con_destlines = vid.height;

	con_destlines &= ~0x3; // multiple of text row height
Alam Ed Arias's avatar
Alam Ed Arias committed
// Handles Console moves in/out of screen (per frame)
//
static void CON_MoveConsole(void)
{
	fixed_t conspeed;

	Lock_state();

	conspeed = FixedDiv(cons_speed.value*vid.fdupy, FRACUNIT);
Alam Ed Arias's avatar
Alam Ed Arias committed

	// instant
	if (!cons_speed.value)
	{
		con_curlines = con_destlines;
		return;
	}

	// up/down move to dest
	if (con_curlines < con_destlines)
	{
		con_curlines += FixedInt(conspeed);
		if (con_curlines > con_destlines)
			con_curlines = con_destlines;
	}
	else if (con_curlines > con_destlines)
	{
		con_curlines -= FixedInt(conspeed);
		if (con_curlines < con_destlines)
			con_curlines = con_destlines;
	}
Alam Ed Arias's avatar
Alam Ed Arias committed
}

INT32 CON_ShiftChar(INT32 ch)
{
	if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
	{
		if (shiftdown ^ capslock)
			ch = shiftxform[ch];
	}
	else	// if we're holding shift we should still shift non letter symbols
	{
		if (shiftdown)
			ch = shiftxform[ch];
	}

	return ch;
}

Alam Ed Arias's avatar
Alam Ed Arias committed
// Clear time of console heads up messages
//
void CON_ClearHUD(void)
{
	INT32 i;

Alam Ed Arias's avatar
Alam Ed Arias committed
	for (i = 0; i < con_hudlines; i++)
		con_hudtime[i] = 0;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Force console to move out immediately
// note: con_ticker will set consoleready false
void CON_ToggleOff(void)
{
Alam Ed Arias's avatar
Alam Ed Arias committed
	if (!con_destlines)
	{
		Unlock_state();
Alam Ed Arias's avatar
Alam Ed Arias committed
		return;
Alam Ed Arias's avatar
Alam Ed Arias committed

	con_destlines = 0;
	con_curlines = 0;
	CON_ClearHUD();
	con_forcepic = 0;
	con_clipviewtop = -1; // remove console clipping of view
Alam Ed Arias's avatar
Alam Ed Arias committed
}

boolean CON_Ready(void)
{
	boolean ready;
	Lock_state();
	{
		ready = consoleready;
	}
	Unlock_state();
	return ready;
Alam Ed Arias's avatar
Alam Ed Arias committed
}

// Console ticker: handles console move in/out, cursor blinking
//
void CON_Ticker(void)
{
	INT32 i;
	INT32 minheight;

	Lock_state();

	minheight = 20 * con_scalefactor;	// 20 = 8+8+4
Alam Ed Arias's avatar
Alam Ed Arias committed

	// cursor blinking
	con_tick++;
	con_tick &= 7;

	// if the menu is open then close the console.
	if (menuactive && con_destlines)
	{
		consoletoggle = false;
		con_destlines = 0;
		CON_ClearHUD();
	}

	// console key was pushed
	if (consoletoggle)
	{
		consoletoggle = false;

		// toggle off console
		if (con_destlines > 0)
		{
			con_destlines = 0;
			CON_ClearHUD();
		}
		else
			CON_ChangeHeight();
Alam Ed Arias's avatar
Alam Ed Arias committed
	}

	// console movement
	if (con_destlines != con_curlines)
		CON_MoveConsole();

	// clip the view, so that the part under the console is not drawn
	con_clipviewtop = -1;
	if (cons_backpic.value) // clip only when using an opaque background
	{
		if (con_curlines > 0)
			con_clipviewtop = con_curlines - viewwindowy - 1 - 10;
		// NOTE: BIG HACK::SUBTRACT 10, SO THAT WATER DON'T COPY LINES OF THE CONSOLE
		//       WINDOW!!! (draw some more lines behind the bottom of the console)
		if (con_clipviewtop < 0)
			con_clipviewtop = -1; // maybe not necessary, provided it's < 0
	}

	// check if console ready for prompt
	if (con_destlines >= minheight)
		consoleready = true;
	else
		consoleready = false;

	// make overlay messages disappear after a while
	for (i = 0; i < con_hudlines; i++)
	{
		con_hudtime[i]--;
		if (con_hudtime[i] < 0)
			con_hudtime[i] = 0;
	}
Alam Ed Arias's avatar
Alam Ed Arias committed
}

//
// ----
//
// Shortcuts for adding and deleting characters, strings, and sections
// Necessary due to moving cursor
//

static void CON_InputClear(void)
	memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
	input_cur = input_sel = input_len = 0;
static void CON_InputSetString(const char *c)
	memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
	strcpy(inputlines[inputline], c);
	input_cur = input_sel = input_len = strlen(c);
static void CON_InputAddString(const char *c)
{
	size_t csize = strlen(c);
	if (input_len + csize > CON_MAXPROMPTCHARS-1)
	{
		Unlock_state();
	if (input_cur != input_len)
		memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
	memcpy(&inputlines[inputline][input_cur], c, csize);
	input_len += csize;
	input_sel = (input_cur += csize);
static void CON_InputDelSelection(void)
{
	size_t start, end, len;
	if (input_cur > input_sel)
	{
		start = input_sel;
		end = input_cur;
	}
	else
	{
		start = input_cur;
		end = input_sel;
	}
	len = (end - start);

	if (end != input_len)
		memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
	memset(&inputlines[inputline][input_len - len], 0, len);

	input_len -= len;
	input_sel = input_cur = start;
static void CON_InputAddChar(char c)
{
	if (input_len >= CON_MAXPROMPTCHARS-1)
		return;
	if (input_cur != input_len)
		memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
	inputlines[inputline][input_cur++] = c;
	inputlines[inputline][++input_len] = 0;
	input_sel = input_cur;
static void CON_InputDelChar(void)
{
	if (!input_cur)
		return;
	if (input_cur != input_len)
		memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
	inputlines[inputline][--input_len] = 0;
	input_sel = --input_cur;
Alam Ed Arias's avatar
Alam Ed Arias committed
// Handles console key input
//
boolean CON_Responder(event_t *ev)
{
	static UINT8 consdown = false; // console is treated differently due to rare usage
Alam Ed Arias's avatar
Alam Ed Arias committed

	// sequential completions a la 4dos
	static char completion[80];
	static INT32 comskips, varskips;

	const char *cmd = "";
	INT32 key;

	if (chat_on)
		return false;

	// let go keyup events, don't eat them
	if (ev->type != ev_keydown && ev->type != ev_console)
	{
		if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
Alam Ed Arias's avatar
Alam Ed Arias committed
			consdown = false;
		return false;
	}

	key = ev->data1;

	// check for console toggle key
	if (ev->type != ev_console)
	{
		if (modeattacking || metalrecording)
			return false;

Sal's avatar
Sal committed
		if (ev->data1 >= KEY_MOUSE1) // See also: HUD_Responder
Sal's avatar
Sal committed
			INT32 i;
			for (i = 0; i < num_gamecontrols; i++)
			{
Sal's avatar
Sal committed
				if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1)
					break;
Sal's avatar
Sal committed

Sal's avatar
Sal committed
			if (i == num_gamecontrols)
Sal's avatar
Sal committed
				return false;
Alam Ed Arias's avatar
Alam Ed Arias committed
		if (key == gamecontrol[gc_console][0] || key == gamecontrol[gc_console][1])
		{
			if (consdown) // ignore repeat
				return true;
			consoletoggle = true;
			consdown = true;
			return true;
		}

		// check other keys only if console prompt is active
		if (!consoleready && key < NUMINPUTS) // metzgermeister: boundary check!!
		{
			if (bindtable[key])
			{
				COM_BufAddText(bindtable[key]);
				COM_BufAddText("\n");
				return true;
			}
			return false;
		}

		// escape key toggle off console
		if (key == KEY_ESCAPE)
		{
			consoletoggle = true;
			return true;
		}
	}

	// Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
	if (key == KEY_LSHIFT || key == KEY_RSHIFT
	 || key == KEY_LCTRL || key == KEY_RCTRL
	 || key == KEY_LALT || key == KEY_RALT)
		return true;

Inuyasha's avatar
Inuyasha committed
	// ctrl modifier -- changes behavior, adds shortcuts
	if (ctrldown)
Alam Ed Arias's avatar
Alam Ed Arias committed
	{
		// show all cvars/commands that match what we have inputted
Inuyasha's avatar
Inuyasha committed
		if (key == KEY_TAB)
Alam Ed Arias's avatar
Alam Ed Arias committed
		{
			size_t i, len;
Alam Ed Arias's avatar
Alam Ed Arias committed

			if (!completion[0])
			{
				if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
					return true;
				strcpy(completion, inputlines[inputline]);
				comskips = varskips = 0;
			}
			len = strlen(completion);
Alam Ed Arias's avatar
Alam Ed Arias committed

			//first check commands
			CONS_Printf("\nCommands:\n");
Inuyasha's avatar
Inuyasha committed
			for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
			if (i == 0) CONS_Printf("  (none)\n");
Alam Ed Arias's avatar
Alam Ed Arias committed

			//now we move on to CVARs
			CONS_Printf("Variables:\n");
Inuyasha's avatar
Inuyasha committed
			for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
				CONS_Printf("  \x83" "%s" "\x80" "%s\n", completion, cmd+len);
			if (i == 0) CONS_Printf("  (none)\n");
Alam Ed Arias's avatar
Alam Ed Arias committed

Inuyasha's avatar
Inuyasha committed
			return true;
		}
		// ---
Alam Ed Arias's avatar
Alam Ed Arias committed

Inuyasha's avatar
Inuyasha committed
		if (key == KEY_HOME) // oldest text in buffer
		{
			con_scrollup = (con_totallines-((con_curlines-16)>>3));
			return true;
		}
		else if (key == KEY_END) // most recent text in buffer
		{
			con_scrollup = 0;
			return true;