From 20fdc9b9e5db808c38d58ef20842a63f0c367a26 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Mon, 16 Jul 2001 21:39:50 +0000 Subject: [PATCH] add the command line completion code (complete.c) and the client and server plugins (though they're not built yet). Again, this /does not/ affect anybody else, or shouldn't, anyway. --- libs/console/Makefile.am | 2 +- libs/console/client.c | 554 +++++++++++++++++++++++++++++++++++++++ libs/console/complete.c | 150 +++++++++++ libs/console/server.c | 296 +++++++++++++++++++++ 4 files changed, 1001 insertions(+), 1 deletion(-) create mode 100644 libs/console/client.c create mode 100644 libs/console/complete.c create mode 100644 libs/console/server.c diff --git a/libs/console/Makefile.am b/libs/console/Makefile.am index f2fa54587..c26e0b40f 100644 --- a/libs/console/Makefile.am +++ b/libs/console/Makefile.am @@ -3,7 +3,7 @@ INCLUDES= -I$(top_srcdir)/include lib_LTLIBRARIES = libQFconsole.la -common_SOURCES = console.c list.c +common_SOURCES = complete.c console.c list.c libQFconsole_la_LDFLAGS = -version-info 1:0:0 libQFconsole_la_LIBADD = diff --git a/libs/console/client.c b/libs/console/client.c new file mode 100644 index 000000000..a489d0e83 --- /dev/null +++ b/libs/console/client.c @@ -0,0 +1,554 @@ +/* + 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 HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include + +#include "QF/cmd.h" +#include "compat.h" +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/draw.h" +#include "QF/input.h" +#include "QF/keys.h" +#include "QF/qargs.h" +#include "QF/screen.h" +#include "QF/sys.h" +#include "QF/va.h" + +#include "client.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; // 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 +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 (); +} + + +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 (); +} + + +void +Con_Clear_f (void) +{ + con_main.numlines = 0; + con_chat.numlines = 0; + memset (con_main.text, ' ', CON_TEXTSIZE); + memset (con_chat.text, ' ', CON_TEXTSIZE); + con_main.display = con_main.current; +} + + +void +Con_ClearNotify (void) +{ + int i; + + for (i = 0; i < NUM_CON_TIMES; i++) + con_times[i] = 0; +} + + +void +Con_MessageMode_f (void) +{ + if (cls.state != ca_active) + return; + chat_team = false; + key_dest = key_message; +} + + +void +Con_MessageMode2_f (void) +{ + if (cls.state != ca_active) + return; + chat_team = true; + key_dest = key_message; +} + + +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; + 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 < 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); +} + + +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 + Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f, + "Toggle the console up and down"); + Cmd_AddCommand ("togglechat", Con_ToggleChat_f, + "Toggle the console up and down"); + Cmd_AddCommand ("messagemode", Con_MessageMode_f, + "Prompt to send a message to everyone"); + Cmd_AddCommand ("messagemode2", Con_MessageMode2_f, + "Prompt to send a message to only people on your team"); + Cmd_AddCommand ("clear", Con_Clear_f, "Clear the console"); + con_initialized = true; +} + + +void +Con_Init_Cvars (void) +{ + con_notifytime = + Cvar_Get ("con_notifytime", "3", CVAR_NONE, NULL, + "How long in seconds messages are displayed on screen"); +} + + +void +Con_Linefeed (void) +{ + con->x = 0; + if (con->display == con->current) + con->display++; + con->current++; + if (con->numlines < con_totallines) + con->numlines++; + 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; + + // echo to debugging console + Sys_Printf ("%s", txt); + + // log all messages to file + if (con_debuglog) + Sys_DebugLog (va ("%s/qconsole.log", com_gamedir), "%s", txt); + + if (!con_initialized) + return; + + 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; + } + + } +} + + +/* + 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; + char temp[MAXCMDLINE]; + + if (key_dest != key_console && cls.state == ca_active) + return; // don't draw anything (always draw if not active) + + text = strcpy (temp, key_lines[edit_line]); + + // fill out remainder with spaces + for (i = strlen (text); i < MAXCMDLINE; i++) + text[i] = ' '; + + // add the cursor frame + if ((int) (realtime * con_cursorspeed) & 1) + text[key_linepos] = 11; + + // 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_Character8 ((i + 1) << 3, con_vislines - 22, text[i]); +} + + +/* + 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_Character8 ((x + 1) << 3, v, text[x]); + + v += 8; + } + + if (key_dest == key_message) { + clearnotify = 0; + scr_copytop = 1; + + if (chat_team) { + Draw_String8 (8, v, "say_team:"); + skip = 11; + } else { + Draw_String8 (8, v, "say:"); + skip = 5; + } + + s = chat_buffer; + if (chat_bufferlen > (vid.width >> 3) - (skip + 1)) + s += chat_bufferlen - ((vid.width >> 3) - (skip + 1)); + x = 0; + while (s[x]) { + Draw_Character8 ((x + skip) << 3, v, s[x]); + x++; + } + Draw_Character8 ((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, x, y; + int rows; + char *text; + int row; + + 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_Character8 ((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_Character8 ((x + 1) << 3, y, text[x]); + } + + // draw the input prompt, user text, and cursor if desired + Con_DrawInput (); +} + + +void +Con_DrawDownload (int lines) +{ + int i, j, x, y, n; + char *text; + char dlbar[1024]; + + if (!cls.download) + return; + + text = COM_SkipPath(cls.downloadname); + + x = con_linewidth - ((con_linewidth * 7) / 40); + y = x - strlen (text) - 8; + i = con_linewidth / 3; + if (strlen (text) > i) { + y = x - i - 11; + strncpy (dlbar, text, i); + dlbar[i] = 0; + strncat (dlbar, "...", sizeof (dlbar) - strlen (dlbar)); + } else + strncpy (dlbar, text, sizeof (dlbar)); + strncat (dlbar, ": ", sizeof (dlbar) - strlen (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; + + snprintf (dlbar + strlen (dlbar), sizeof (dlbar) - strlen (dlbar), + " %02d%%", cls.downloadpercent); + // draw it + y = lines - 22 + 8; + for (i = 0; i < strlen (dlbar); i++) + Draw_Character8 ((i + 1) << 3, y, dlbar[i]); +} diff --git a/libs/console/complete.c b/libs/console/complete.c new file mode 100644 index 000000000..500d39d1d --- /dev/null +++ b/libs/console/complete.c @@ -0,0 +1,150 @@ +/* + complete.c + + command completion + + 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 HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#include + +#include "QF/cmd.h" +#include "QF/console.h" +#include "QF/cvar.h" + +//FIXME eww +#define MAXCMDLINE 256 +extern char key_lines[32][MAXCMDLINE]; +extern int edit_line; +extern int key_linepos; + +extern int con_linewidth; + +/* + Con_CompleteCommandLine + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +void +Con_CompleteCommandLine (void) +{ + const char *cmd = ""; + char *s; + int c, v, a, i; + int cmd_len; + const char **list[3] = {0, 0, 0}; + + s = key_lines[edit_line] + 1; + if (*s == '\\' || *s == '/') + s++; + + // Count number of possible matches + c = Cmd_CompleteCountPossible(s); + v = Cvar_CompleteCountPossible(s); + a = Cmd_CompleteAliasCountPossible(s); + + if (!(c + v + a)) // No possible matches + return; + + if (c + v + a == 1) { + if (c) + list[0] = Cmd_CompleteBuildList(s); + else if (v) + list[0] = Cvar_CompleteBuildList(s); + else + list[0] = Cmd_CompleteAliasBuildList(s); + cmd = *list[0]; + cmd_len = strlen (cmd); + } else { + if (c) + cmd = *(list[0] = Cmd_CompleteBuildList(s)); + if (v) + cmd = *(list[1] = Cvar_CompleteBuildList(s)); + if (a) + cmd = *(list[2] = Cmd_CompleteAliasBuildList(s)); + + cmd_len = strlen (s); + do { + for (i = 0; i < 3; i++) { + char ch = cmd[cmd_len]; + const char **l = list[i]; + if (l) { + while (*l && (*l)[cmd_len] == ch) + l++; + if (*l) + break; + } + } + if (i == 3) + cmd_len++; + } while (i == 3); + // 'quakebar' + Con_Printf("\n\35"); + for (i = 0; i < con_linewidth - 4; i++) + Con_Printf("\36"); + Con_Printf("\37\n"); + + // Print Possible Commands + if (c) { + Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":"); + Con_DisplayList(list[0], con_linewidth); + } + + if (v) { + Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":"); + Con_DisplayList(list[1], con_linewidth); + } + + if (a) { + Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":"); + Con_DisplayList(list[2], con_linewidth); + } + } + + if (cmd) { + key_lines[edit_line][1] = '/'; + strncpy(key_lines[edit_line] + 2, cmd, cmd_len); + key_linepos = cmd_len + 2; + if (c + v + a == 1) { + key_lines[edit_line][key_linepos] = ' '; + key_linepos++; + } + key_lines[edit_line][key_linepos] = 0; + } + for (i = 0; i < 3; i++) + if (list[i]) + free (list[i]); +} diff --git a/libs/console/server.c b/libs/console/server.c new file mode 100644 index 000000000..1d536e0b8 --- /dev/null +++ b/libs/console/server.c @@ -0,0 +1,296 @@ +/* + sv_console.c + + ncurses console for the server + + Copyright (C) 2001 Bill Currie + + Author: Bill Currie + Date: 2001/7/10 + + 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 +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#ifdef HAVE_CURSES_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif + +#include "QF/cmd.h" +#include "QF/console.h" +#include "QF/cvar.h" +#include "QF/qtypes.h" +#include "QF/sys.h" + +static WINDOW *output; +static WINDOW *status; +static WINDOW *input; +static int screen_x, screen_y; + +#define MAXCMDLINE 256 +char key_lines[32][MAXCMDLINE]; +int edit_line; +int history_line; +int key_linepos; + +static chtype attr_table[4] = { + A_NORMAL, + COLOR_PAIR(1), + COLOR_PAIR(2), + COLOR_PAIR(3), +}; + +static const byte attr_map[256] = { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 3, 3, 0, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +}; + +void +Con_Init (void) +{ + int i; + + for (i = 0; i < 32; i++) { + key_lines[i][0] = ']'; + key_lines[i][1] = 0; + } + key_linepos = 1; + + initscr (); + start_color (); + cbreak (); + noecho (); + + nonl (); + intrflush (stdscr, FALSE); + + getmaxyx (stdscr, screen_y, screen_x); + output = subwin (stdscr, screen_y - 2, screen_x, 0, 0); + status = subwin (stdscr, 1, screen_x, screen_y - 2, 0); + input = subwin (stdscr, 1, screen_x, screen_y - 1, 0); + + init_pair (1, COLOR_YELLOW, COLOR_BLACK); + init_pair (2, COLOR_GREEN, COLOR_BLACK); + init_pair (3, COLOR_RED, COLOR_BLACK); + init_pair (4, COLOR_YELLOW, COLOR_BLUE); + + scrollok (output, TRUE); + leaveok (output, TRUE); + + scrollok (status, FALSE); + leaveok (status, TRUE); + + scrollok (input, FALSE); + nodelay (input, TRUE); + keypad (input, TRUE); + + wclear (output); + wbkgdset (status, COLOR_PAIR(4)); + wclear (status); + wclear (input); + touchwin (stdscr); + wrefresh (output); + wrefresh (status); + wrefresh (input); +} + +void +Con_Shutdown (void) +{ + endwin (); +} + +void +Con_Print (char *txt) +{ + chtype ch; + + while ((ch = *txt++)) { + ch = sys_char_map[ch] | attr_table[attr_map[ch]]; + waddch (output, ch); + } + touchwin (stdscr); + wrefresh (output); +} + +void +Con_ProcessInput (void) +{ + int ch = wgetch (input); + int i; + int curs_x; + char *text = 0; + + static int scroll; + + switch (ch) { + case KEY_ENTER: + case '\n': + case '\r': + if (key_lines[edit_line][1] == '/' + && key_lines[edit_line][2] == '/') + goto no_lf; + else if (key_lines[edit_line][1] == '\\' + || key_lines[edit_line][1] == '/') + Cbuf_AddText (key_lines[edit_line] + 2); + else + Cbuf_AddText (key_lines[edit_line] + 1); + Cbuf_AddText ("\n"); + no_lf: + Con_Printf ("%s\n", key_lines[edit_line]); + edit_line = (edit_line + 1) & 31; + history_line = edit_line; + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + key_linepos = 1; + break; + case '\t': + Con_CompleteCommandLine(); + break; + case KEY_BACKSPACE: + if (key_linepos > 1) { + strcpy (key_lines[edit_line] + key_linepos - 1, + key_lines[edit_line] + key_linepos); + key_linepos--; + } + break; + case KEY_DC: + if (key_linepos < strlen (key_lines[edit_line])) + strcpy (key_lines[edit_line] + key_linepos, + key_lines[edit_line] + key_linepos + 1); + break; + case KEY_RIGHT: + if (key_linepos < strlen (key_lines[edit_line])) + key_linepos++; + break; + case KEY_LEFT: + if (key_linepos > 1) + key_linepos--; + break; + case KEY_UP: + do { + history_line = (history_line - 1) & 31; + } while (history_line != edit_line && !key_lines[history_line][1]); + if (history_line == edit_line) + history_line = (edit_line + 1) & 31; + strcpy (key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen (key_lines[edit_line]); + break; + case KEY_DOWN: + if (history_line == edit_line) + break; + do { + history_line = (history_line + 1) & 31; + } while (history_line != edit_line && !key_lines[history_line][1]); + if (history_line == edit_line) { + key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; + key_linepos = 1; + } else { + strcpy (key_lines[edit_line], key_lines[history_line]); + key_linepos = strlen (key_lines[edit_line]); + } + break; + case KEY_PPAGE: + break; + case KEY_NPAGE: + break; + case KEY_HOME: + key_linepos = 1; + break; + case KEY_END: + key_linepos = strlen (key_lines[edit_line]); + break; + default: + if (ch >= ' ' && ch < 127) { + i = strlen (key_lines[edit_line]); + if (i >= MAXCMDLINE - 1) + break; + // This also moves the ending \0 + memmove (key_lines[edit_line] + key_linepos + 1, + key_lines[edit_line] + key_linepos, + i - key_linepos + 1); + key_lines[edit_line][key_linepos] = ch; + key_linepos++; + } + break; + } + + i = key_linepos - 1; + if (scroll > i) + scroll = i; + if (scroll < i - (screen_x - 2) + 1) + scroll = i - (screen_x - 2) + 1; + text = key_lines[edit_line] + scroll + 1; + if ((int)strlen (text) < screen_x - 2) { + scroll = strlen (key_lines[edit_line] + 1) - (screen_x - 2); + if (scroll < 0) + scroll = 0; + text = key_lines[edit_line] + scroll + 1; + } + + curs_x = key_linepos - scroll; + + wmove (input, 0, 0); + if (scroll) { + waddch (input, '<'); + } else { + waddch (input, key_lines[edit_line][0]); + } + for (i = 0; i < screen_x - 2 && *text; i++) + waddch (input, *text++); + while (i++ < screen_x - 2) + waddch (input, ' '); + if (*text) { + waddch (input, '>'); + } else { + waddch (input, ' '); + } + wmove (input, 0, curs_x); + touchline (stdscr, screen_y - 1, 1); + wrefresh (input); +}