mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 20:58:22 +00:00
285 lines
8.8 KiB
C
285 lines
8.8 KiB
C
#include <kernel/console.h>
|
|
#include <mutex.h>
|
|
#include <ringbuffer.h>
|
|
#include <logging.h>
|
|
#include <kernel/term.h>
|
|
#include <kernel/devfs.h>
|
|
#include <errno.h>
|
|
#include <termios.h>
|
|
#include <kernel/poll.h>
|
|
#include <kernel/tty.h>
|
|
#include <kernel/init.h>
|
|
#include <kernel/kernel_args.h>
|
|
|
|
DEFINE_KERNEL_ARGUMENT(nofblog, bool);
|
|
|
|
static spinlock_t input_buffer_lock = SPINLOCK_INIT_VALUE;
|
|
static ringbuffer_t input_buffer;
|
|
static semaphore_t input_sem;
|
|
|
|
static mutex_t writemutex;
|
|
static thread_t *thread;
|
|
static tty_t *tty;
|
|
static bool locked;
|
|
|
|
static size_t console_ttywrite(void *internal, char *str, size_t count) {
|
|
return console_write(str, count);
|
|
}
|
|
|
|
int console_set_lock(bool lock) {
|
|
bool expected = !lock;
|
|
if (!__atomic_compare_exchange_n(&locked, &expected, lock, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST))
|
|
return EBUSY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool console_is_tty(tty_t *p) {
|
|
return p == tty;
|
|
}
|
|
|
|
void console_putc(char c) {
|
|
if (c == '\n') {
|
|
console_write("\r\n", 2);
|
|
} else {
|
|
console_write(&c, 1);
|
|
}
|
|
}
|
|
|
|
size_t console_write(char *str, size_t count) {
|
|
MUTEX_ACQUIRE(&writemutex);
|
|
|
|
term_write(str, count);
|
|
|
|
MUTEX_RELEASE(&writemutex);
|
|
return count;
|
|
}
|
|
|
|
void console_process_events(input_event_t *events, int count) {
|
|
if (__atomic_load_n(&locked, __ATOMIC_RELAXED))
|
|
return;
|
|
|
|
long ipl = spinlock_acquire_raise_ipl(&input_buffer_lock, IPL_INPUT);
|
|
for (int i = 0; i < count; ++i) {
|
|
if (events[i].type != INPUT_EV_KEY || events[i].code >= INPUT_KEY_BTN_LEFT)
|
|
continue;
|
|
|
|
size_t written = ringbuffer_write(&input_buffer, &events[i], sizeof(input_event_t));
|
|
if (written == sizeof(input_event_t))
|
|
semaphore_signal(&input_sem);
|
|
}
|
|
spinlock_release_lower_ipl(&input_buffer_lock, ipl);
|
|
}
|
|
|
|
// taken from https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_input_sequences
|
|
|
|
#define HOME_STR "\e[1~"
|
|
#define INSERT_STR "\e[2~"
|
|
#define DELETE_STR "\e[3~"
|
|
#define END_STR "\e[4~"
|
|
#define PGUP_STR "\e[5~"
|
|
#define PGDN_STR "\e[6~"
|
|
|
|
#define UP_STR "\e[A"
|
|
#define DOWN_STR "\e[B"
|
|
#define RIGHT_STR "\e[C"
|
|
#define LEFT_STR "\e[D"
|
|
|
|
static const char key_to_ascii_normal[] = {
|
|
[INPUT_KEY_ESC] = '\e', [INPUT_KEY_1] = '1',
|
|
[INPUT_KEY_2] = '2', [INPUT_KEY_3] = '3',
|
|
[INPUT_KEY_4] = '4', [INPUT_KEY_5] = '5',
|
|
[INPUT_KEY_6] = '6', [INPUT_KEY_7] = '7',
|
|
[INPUT_KEY_8] = '8', [INPUT_KEY_9] = '9',
|
|
[INPUT_KEY_0] = '0', [INPUT_KEY_MINUS] = '-',
|
|
[INPUT_KEY_EQUAL] = '=', [INPUT_KEY_BACKSPACE] = '\b',
|
|
[INPUT_KEY_TAB] = '\t', [INPUT_KEY_Q] = 'q',
|
|
[INPUT_KEY_W] = 'w', [INPUT_KEY_E] = 'e',
|
|
[INPUT_KEY_R] = 'r', [INPUT_KEY_T] = 't',
|
|
[INPUT_KEY_Y] = 'y', [INPUT_KEY_U] = 'u',
|
|
[INPUT_KEY_I] = 'i', [INPUT_KEY_O] = 'o',
|
|
[INPUT_KEY_P] = 'p', [INPUT_KEY_LEFTBRACE] = '[',
|
|
[INPUT_KEY_RIGHTBRACE] = ']', [INPUT_KEY_ENTER] = '\r',
|
|
[INPUT_KEY_A] = 'a', [INPUT_KEY_S] = 's',
|
|
[INPUT_KEY_D] = 'd', [INPUT_KEY_F] = 'f',
|
|
[INPUT_KEY_G] = 'g', [INPUT_KEY_H] = 'h',
|
|
[INPUT_KEY_J] = 'j', [INPUT_KEY_K] = 'k',
|
|
[INPUT_KEY_L] = 'l', [INPUT_KEY_SEMICOLON] = ';',
|
|
[INPUT_KEY_APOSTROPHE] = '\'', [INPUT_KEY_GRAVE] = '`',
|
|
[INPUT_KEY_BACKSLASH] = '\\', [INPUT_KEY_Z] = 'z',
|
|
[INPUT_KEY_X] = 'x', [INPUT_KEY_C] = 'c',
|
|
[INPUT_KEY_V] = 'v', [INPUT_KEY_B] = 'b',
|
|
[INPUT_KEY_N] = 'n', [INPUT_KEY_M] = 'm',
|
|
[INPUT_KEY_COMMA] = ',', [INPUT_KEY_DOT] = '.',
|
|
[INPUT_KEY_SLASH] = '/', [INPUT_KEY_KPASTERISK] = '*',
|
|
[INPUT_KEY_SPACE] = ' ', [INPUT_KEY_KP7] = '7',
|
|
[INPUT_KEY_KP8] = '8', [INPUT_KEY_KP9] = '9',
|
|
[INPUT_KEY_KPMINUS] = '-', [INPUT_KEY_KP4] = '4',
|
|
[INPUT_KEY_KP5] = '5', [INPUT_KEY_KP6] = '6',
|
|
[INPUT_KEY_KPPLUS] = '+', [INPUT_KEY_KP1] = '1',
|
|
[INPUT_KEY_KP2] = '2', [INPUT_KEY_KP3] = '3',
|
|
[INPUT_KEY_KP0] = '0', [INPUT_KEY_KPDOT] = '.',
|
|
[INPUT_KEY_KPENTER] = '\r', [INPUT_KEY_KPSLASH] = '/',
|
|
};
|
|
|
|
static const char key_to_ascii_shift[] = {
|
|
[INPUT_KEY_ESC] = '\e', [INPUT_KEY_1] = '!',
|
|
[INPUT_KEY_2] = '@', [INPUT_KEY_3] = '#',
|
|
[INPUT_KEY_4] = '$', [INPUT_KEY_5] = '%',
|
|
[INPUT_KEY_6] = '^', [INPUT_KEY_7] = '&',
|
|
[INPUT_KEY_8] = '*', [INPUT_KEY_9] = '(',
|
|
[INPUT_KEY_0] = ')', [INPUT_KEY_MINUS] = '_',
|
|
[INPUT_KEY_EQUAL] = '+', [INPUT_KEY_BACKSPACE] = '\b',
|
|
[INPUT_KEY_TAB] = '\t', [INPUT_KEY_Q] = 'Q',
|
|
[INPUT_KEY_W] = 'W', [INPUT_KEY_E] = 'E',
|
|
[INPUT_KEY_R] = 'R', [INPUT_KEY_T] = 'T',
|
|
[INPUT_KEY_Y] = 'Y', [INPUT_KEY_U] = 'U',
|
|
[INPUT_KEY_I] = 'I', [INPUT_KEY_O] = 'O',
|
|
[INPUT_KEY_P] = 'P', [INPUT_KEY_LEFTBRACE] = '{',
|
|
[INPUT_KEY_RIGHTBRACE] = '}', [INPUT_KEY_ENTER] = '\r',
|
|
[INPUT_KEY_A] = 'A', [INPUT_KEY_S] = 'S',
|
|
[INPUT_KEY_D] = 'D', [INPUT_KEY_F] = 'F',
|
|
[INPUT_KEY_G] = 'G', [INPUT_KEY_H] = 'H',
|
|
[INPUT_KEY_J] = 'J', [INPUT_KEY_K] = 'K',
|
|
[INPUT_KEY_L] = 'L', [INPUT_KEY_SEMICOLON] = ':',
|
|
[INPUT_KEY_APOSTROPHE] = '"', [INPUT_KEY_GRAVE] = '~',
|
|
[INPUT_KEY_BACKSLASH] = '|', [INPUT_KEY_Z] = 'Z',
|
|
[INPUT_KEY_X] = 'X', [INPUT_KEY_C] = 'C',
|
|
[INPUT_KEY_V] = 'V', [INPUT_KEY_B] = 'B',
|
|
[INPUT_KEY_N] = 'N', [INPUT_KEY_M] = 'M',
|
|
[INPUT_KEY_COMMA] = '<', [INPUT_KEY_DOT] = '>',
|
|
[INPUT_KEY_SLASH] = '?', [INPUT_KEY_KPASTERISK] = '*',
|
|
[INPUT_KEY_SPACE] = ' ', [INPUT_KEY_KP7] = '7',
|
|
[INPUT_KEY_KP8] = '8', [INPUT_KEY_KP9] = '9',
|
|
[INPUT_KEY_KPMINUS] = '-', [INPUT_KEY_KP4] = '4',
|
|
[INPUT_KEY_KP5] = '5', [INPUT_KEY_KP6] = '6',
|
|
[INPUT_KEY_KPPLUS] = '+', [INPUT_KEY_KP1] = '1',
|
|
[INPUT_KEY_KP2] = '2', [INPUT_KEY_KP3] = '3',
|
|
[INPUT_KEY_KP0] = '0', [INPUT_KEY_KPDOT] = '.',
|
|
[INPUT_KEY_KPENTER] = '\r', [INPUT_KEY_KPSLASH] = '/',
|
|
};
|
|
|
|
static void consolethread() {
|
|
bool left_ctrl = false;
|
|
bool right_ctrl = false;
|
|
bool left_shift = false;
|
|
bool right_shift = false;
|
|
|
|
for (;;) {
|
|
semaphore_wait(&input_sem, false);
|
|
__assert(RINGBUFFER_DATACOUNT(&input_buffer) >= sizeof(input_event_t));
|
|
|
|
input_event_t ev;
|
|
__assert(ringbuffer_read(&input_buffer, &ev, sizeof(input_event_t)) == sizeof(input_event_t));
|
|
|
|
if (ev.code == INPUT_KEY_LEFTCTRL) {
|
|
left_ctrl = ev.value != 0;
|
|
continue;
|
|
} else if (ev.code == INPUT_KEY_RIGHTCTRL) {
|
|
right_ctrl = ev.value != 0;
|
|
continue;
|
|
} else if (ev.code == INPUT_KEY_LEFTSHIFT) {
|
|
left_shift = ev.value != 0;
|
|
continue;
|
|
} else if (ev.code == INPUT_KEY_RIGHTSHIFT) {
|
|
right_shift = ev.value != 0;
|
|
continue;
|
|
}
|
|
|
|
bool ctrl = left_ctrl || right_ctrl;
|
|
bool shift = left_shift || right_shift;
|
|
|
|
// we only care about key presses, not releases
|
|
if (ev.value == 0)
|
|
continue;
|
|
|
|
char ascii = 0;
|
|
if (shift) {
|
|
if (ev.code < sizeof(key_to_ascii_shift))
|
|
ascii = key_to_ascii_shift[ev.code];
|
|
} else {
|
|
if (ev.code < sizeof(key_to_ascii_normal))
|
|
ascii = key_to_ascii_normal[ev.code];
|
|
}
|
|
|
|
if (ascii != 0) {
|
|
if (ctrl) {
|
|
// control characters
|
|
if (!((ascii >= 'a' && ascii <= 'z') || (ascii >= 'A' && ascii <= '\\')))
|
|
continue;
|
|
|
|
ascii = ascii >= 'a' ? ascii - 0x60 : ascii - 0x40;
|
|
}
|
|
|
|
tty_process(tty, ascii);
|
|
} else {
|
|
// no ascii for key but we can send in a escape sequence for it
|
|
char *target = NULL;
|
|
switch (ev.code) {
|
|
case INPUT_KEY_HOME:
|
|
target = HOME_STR;
|
|
break;
|
|
case INPUT_KEY_INSERT:
|
|
target = INSERT_STR;
|
|
break;
|
|
case INPUT_KEY_DELETE:
|
|
target = DELETE_STR;
|
|
break;
|
|
case INPUT_KEY_END:
|
|
target = END_STR;
|
|
break;
|
|
case INPUT_KEY_PAGEUP:
|
|
target = PGUP_STR;
|
|
break;
|
|
case INPUT_KEY_PAGEDOWN:
|
|
target = PGDN_STR;
|
|
break;
|
|
case INPUT_KEY_UP:
|
|
target = UP_STR;
|
|
break;
|
|
case INPUT_KEY_DOWN:
|
|
target = DOWN_STR;
|
|
break;
|
|
case INPUT_KEY_RIGHT:
|
|
target = RIGHT_STR;
|
|
break;
|
|
case INPUT_KEY_LEFT:
|
|
target = LEFT_STR;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
size_t len = strlen(target);
|
|
for (int i = 0; i < len; ++i)
|
|
tty_process(tty, target[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void console_init() {
|
|
SEMAPHORE_INIT(&input_sem, 0);
|
|
ringbuffer_init(&input_buffer, 128 * sizeof(input_event_t));
|
|
|
|
MUTEX_INIT(&writemutex);
|
|
|
|
thread = sched_newthread(consolethread, PAGE_SIZE * 16, 0, NULL, NULL);
|
|
__assert(thread);
|
|
sched_queue(thread);
|
|
|
|
tty = tty_create("console", console_ttywrite, NULL, NULL, NULL, NULL);
|
|
__assert(tty);
|
|
|
|
size_t x, y, fbx, fby;
|
|
term_getsize(&x, &y, &fbx, &fby);
|
|
tty->winsize.ws_col = x;
|
|
tty->winsize.ws_row = y;
|
|
tty->winsize.ws_xpixel = fbx;
|
|
tty->winsize.ws_ypixel = fby;
|
|
|
|
if (!GET_KERNEL_ARGUMENT(nofblog, bool))
|
|
logging_sethook(console_putc);
|
|
}
|
|
|
|
INIT_ROUTINE_DEFINE(console, INIT_ROUTINE_FLAGS_NONE, console_init, scheduler);
|