2015-02-11 01:00:36 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>.
|
|
|
|
* It is copyright by its individual contributors, as recorded in the
|
|
|
|
* project's Git history. See COPYING.txt at the top level for license
|
|
|
|
* terms and a link to the Git history.
|
|
|
|
*
|
|
|
|
* Based on an early version of SDL_Console
|
|
|
|
* Written By: Garrett Banuk <mongoose@mongeese.org>
|
|
|
|
* Code Cleanup and heavily extended by: Clemens Wacha <reflex-2000@gmx.net>
|
|
|
|
* Ported to use native Descent interfaces by: Bradley Bell <btb@icculus.org>
|
|
|
|
*
|
|
|
|
* This is free, just be sure to give us credit when using it
|
|
|
|
* in any of your programs.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Command-line interface for the console
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "inferno.h"
|
|
|
|
#include "maths.h"
|
|
|
|
#include "gr.h"
|
|
|
|
#include "timer.h"
|
|
|
|
#include "u_mem.h"
|
|
|
|
#include "strutil.h"
|
|
|
|
#include "gamefont.h"
|
|
|
|
#include "console.h"
|
|
|
|
|
|
|
|
|
|
|
|
#define CLI_HISTORY_MAX 128
|
|
|
|
// Cut the buffer line if it becomes longer than this
|
|
|
|
#define CLI_CHARS_PER_LINE 128
|
2015-02-12 06:34:18 +00:00
|
|
|
// Cursor blink interval
|
2015-02-11 01:00:36 +00:00
|
|
|
#define CLI_BLINK_RATE (F1_0/2)
|
|
|
|
// Border in pixels from the most left to the first letter
|
|
|
|
#define CLI_CHAR_BORDER FSPACX(1)
|
|
|
|
// Default prompt used at the commandline
|
|
|
|
#define CLI_DEFAULT_PROMPT "]"
|
|
|
|
// Cursor shown if we are in insert mode
|
|
|
|
#define CLI_INS_CURSOR "_"
|
|
|
|
// Cursor shown if we are in overwrite mode
|
|
|
|
#define CLI_OVR_CURSOR "|"
|
|
|
|
|
|
|
|
int CLI_insert_mode; // Insert or Overwrite characters?
|
|
|
|
|
2015-06-07 16:20:46 +00:00
|
|
|
static array<RAIIdmem<char[]>, CLI_HISTORY_MAX> CommandLines; // List of all the past commands
|
2015-02-11 01:00:36 +00:00
|
|
|
static int TotalCommands; // Number of commands in the Back Commands
|
2015-06-07 16:20:46 +00:00
|
|
|
static RAIIdmem<char[]> Prompt; // Prompt displayed in command line
|
2015-02-11 01:00:36 +00:00
|
|
|
static char Command[CLI_CHARS_PER_LINE]; // current command in command line = lcommand + rcommand
|
|
|
|
static char LCommand[CLI_CHARS_PER_LINE]; // right hand side of cursor
|
|
|
|
static char RCommand[CLI_CHARS_PER_LINE]; // left hand side of cursor
|
|
|
|
static char VCommand[CLI_CHARS_PER_LINE]; // current visible command line
|
2015-02-16 06:21:30 +00:00
|
|
|
static uint_fast32_t CursorPos; // Current cursor position in CurrentCommand
|
|
|
|
static uint_fast32_t Offset; // CommandOffset (first visible char of command) - if command is too long to fit into console
|
2015-02-11 01:00:36 +00:00
|
|
|
static int CommandScrollBack; // How much the users scrolled back in the command lines
|
|
|
|
|
|
|
|
/* Initializes the cli */
|
|
|
|
void cli_init()
|
|
|
|
{
|
|
|
|
TotalCommands = 0;
|
|
|
|
CLI_insert_mode = 1;
|
|
|
|
CursorPos = 0;
|
|
|
|
CommandScrollBack = 0;
|
2015-06-07 16:20:46 +00:00
|
|
|
Prompt.reset(d_strdup(CLI_DEFAULT_PROMPT));
|
2015-02-11 01:00:36 +00:00
|
|
|
|
2015-02-16 08:08:18 +00:00
|
|
|
CommandLines = {};
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(Command, 0, sizeof(Command));
|
|
|
|
memset(LCommand, 0, sizeof(LCommand));
|
|
|
|
memset(RCommand, 0, sizeof(RCommand));
|
|
|
|
memset(VCommand, 0, sizeof(VCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Increments the command lines */
|
|
|
|
static void cli_newline(void)
|
|
|
|
{
|
2015-06-07 16:20:46 +00:00
|
|
|
std::move(CommandLines.begin(), std::prev(CommandLines.end()), std::next(CommandLines.begin()));
|
2015-02-11 01:00:36 +00:00
|
|
|
if (TotalCommands < CLI_HISTORY_MAX - 1)
|
|
|
|
TotalCommands++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Draws the command line the user is typing in to the screen */
|
|
|
|
/* completely rewritten by C.Wacha */
|
|
|
|
void cli_draw(int y)
|
|
|
|
{
|
|
|
|
int x, w, h, aw;
|
|
|
|
float real_aw;
|
2015-02-16 06:21:30 +00:00
|
|
|
uint_fast32_t commandbuffer;
|
2015-02-12 06:34:18 +00:00
|
|
|
fix cur_time = timer_query();
|
2015-02-11 01:00:36 +00:00
|
|
|
static fix LastBlinkTime = 0; // Last time the cursor blinked
|
2015-02-16 06:21:30 +00:00
|
|
|
static uint_fast32_t LastCursorPos = 0; // Last cursor position
|
2015-02-11 01:00:36 +00:00
|
|
|
static int Blink = 0; // Is the cursor currently blinking
|
|
|
|
|
|
|
|
// Concatenate the left and right side to command
|
|
|
|
strcpy(Command, LCommand);
|
2015-02-16 02:18:03 +00:00
|
|
|
strncat(Command, RCommand, sizeof(Command) - strlen(Command) - 1);
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
gr_get_string_size(Command, &w, &h, &aw);
|
2015-02-12 06:34:18 +00:00
|
|
|
if (w > 0 && *Command)
|
2015-02-11 01:00:36 +00:00
|
|
|
real_aw = (float)w/(float)strlen(Command);
|
|
|
|
else
|
|
|
|
real_aw = (float)aw;
|
2015-06-07 16:20:46 +00:00
|
|
|
commandbuffer = (GWIDTH - 2*CLI_CHAR_BORDER)/real_aw - strlen(Prompt.get()) - 1; // -1 to make cursor visible
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
//calculate display offset from current cursor position
|
2015-02-16 06:21:30 +00:00
|
|
|
if (CursorPos > commandbuffer && Offset < CursorPos - commandbuffer)
|
2015-02-11 01:00:36 +00:00
|
|
|
Offset = CursorPos - commandbuffer;
|
|
|
|
if(Offset > CursorPos)
|
|
|
|
Offset = CursorPos;
|
|
|
|
|
|
|
|
// first add prompt to visible part
|
2015-06-07 16:20:46 +00:00
|
|
|
strcpy(VCommand, Prompt.get());
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
// then add the visible part of the command
|
2015-02-16 02:18:03 +00:00
|
|
|
strncat(VCommand, &Command[Offset], sizeof(VCommand) - strlen(VCommand) - 1);
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
// now display the result
|
|
|
|
gr_string(CLI_CHAR_BORDER, y-h, VCommand);
|
|
|
|
|
|
|
|
// at last add the cursor
|
|
|
|
// check if the blink period is over
|
2015-02-12 06:34:18 +00:00
|
|
|
if (cur_time > LastBlinkTime) {
|
|
|
|
LastBlinkTime = cur_time + CLI_BLINK_RATE;
|
2015-02-11 01:00:36 +00:00
|
|
|
if(Blink)
|
|
|
|
Blink = 0;
|
|
|
|
else
|
|
|
|
Blink = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if cursor has moved - if yes display cursor anyway
|
|
|
|
if (CursorPos != LastCursorPos) {
|
|
|
|
LastCursorPos = CursorPos;
|
2015-02-12 06:34:18 +00:00
|
|
|
LastBlinkTime = cur_time + CLI_BLINK_RATE;
|
2015-02-11 01:00:36 +00:00
|
|
|
Blink = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Blink) {
|
2015-09-29 02:41:22 +00:00
|
|
|
int prompt_width, cmd_width, h;
|
2015-02-11 01:00:36 +00:00
|
|
|
|
2015-09-29 02:41:22 +00:00
|
|
|
gr_get_string_size(Prompt.get(), &prompt_width, nullptr, nullptr);
|
|
|
|
gr_get_string_size(LCommand + Offset, &cmd_width, &h, nullptr);
|
2015-02-11 01:00:36 +00:00
|
|
|
x = CLI_CHAR_BORDER + prompt_width + cmd_width;
|
2015-06-07 16:20:47 +00:00
|
|
|
gr_string(x, y-h, CLI_insert_mode ? CLI_INS_CURSOR : CLI_OVR_CURSOR);
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Executes the command entered */
|
|
|
|
void cli_execute(void)
|
|
|
|
{
|
|
|
|
if(strlen(Command) > 0) {
|
|
|
|
cli_newline();
|
|
|
|
|
|
|
|
// copy the input into the past commands strings
|
2015-06-07 16:20:46 +00:00
|
|
|
CommandLines[0].reset(d_strdup(Command));
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
// display the command including the prompt
|
2015-06-07 16:20:46 +00:00
|
|
|
con_printf(CON_NORMAL, "%s%s", Prompt.get(), Command);
|
2015-02-11 01:00:36 +00:00
|
|
|
|
2015-02-11 07:35:44 +00:00
|
|
|
cmd_append(Command);
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
cli_clear();
|
|
|
|
CommandScrollBack = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_autocomplete(void)
|
|
|
|
{
|
2015-02-16 06:21:30 +00:00
|
|
|
uint_fast32_t i, j;
|
2015-02-12 06:34:18 +00:00
|
|
|
const char *command;
|
2015-02-11 01:00:36 +00:00
|
|
|
|
2015-02-11 07:35:44 +00:00
|
|
|
command = cmd_complete(LCommand);
|
2015-02-11 01:00:36 +00:00
|
|
|
|
|
|
|
if (!command)
|
|
|
|
return; // no tab completion took place so return silently
|
|
|
|
|
2015-02-16 06:21:30 +00:00
|
|
|
j = strlen(command);
|
2015-02-11 01:00:36 +00:00
|
|
|
if (j > CLI_CHARS_PER_LINE - 2)
|
|
|
|
j = CLI_CHARS_PER_LINE - 1;
|
|
|
|
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(LCommand, 0, sizeof(LCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
CursorPos = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < j; i++) {
|
|
|
|
CursorPos++;
|
|
|
|
LCommand[i] = command[i];
|
|
|
|
}
|
|
|
|
// add a trailing space
|
|
|
|
CursorPos++;
|
|
|
|
LCommand[j] = ' ';
|
|
|
|
LCommand[j+1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_cursor_left(void)
|
|
|
|
{
|
|
|
|
char temp[CLI_CHARS_PER_LINE];
|
|
|
|
|
|
|
|
if (CursorPos > 0) {
|
|
|
|
CursorPos--;
|
|
|
|
strcpy(temp, RCommand);
|
|
|
|
strcpy(RCommand, &LCommand[strlen(LCommand)-1]);
|
|
|
|
strcat(RCommand, temp);
|
|
|
|
LCommand[strlen(LCommand)-1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_cursor_right(void)
|
|
|
|
{
|
|
|
|
char temp[CLI_CHARS_PER_LINE];
|
|
|
|
|
|
|
|
if(CursorPos < strlen(Command)) {
|
|
|
|
CursorPos++;
|
|
|
|
strncat(LCommand, RCommand, 1);
|
|
|
|
strcpy(temp, RCommand);
|
|
|
|
strcpy(RCommand, &temp[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_cursor_home(void)
|
|
|
|
{
|
|
|
|
char temp[CLI_CHARS_PER_LINE];
|
|
|
|
|
|
|
|
CursorPos = 0;
|
|
|
|
strcpy(temp, RCommand);
|
|
|
|
strcpy(RCommand, LCommand);
|
|
|
|
strncat(RCommand, temp, strlen(temp));
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(LCommand, 0, sizeof(LCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_cursor_end(void)
|
|
|
|
{
|
2015-02-16 06:21:30 +00:00
|
|
|
CursorPos = strlen(Command);
|
2015-02-11 01:00:36 +00:00
|
|
|
strncat(LCommand, RCommand, strlen(RCommand));
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(RCommand, 0, sizeof(RCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_cursor_del(void)
|
|
|
|
{
|
|
|
|
char temp[CLI_CHARS_PER_LINE];
|
|
|
|
|
|
|
|
if (strlen(RCommand) > 0) {
|
|
|
|
strcpy(temp, RCommand);
|
|
|
|
strcpy(RCommand, &temp[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_cursor_backspace(void)
|
|
|
|
{
|
|
|
|
if (CursorPos > 0) {
|
|
|
|
CursorPos--;
|
2015-02-16 06:21:30 +00:00
|
|
|
if (Offset > 0)
|
|
|
|
Offset--;
|
2015-02-11 01:00:36 +00:00
|
|
|
LCommand[strlen(LCommand)-1] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_add_character(char character)
|
|
|
|
{
|
|
|
|
if (strlen(Command) < CLI_CHARS_PER_LINE - 1)
|
|
|
|
{
|
|
|
|
CursorPos++;
|
|
|
|
LCommand[strlen(LCommand)] = character;
|
|
|
|
LCommand[strlen(LCommand)] = '\0';
|
|
|
|
}
|
|
|
|
if (!CLI_insert_mode)
|
|
|
|
cli_cursor_del();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_clear(void)
|
|
|
|
{
|
|
|
|
CursorPos = 0;
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(Command, 0, sizeof(Command));
|
|
|
|
memset(LCommand, 0, sizeof(LCommand));
|
|
|
|
memset(RCommand, 0, sizeof(RCommand));
|
|
|
|
memset(VCommand, 0, sizeof(VCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_history_prev(void)
|
|
|
|
{
|
|
|
|
if(CommandScrollBack < TotalCommands - 1) {
|
|
|
|
/* move back a line in the command strings and copy the command to the current input string */
|
|
|
|
CommandScrollBack++;
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(RCommand, 0, sizeof(RCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
Offset = 0;
|
2015-06-07 16:20:46 +00:00
|
|
|
strcpy(LCommand, CommandLines[CommandScrollBack].get());
|
|
|
|
CursorPos = strlen(CommandLines[CommandScrollBack].get());
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cli_history_next(void)
|
|
|
|
{
|
|
|
|
if(CommandScrollBack > -1) {
|
|
|
|
/* move forward a line in the command strings and copy the command to the current input string */
|
|
|
|
CommandScrollBack--;
|
2015-02-16 02:18:03 +00:00
|
|
|
memset(RCommand, 0, sizeof(RCommand));
|
|
|
|
memset(LCommand, 0, sizeof(LCommand));
|
2015-02-11 01:00:36 +00:00
|
|
|
Offset = 0;
|
|
|
|
if(CommandScrollBack > -1)
|
2015-06-07 16:20:46 +00:00
|
|
|
strcpy(LCommand, CommandLines[CommandScrollBack].get());
|
2015-02-16 06:21:30 +00:00
|
|
|
CursorPos = strlen(LCommand);
|
2015-02-11 01:00:36 +00:00
|
|
|
}
|
|
|
|
}
|