From 50dcc58a776b4233f7b2c0622cad3b6ff0a0be0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arsen=20Arsenovi=C4=87?= Date: Wed, 25 Aug 2021 22:28:32 +0200 Subject: [PATCH] readline: re-add emacs key support This time, we properly check shift flags from both the firmware options, as to not lead to conflicts with existing code. The logic for checking whether Ctrl is being held is also a lot cleaner now. --- stage23/lib/readline.c | 91 +++++++++++++++++++++++++++++++++++++----- stage23/lib/readline.h | 8 ++++ 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/stage23/lib/readline.c b/stage23/lib/readline.c index d9e19226..f7d89ece 100644 --- a/stage23/lib/readline.c +++ b/stage23/lib/readline.c @@ -4,13 +4,14 @@ #include #include #include +#include #if bios == 1 # include #elif uefi == 1 # include #endif -int getchar_internal(uint8_t scancode, uint8_t ascii) { +int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) { switch (scancode) { #if bios == 1 case 0x44: @@ -66,6 +67,17 @@ int getchar_internal(uint8_t scancode, uint8_t ascii) { case '\b': return '\b'; } + + if (shift_state & (GETCHAR_LCTRL | GETCHAR_RCTRL)) { + switch (ascii) { + case 'p': return GETCHAR_CURSOR_UP; + case 'n': return GETCHAR_CURSOR_DOWN; + case 'b': return GETCHAR_CURSOR_LEFT; + case 'f': return GETCHAR_CURSOR_RIGHT; + default: break; + } + } + // Guard against non-printable values if (ascii < 0x20 || ascii > 0x7e) { return -1; @@ -75,10 +87,26 @@ int getchar_internal(uint8_t scancode, uint8_t ascii) { #if bios == 1 int getchar(void) { + uint8_t scancode = 0; + uint8_t ascii = 0; + uint32_t mods = 0; again:; struct rm_regs r = {0}; rm_int(0x16, &r, &r); - int ret = getchar_internal((r.eax >> 8) & 0xff, r.eax); + scancode = (r.eax >> 8) & 0xff; + ascii = r.eax & 0xff; + + r = (struct rm_regs){ 0 }; + r.eax = 0x0200; // GET SHIFT FLAGS + rm_int(0x16, &r, &r); + + if (r.eax & GETCHAR_LCTRL) { + /* the bios subtracts 0x60 from ascii if ctrl is pressed */ + mods = GETCHAR_LCTRL; + ascii += 0x60; + } + + int ret = getchar_internal(scancode, ascii, mods); if (ret == -1) goto again; return ret; @@ -92,26 +120,67 @@ int pit_sleep_and_quit_on_keypress(int seconds) { #endif #if uefi == 1 +static EFI_KEY_DATA _read_efi_key(EFI_HANDLE device) { + EFI_KEY_DATA out = { + /* default to no modifiers */ + .KeyState = { 0 }, + }; + + EFI_GUID exproto_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; + EFI_GUID sproto_guid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto = NULL; + EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto = NULL; + + if (gBS->HandleProtocol(device, &exproto_guid, (void **)&exproto) != EFI_SUCCESS) { + if (gBS->HandleProtocol(device, &sproto_guid, (void **)&sproto) + != EFI_SUCCESS) { + panic("Your input device doesn't have an input protocol!"); + } + } + + EFI_STATUS status = EFI_UNSUPPORTED; + if (exproto) { + status = exproto->ReadKeyStrokeEx(exproto, &out); + } else { + status = sproto->ReadKeyStroke(sproto, &out.Key); + } + + /* there is definitely a key pending here, if there isn't, there's + * something very wrong, as the caller should be waiting on a key. + */ + if (status != EFI_SUCCESS) { + panic("Failed to read from the keyboard"); + } + + if (!(out.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID)) { + out.KeyState.KeyShiftState = 0; + } + + return out; +} + int getchar(void) { again:; - EFI_INPUT_KEY key = {0}; + EFI_KEY_DATA kd; UINTN which; gBS->WaitForEvent(1, (EFI_EVENT[]){ gST->ConIn->WaitForKey }, &which); - gST->ConIn->ReadKeyStroke(gST->ConIn, &key); + kd = _read_efi_key(gST->ConsoleInHandle); - int ret = getchar_internal(key.ScanCode, key.UnicodeChar); + int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar, + kd.KeyState.KeyShiftState); - if (ret == -1) + if (ret == -1) { goto again; + } return ret; } int pit_sleep_and_quit_on_keypress(int seconds) { - EFI_INPUT_KEY key = {0}; + EFI_KEY_DATA kd; UINTN which; @@ -130,12 +199,14 @@ again: return 0; } - gST->ConIn->ReadKeyStroke(gST->ConIn, &key); + kd = _read_efi_key(gST->ConsoleInHandle); - int ret = getchar_internal(key.ScanCode, key.UnicodeChar); + int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar, + kd.KeyState.KeyShiftState); - if (ret == -1) + if (ret == -1) { goto again; + } return ret; } diff --git a/stage23/lib/readline.h b/stage23/lib/readline.h index 8ce83c07..fea81766 100644 --- a/stage23/lib/readline.h +++ b/stage23/lib/readline.h @@ -15,6 +15,14 @@ #define GETCHAR_F10 (-19) #define GETCHAR_ESCAPE (-20) +#if bios == 1 +# define GETCHAR_RCTRL 0x4 +# define GETCHAR_LCTRL GETCHAR_RCTRL +#elif uefi == 1 +# define GETCHAR_RCTRL EFI_RIGHT_CONTROL_PRESSED +# define GETCHAR_LCTRL EFI_LEFT_CONTROL_PRESSED +#endif + int getchar(void); void readline(const char *orig_str, char *buf, size_t limit);