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);