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.
This commit is contained in:
Arsen Arsenović 2021-08-25 22:28:32 +02:00
parent 31f52c2b30
commit 50dcc58a77
No known key found for this signature in database
GPG Key ID: 4A3FCA65C153F43E
2 changed files with 89 additions and 10 deletions

View File

@ -4,13 +4,14 @@
#include <lib/libc.h>
#include <lib/blib.h>
#include <lib/term.h>
#include <lib/print.h>
#if bios == 1
# include <lib/real.h>
#elif uefi == 1
# include <efi.h>
#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;
}

View File

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