From ceb63be9c69baa02da6875fcc22370043ff545c4 Mon Sep 17 00:00:00 2001 From: Tim Bergeron Date: Fri, 6 Dec 2024 22:56:42 -0800 Subject: [PATCH] add arrow key support for dedicated console --- Quake/sys_sdl_unix.c | 185 ++++++++++++++++++++++++++++++++++--------- Quake/sys_sdl_win.c | 118 ++++++++++++++++++++------- 2 files changed, 235 insertions(+), 68 deletions(-) diff --git a/Quake/sys_sdl_unix.c b/Quake/sys_sdl_unix.c index 2d713fe7..24f30ece 100644 --- a/Quake/sys_sdl_unix.c +++ b/Quake/sys_sdl_unix.c @@ -47,6 +47,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "SDL.h" #endif +#include // woods #arrowkeys +#include // woods #arrowkeys qboolean isDedicated; cvar_t sys_throttle = {"sys_throttle", "0.02", CVAR_ARCHIVE}; @@ -469,52 +471,157 @@ double Sys_DoubleTime (void) return SDL_GetTicks() / 1000.0; } -const char *Sys_ConsoleInput (void) +const char *Sys_ConsoleInput (void) // woods #arrowkeys { static char con_text[256]; static int textlen; + static int cursor_pos; // Track cursor position separately from text length char c; fd_set set; struct timeval timeout; - - FD_ZERO (&set); + static struct termios orig_termios, raw_termios; + static qboolean term_setup = false; + + // Set up terminal once + if (!term_setup) + { + if (tcgetattr(0, &orig_termios) != -1) + { + raw_termios = orig_termios; + raw_termios.c_lflag &= ~(ICANON | ECHO); // Disable canonical mode and echo + raw_termios.c_cc[VMIN] = 1; + raw_termios.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &raw_termios); + term_setup = true; + cursor_pos = 0; + } + } + + FD_ZERO (&set); FD_SET (0, &set); // stdin - timeout.tv_sec = 0; - timeout.tv_usec = 0; - - while (select (1, &set, NULL, NULL, &timeout)) - { - read (0, &c, 1); - if (c == '\n' || c == '\r') - { - con_text[textlen] = '\0'; - textlen = 0; - return con_text; - } - else if (c == 8) - { - if (textlen) - { - textlen--; - con_text[textlen] = '\0'; - } - continue; - } - con_text[textlen] = c; - textlen++; - if (textlen < (int) sizeof(con_text)) - con_text[textlen] = '\0'; - else - { - // buffer is full - textlen = 0; - con_text[0] = '\0'; - Sys_Printf("\nConsole input too long!\n"); - break; - } - } - - return NULL; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + while (select (1, &set, NULL, NULL, &timeout)) + { + ssize_t len = read(0, &c, 1); + if (len != 1) + continue; + + // Handle escape sequences for arrow keys + if (c == 27) // ESC character + { + char seq[3] = {0}; + struct timeval seq_timeout; + seq_timeout.tv_sec = 0; + seq_timeout.tv_usec = 10000; + + FD_ZERO(&set); + FD_SET(0, &set); + if (select(1, &set, NULL, NULL, &seq_timeout) > 0) + { + len = read(0, seq, 1); + if (len == 1 && seq[0] == '[') + { + FD_ZERO(&set); + FD_SET(0, &set); + if (select(1, &set, NULL, NULL, &seq_timeout) > 0) + { + len = read(0, seq + 1, 1); + if (len == 1) + { + switch (seq[1]) + { + case 'D': // Left arrow + if (cursor_pos > 0) + { + cursor_pos--; + write(1, "\b", 1); + } + continue; + case 'C': // Right arrow + if (cursor_pos < textlen) + { + write(1, &con_text[cursor_pos], 1); + cursor_pos++; + } + continue; + case 'A': // Up arrow + case 'B': // Down arrow + continue; + } + } + } + } + } + continue; + } + + if (c == '\n' || c == '\r') + { + write(1, "\n", 1); + con_text[textlen] = '\0'; + textlen = 0; + cursor_pos = 0; + return con_text; + } + else if (c == 8 || c == 127) // backspace or delete + { + if (cursor_pos > 0) + { + // Move characters after cursor back by one position + memmove(&con_text[cursor_pos - 1], &con_text[cursor_pos], textlen - cursor_pos); + cursor_pos--; + textlen--; + + // Rewrite the line from cursor position + write(1, "\b", 1); + if (cursor_pos < textlen) + { + write(1, &con_text[cursor_pos], textlen - cursor_pos); + write(1, " ", 1); // Clear last character + // Move cursor back to position + for (int i = 0; i < textlen - cursor_pos + 1; i++) + write(1, "\b", 1); + } + else + { + write(1, " \b", 2); // Clear last character + } + } + continue; + } + + if (textlen < sizeof(con_text)-1 && c >= 32 && c < 127) + { + // Insert character at cursor position + if (cursor_pos < textlen) + { + // Make room for new character + memmove(&con_text[cursor_pos + 1], &con_text[cursor_pos], textlen - cursor_pos); + con_text[cursor_pos] = c; + textlen++; + + // Write the new character and the rest of the line + write(1, &con_text[cursor_pos], textlen - cursor_pos); + + // Move cursor back to just after inserted character + cursor_pos++; + for (int i = 0; i < textlen - cursor_pos; i++) + write(1, "\b", 1); + } + else + { + // Append character at end of line + con_text[textlen] = c; + write(1, &c, 1); + textlen++; + cursor_pos++; + } + } + } + + return NULL; } void Sys_Sleep (unsigned long msecs) diff --git a/Quake/sys_sdl_win.c b/Quake/sys_sdl_win.c index 2dbc9b7c..2e628165 100644 --- a/Quake/sys_sdl_win.c +++ b/Quake/sys_sdl_win.c @@ -518,10 +518,11 @@ void Sys_Image_BGRA_To_Clipboard(byte* bmbits, int width, int height, int size) } #endif -const char *Sys_ConsoleInput (void) +const char *Sys_ConsoleInput (void) // woods #arrowkeys { static char con_text[256]; static int textlen; + static int cursor_pos; INPUT_RECORD recs[1024]; int ch; DWORD dummy, numread, numevents; @@ -542,42 +543,101 @@ const char *Sys_ConsoleInput (void) if (recs[0].EventType == KEY_EVENT) { - if (recs[0].Event.KeyEvent.bKeyDown == FALSE) - { - ch = recs[0].Event.KeyEvent.uChar.AsciiChar; - - switch (ch) + if (recs[0].Event.KeyEvent.bKeyDown == FALSE) { - case '\r': - WriteFile(houtput, "\r\n", 2, &dummy, NULL); - - if (textlen != 0) + if (recs[0].Event.KeyEvent.wVirtualKeyCode == VK_LEFT) { - con_text[textlen] = 0; - textlen = 0; - return con_text; + if (cursor_pos > 0) + { + cursor_pos--; + WriteFile(houtput, "\b", 1, &dummy, NULL); + } + continue; + } + else if (recs[0].Event.KeyEvent.wVirtualKeyCode == VK_RIGHT) + { + if (cursor_pos < textlen) + { + WriteFile(houtput, &con_text[cursor_pos], 1, &dummy, NULL); + cursor_pos++; + } + continue; } - break; - - case '\b': - WriteFile(houtput, "\b \b", 3, &dummy, NULL); - if (textlen != 0) - textlen--; - - break; + ch = recs[0].Event.KeyEvent.uChar.AsciiChar; - default: - if (ch >= ' ') + switch (ch) { - WriteFile(houtput, &ch, 1, &dummy, NULL); - con_text[textlen] = ch; - textlen = (textlen + 1) & 0xff; + case '\r': + WriteFile(houtput, "\r\n", 2, &dummy, NULL); + + if (textlen != 0) + { + con_text[textlen] = 0; + textlen = 0; + cursor_pos = 0; // woods #arrowkeys + return con_text; + } + + break; + + case '\b': + if (cursor_pos > 0) + { + // Move characters after cursor back by one position + memmove(&con_text[cursor_pos - 1], &con_text[cursor_pos], textlen - cursor_pos); + cursor_pos--; + textlen--; + + // Rewrite the line from cursor position + WriteFile(houtput, "\b", 1, &dummy, NULL); + if (cursor_pos < textlen) + { + WriteFile(houtput, &con_text[cursor_pos], textlen - cursor_pos, &dummy, NULL); + WriteFile(houtput, " ", 1, &dummy, NULL); // Clear last character + // Move cursor back to position + for (int i = 0; i < textlen - cursor_pos + 1; i++) + WriteFile(houtput, "\b", 1, &dummy, NULL); + } + else + { + WriteFile(houtput, " \b", 2, &dummy, NULL); // Clear last character + } + } + break; + + default: + if (ch >= ' ') + { + // Insert character at cursor position + if (cursor_pos < textlen) + { + // Make room for new character + memmove(&con_text[cursor_pos + 1], &con_text[cursor_pos], textlen - cursor_pos); + con_text[cursor_pos] = ch; + textlen++; + + // Write the new character and the rest of the line + WriteFile(houtput, &con_text[cursor_pos], textlen - cursor_pos, &dummy, NULL); + + // Move cursor back to just after inserted character + cursor_pos++; + for (int i = 0; i < textlen - cursor_pos; i++) + WriteFile(houtput, "\b", 1, &dummy, NULL); + } + else + { + // Append character at end of line + con_text[textlen] = ch; + WriteFile(houtput, &ch, 1, &dummy, NULL); + textlen++; + cursor_pos++; + } + } + + break; } - - break; } - } } }