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