rulimine/stage23/lib/readline.c
Arsen Arsenović 2fcabe8dfe
readline: exclude the non-ctrl case from checks
Honestly, all of this is hacky, somehow people implementing these APIs
never bothered to put modifiers in some other register to be returned.
The EFI people got a whole struct dedicated to returning what key is
pressed, and didn't even put the modifiers in it.

EFI actually did implement this with EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL,
the returned struct holds the modifier. I can implement this using that
protocol and int 16/AH=01h at a later date.
2021-07-08 17:43:58 +02:00

270 lines
6.7 KiB
C

#include <stdint.h>
#include <stddef.h>
#include <lib/readline.h>
#include <lib/libc.h>
#include <lib/blib.h>
#include <lib/term.h>
#if defined (bios)
# include <lib/real.h>
#elif defined (uefi)
# include <efi.h>
#endif
int getchar_internal(uint8_t scancode, uint8_t ascii) {
switch (scancode) {
#if defined (bios)
case 0x44:
return GETCHAR_F10;
case 0x4b:
return GETCHAR_CURSOR_LEFT;
case 0x4d:
return GETCHAR_CURSOR_RIGHT;
case 0x48:
return GETCHAR_CURSOR_UP;
case 0x50:
return GETCHAR_CURSOR_DOWN;
case 0x53:
return GETCHAR_DELETE;
case 0x4f:
return GETCHAR_END;
case 0x47:
return GETCHAR_HOME;
case 0x49:
return GETCHAR_PGUP;
case 0x51:
return GETCHAR_PGDOWN;
case 0x01:
return GETCHAR_ESCAPE;
// special case checks for C-[pnfb]
#define CTRL_CHECK(ch, key) \
if ((ascii | 0x60) == ch && ascii != ch) { \
return key; \
} else { \
break; \
}
case 0x19:
CTRL_CHECK('p', GETCHAR_CURSOR_UP);
case 0x31:
CTRL_CHECK('n', GETCHAR_CURSOR_DOWN);
case 0x21:
CTRL_CHECK('f', GETCHAR_CURSOR_RIGHT);
case 0x30:
CTRL_CHECK('b', GETCHAR_CURSOR_LEFT);
#elif defined (uefi)
case SCAN_F10:
return GETCHAR_F10;
case SCAN_LEFT:
return GETCHAR_CURSOR_LEFT;
case SCAN_RIGHT:
return GETCHAR_CURSOR_RIGHT;
case SCAN_UP:
return GETCHAR_CURSOR_UP;
case SCAN_DOWN:
return GETCHAR_CURSOR_DOWN;
case SCAN_DELETE:
return GETCHAR_DELETE;
case SCAN_END:
return GETCHAR_END;
case SCAN_HOME:
return GETCHAR_HOME;
case SCAN_PAGE_UP:
return GETCHAR_PGUP;
case SCAN_PAGE_DOWN:
return GETCHAR_PGDOWN;
case SCAN_ESC:
return GETCHAR_ESCAPE;
// special case checks for C-[pnfb]
#define CTRL_CHECK(ch, key) \
if ((ascii | 0x60) == ch && ascii != ch) { \
return key; \
}
case 0:
CTRL_CHECK('p', GETCHAR_CURSOR_UP);
CTRL_CHECK('n', GETCHAR_CURSOR_DOWN);
CTRL_CHECK('f', GETCHAR_CURSOR_RIGHT);
CTRL_CHECK('b', GETCHAR_CURSOR_LEFT);
break;
#endif
}
#undef CTRL_CHECK
switch (ascii) {
case '\r':
return '\n';
case '\b':
return '\b';
}
// Guard against non-printable values
if (ascii < 0x20 || ascii > 0x7e) {
return -1;
}
return ascii;
}
#if defined (bios)
int getchar(void) {
again:;
struct rm_regs r = {0};
rm_int(0x16, &r, &r);
int ret = getchar_internal((r.eax >> 8) & 0xff, r.eax);
if (ret == -1)
goto again;
return ret;
}
int _pit_sleep_and_quit_on_keypress(uint32_t ticks);
int pit_sleep_and_quit_on_keypress(int seconds) {
return _pit_sleep_and_quit_on_keypress(seconds * 18);
}
#endif
#if defined (uefi)
int getchar(void) {
again:;
EFI_INPUT_KEY key = {0};
UINTN which;
uefi_call_wrapper(
gBS->WaitForEvent, 3, 1, (EFI_EVENT[]){ gST->ConIn->WaitForKey }, &which);
uefi_call_wrapper(gST->ConIn->ReadKeyStroke, 2, gST->ConIn, &key);
int ret = getchar_internal(key.ScanCode, key.UnicodeChar);
if (ret == -1)
goto again;
return ret;
}
int pit_sleep_and_quit_on_keypress(int seconds) {
EFI_INPUT_KEY key = {0};
UINTN which;
EFI_EVENT events[2];
events[0] = gST->ConIn->WaitForKey;
uefi_call_wrapper(
gBS->CreateEvent, 5, EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
uefi_call_wrapper(
gBS->SetTimer, 3, events[1], TimerRelative, 10000000 * seconds);
again:
uefi_call_wrapper(gBS->WaitForEvent, 3, 2, events, &which);
if (which == 1) {
return 0;
}
uefi_call_wrapper(gST->ConIn->ReadKeyStroke, 2, gST->ConIn, &key);
int ret = getchar_internal(key.ScanCode, key.UnicodeChar);
if (ret == -1)
goto again;
return ret;
}
#endif
static void reprint_string(int x, int y, const char *s) {
int orig_x, orig_y;
disable_cursor();
get_cursor_pos(&orig_x, &orig_y);
set_cursor_pos(x, y);
term_write(s, strlen(s));
set_cursor_pos(orig_x, orig_y);
enable_cursor();
}
static void cursor_back(void) {
int x, y;
get_cursor_pos(&x, &y);
if (x) {
x--;
} else if (y) {
y--;
x = term_cols - 1;
}
set_cursor_pos(x, y);
}
static void cursor_fwd(void) {
int x, y;
get_cursor_pos(&x, &y);
if (x < term_cols - 1) {
x++;
} else if (y < term_rows - 1) {
y++;
x = 0;
}
set_cursor_pos(x, y);
}
void readline(const char *orig_str, char *buf, size_t limit) {
size_t orig_str_len = strlen(orig_str);
memmove(buf, orig_str, orig_str_len);
buf[orig_str_len] = 0;
int orig_x, orig_y;
get_cursor_pos(&orig_x, &orig_y);
term_write(orig_str, orig_str_len);
for (size_t i = orig_str_len; ; ) {
int c = getchar();
switch (c) {
case GETCHAR_CURSOR_LEFT:
if (i) {
i--;
cursor_back();
}
continue;
case GETCHAR_CURSOR_RIGHT:
if (i < strlen(buf)) {
i++;
cursor_fwd();
}
continue;
case '\b':
if (i) {
i--;
cursor_back();
case GETCHAR_DELETE:;
size_t j;
for (j = i; ; j++) {
buf[j] = buf[j+1];
if (!buf[j]) {
buf[j] = ' ';
break;
}
}
reprint_string(orig_x, orig_y, buf);
buf[j] = 0;
}
continue;
case '\n':
term_write("\n", 1);
return;
default:
if (strlen(buf) < limit - 1) {
for (size_t j = strlen(buf); ; j--) {
buf[j+1] = buf[j];
if (j == i)
break;
}
buf[i] = c;
i++;
cursor_fwd();
reprint_string(orig_x, orig_y, buf);
}
}
}
}