From 32637ee60b72ffec11eacd7241b0720feabd889c Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Sat, 11 Apr 2026 17:13:58 +0800 Subject: [PATCH] Stage 19 --- clks/include/clks/keyboard.h | 13 +- clks/kernel/keyboard.c | 87 ++++++++-- clks/kernel/shell.c | 321 +++++++++++++++++++++++++++++++++-- 3 files changed, 393 insertions(+), 28 deletions(-) diff --git a/clks/include/clks/keyboard.h b/clks/include/clks/keyboard.h index 9a9e19b..b4f9cbe 100644 --- a/clks/include/clks/keyboard.h +++ b/clks/include/clks/keyboard.h @@ -3,10 +3,21 @@ #include +#define CLKS_KEY_LEFT ((char)0x01) +#define CLKS_KEY_RIGHT ((char)0x02) +#define CLKS_KEY_UP ((char)0x03) +#define CLKS_KEY_DOWN ((char)0x04) +#define CLKS_KEY_HOME ((char)0x05) +#define CLKS_KEY_END ((char)0x06) +#define CLKS_KEY_DELETE ((char)0x07) + void clks_keyboard_init(void); void clks_keyboard_handle_scancode(u8 scancode); u64 clks_keyboard_hotkey_switch_count(void); clks_bool clks_keyboard_pop_char(char *out_ch); u64 clks_keyboard_buffered_count(void); +u64 clks_keyboard_drop_count(void); +u64 clks_keyboard_push_count(void); +u64 clks_keyboard_pop_count(void); -#endif \ No newline at end of file +#endif diff --git a/clks/kernel/keyboard.c b/clks/kernel/keyboard.c index 132c255..19f8cb6 100644 --- a/clks/kernel/keyboard.c +++ b/clks/kernel/keyboard.c @@ -4,16 +4,25 @@ #include #include -#define CLKS_SC_ALT 0x38U -#define CLKS_SC_LSHIFT 0x2AU -#define CLKS_SC_RSHIFT 0x36U -#define CLKS_SC_F1 0x3BU -#define CLKS_SC_F2 0x3CU -#define CLKS_SC_F3 0x3DU -#define CLKS_SC_F4 0x3EU -#define CLKS_SC_EXT_PREFIX 0xE0U +#define CLKS_SC_ALT 0x38U +#define CLKS_SC_LSHIFT 0x2AU +#define CLKS_SC_RSHIFT 0x36U +#define CLKS_SC_F1 0x3BU +#define CLKS_SC_F2 0x3CU +#define CLKS_SC_F3 0x3DU +#define CLKS_SC_F4 0x3EU +#define CLKS_SC_EXT_PREFIX 0xE0U -#define CLKS_KBD_INPUT_CAP 256U +#define CLKS_SC_EXT_HOME 0x47U +#define CLKS_SC_EXT_UP 0x48U +#define CLKS_SC_EXT_LEFT 0x4BU +#define CLKS_SC_EXT_RIGHT 0x4DU +#define CLKS_SC_EXT_END 0x4FU +#define CLKS_SC_EXT_DOWN 0x50U +#define CLKS_SC_EXT_DELETE 0x53U + +#define CLKS_KBD_INPUT_CAP 256U +#define CLKS_KBD_DROP_LOG_EVERY 64ULL static const char clks_kbd_map[128] = { [2] = '1', [3] = '2', [4] = '3', [5] = '4', [6] = '5', [7] = '6', [8] = '7', [9] = '8', @@ -48,14 +57,47 @@ static clks_bool clks_kbd_rshift_down = CLKS_FALSE; static clks_bool clks_kbd_e0_prefix = CLKS_FALSE; static u64 clks_kbd_hotkey_switches = 0ULL; +static u64 clks_kbd_push_count = 0ULL; +static u64 clks_kbd_pop_count = 0ULL; +static u64 clks_kbd_drop_count = 0ULL; + +static char clks_keyboard_translate_ext_scancode(u8 code) { + switch (code) { + case CLKS_SC_EXT_LEFT: + return CLKS_KEY_LEFT; + case CLKS_SC_EXT_RIGHT: + return CLKS_KEY_RIGHT; + case CLKS_SC_EXT_UP: + return CLKS_KEY_UP; + case CLKS_SC_EXT_DOWN: + return CLKS_KEY_DOWN; + case CLKS_SC_EXT_HOME: + return CLKS_KEY_HOME; + case CLKS_SC_EXT_END: + return CLKS_KEY_END; + case CLKS_SC_EXT_DELETE: + return CLKS_KEY_DELETE; + default: + return '\0'; + } +} + static clks_bool clks_keyboard_queue_push(char ch) { if (clks_kbd_input_count >= CLKS_KBD_INPUT_CAP) { + clks_kbd_drop_count++; + + if ((clks_kbd_drop_count % CLKS_KBD_DROP_LOG_EVERY) == 1ULL) { + clks_log(CLKS_LOG_WARN, "KBD", "INPUT QUEUE OVERFLOW"); + clks_log_hex(CLKS_LOG_WARN, "KBD", "DROPPED", clks_kbd_drop_count); + } + return CLKS_FALSE; } clks_kbd_input_queue[clks_kbd_input_head] = ch; clks_kbd_input_head = (u16)((clks_kbd_input_head + 1U) % CLKS_KBD_INPUT_CAP); clks_kbd_input_count++; + clks_kbd_push_count++; return CLKS_TRUE; } @@ -80,9 +122,13 @@ void clks_keyboard_init(void) { clks_kbd_rshift_down = CLKS_FALSE; clks_kbd_e0_prefix = CLKS_FALSE; clks_kbd_hotkey_switches = 0ULL; + clks_kbd_push_count = 0ULL; + clks_kbd_pop_count = 0ULL; + clks_kbd_drop_count = 0ULL; clks_log(CLKS_LOG_INFO, "KBD", "ALT+F1..F4 TTY HOTKEY ONLINE"); clks_log(CLKS_LOG_INFO, "KBD", "PS2 INPUT QUEUE ONLINE"); + clks_log_hex(CLKS_LOG_INFO, "KBD", "QUEUE_CAP", CLKS_KBD_INPUT_CAP); } void clks_keyboard_handle_scancode(u8 scancode) { @@ -120,7 +166,15 @@ void clks_keyboard_handle_scancode(u8 scancode) { } if (clks_kbd_e0_prefix == CLKS_TRUE) { + char ext = clks_keyboard_translate_ext_scancode(code); clks_kbd_e0_prefix = CLKS_FALSE; + + if (ext != '\0') { + if (clks_keyboard_queue_push(ext) == CLKS_TRUE) { + clks_shell_pump_input(1U); + } + } + return; } @@ -165,9 +219,22 @@ clks_bool clks_keyboard_pop_char(char *out_ch) { *out_ch = clks_kbd_input_queue[clks_kbd_input_tail]; clks_kbd_input_tail = (u16)((clks_kbd_input_tail + 1U) % CLKS_KBD_INPUT_CAP); clks_kbd_input_count--; + clks_kbd_pop_count++; return CLKS_TRUE; } u64 clks_keyboard_buffered_count(void) { return (u64)clks_kbd_input_count; -} \ No newline at end of file +} + +u64 clks_keyboard_drop_count(void) { + return clks_kbd_drop_count; +} + +u64 clks_keyboard_push_count(void) { + return clks_kbd_push_count; +} + +u64 clks_keyboard_pop_count(void) { + return clks_kbd_pop_count; +} diff --git a/clks/kernel/shell.c b/clks/kernel/shell.c index 3098a2c..b17d692 100644 --- a/clks/kernel/shell.c +++ b/clks/kernel/shell.c @@ -7,18 +7,29 @@ #include #include -#define CLKS_SHELL_LINE_MAX 192U -#define CLKS_SHELL_CMD_MAX 32U -#define CLKS_SHELL_ARG_MAX 160U -#define CLKS_SHELL_NAME_MAX 96U -#define CLKS_SHELL_PATH_MAX 192U -#define CLKS_SHELL_CAT_LIMIT 512U -#define CLKS_SHELL_INPUT_BUDGET 128U -#define CLKS_SHELL_CLEAR_LINES 56U +#define CLKS_SHELL_LINE_MAX 192U +#define CLKS_SHELL_CMD_MAX 32U +#define CLKS_SHELL_ARG_MAX 160U +#define CLKS_SHELL_NAME_MAX 96U +#define CLKS_SHELL_PATH_MAX 192U +#define CLKS_SHELL_CAT_LIMIT 512U +#define CLKS_SHELL_INPUT_BUDGET 128U +#define CLKS_SHELL_CLEAR_LINES 56U +#define CLKS_SHELL_HISTORY_MAX 16U +#define CLKS_SHELL_PROMPT_TEXT "cleonos> " static clks_bool clks_shell_ready = CLKS_FALSE; static char clks_shell_line[CLKS_SHELL_LINE_MAX]; static usize clks_shell_line_len = 0U; +static usize clks_shell_cursor = 0U; +static usize clks_shell_rendered_len = 0U; + +static char clks_shell_history[CLKS_SHELL_HISTORY_MAX][CLKS_SHELL_LINE_MAX]; +static u32 clks_shell_history_count = 0U; +static i32 clks_shell_history_nav = -1; +static char clks_shell_nav_saved_line[CLKS_SHELL_LINE_MAX]; +static usize clks_shell_nav_saved_len = 0U; +static usize clks_shell_nav_saved_cursor = 0U; static clks_bool clks_shell_is_space(char ch) { return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') ? CLKS_TRUE : CLKS_FALSE; @@ -50,14 +61,185 @@ static void clks_shell_writeln(const char *text) { } static void clks_shell_prompt(void) { - clks_shell_write("cleonos> "); + clks_shell_write(CLKS_SHELL_PROMPT_TEXT); +} + +static void clks_shell_copy_line(char *dst, usize dst_size, const char *src) { + usize i = 0U; + + if (dst == CLKS_NULL || src == CLKS_NULL || dst_size == 0U) { + return; + } + + while (i + 1U < dst_size && src[i] != '\0') { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static void clks_shell_history_cancel_nav(void) { + clks_shell_history_nav = -1; + clks_shell_nav_saved_len = 0U; + clks_shell_nav_saved_cursor = 0U; + clks_shell_nav_saved_line[0] = '\0'; } static void clks_shell_reset_line(void) { clks_shell_line_len = 0U; + clks_shell_cursor = 0U; + clks_shell_rendered_len = 0U; clks_shell_line[0] = '\0'; } +static void clks_shell_load_line(const char *line) { + if (line == CLKS_NULL) { + clks_shell_reset_line(); + return; + } + + clks_shell_copy_line(clks_shell_line, sizeof(clks_shell_line), line); + clks_shell_line_len = clks_strlen(clks_shell_line); + clks_shell_cursor = clks_shell_line_len; +} + +static void clks_shell_render_line(void) { + usize i; + + if (clks_shell_ready == CLKS_FALSE) { + return; + } + + clks_shell_write_char('\r'); + clks_shell_prompt(); + + for (i = 0U; i < clks_shell_line_len; i++) { + clks_shell_write_char(clks_shell_line[i]); + } + + for (i = clks_shell_line_len; i < clks_shell_rendered_len; i++) { + clks_shell_write_char(' '); + } + + clks_shell_write_char('\r'); + clks_shell_prompt(); + + for (i = 0U; i < clks_shell_cursor; i++) { + clks_shell_write_char(clks_shell_line[i]); + } + + clks_shell_rendered_len = clks_shell_line_len; +} + +static clks_bool clks_shell_line_has_non_space(const char *line) { + usize i = 0U; + + if (line == CLKS_NULL) { + return CLKS_FALSE; + } + + while (line[i] != '\0') { + if (clks_shell_is_space(line[i]) == CLKS_FALSE) { + return CLKS_TRUE; + } + + i++; + } + + return CLKS_FALSE; +} + +static void clks_shell_history_push(const char *line) { + if (clks_shell_line_has_non_space(line) == CLKS_FALSE) { + clks_shell_history_cancel_nav(); + return; + } + + if (clks_shell_history_count > 0U && + clks_strcmp(clks_shell_history[clks_shell_history_count - 1U], line) == 0) { + clks_shell_history_cancel_nav(); + return; + } + + if (clks_shell_history_count < CLKS_SHELL_HISTORY_MAX) { + clks_shell_copy_line( + clks_shell_history[clks_shell_history_count], + sizeof(clks_shell_history[clks_shell_history_count]), + line + ); + clks_shell_history_count++; + } else { + u32 i; + + for (i = 1U; i < CLKS_SHELL_HISTORY_MAX; i++) { + clks_memcpy( + clks_shell_history[i - 1U], + clks_shell_history[i], + CLKS_SHELL_LINE_MAX + ); + } + + clks_shell_copy_line( + clks_shell_history[CLKS_SHELL_HISTORY_MAX - 1U], + sizeof(clks_shell_history[CLKS_SHELL_HISTORY_MAX - 1U]), + line + ); + } + + clks_shell_history_cancel_nav(); +} + +static void clks_shell_history_apply_current(void) { + if (clks_shell_history_nav >= 0) { + clks_shell_load_line(clks_shell_history[(u32)clks_shell_history_nav]); + } else { + clks_shell_copy_line(clks_shell_line, sizeof(clks_shell_line), clks_shell_nav_saved_line); + clks_shell_line_len = clks_shell_nav_saved_len; + if (clks_shell_line_len > CLKS_SHELL_LINE_MAX - 1U) { + clks_shell_line_len = CLKS_SHELL_LINE_MAX - 1U; + clks_shell_line[clks_shell_line_len] = '\0'; + } + clks_shell_cursor = clks_shell_nav_saved_cursor; + if (clks_shell_cursor > clks_shell_line_len) { + clks_shell_cursor = clks_shell_line_len; + } + } + + clks_shell_render_line(); +} + +static void clks_shell_history_up(void) { + if (clks_shell_history_count == 0U) { + return; + } + + if (clks_shell_history_nav < 0) { + clks_shell_copy_line(clks_shell_nav_saved_line, sizeof(clks_shell_nav_saved_line), clks_shell_line); + clks_shell_nav_saved_len = clks_shell_line_len; + clks_shell_nav_saved_cursor = clks_shell_cursor; + clks_shell_history_nav = (i32)clks_shell_history_count - 1; + } else if (clks_shell_history_nav > 0) { + clks_shell_history_nav--; + } + + clks_shell_history_apply_current(); +} + +static void clks_shell_history_down(void) { + if (clks_shell_history_nav < 0) { + return; + } + + if ((u32)clks_shell_history_nav + 1U < clks_shell_history_count) { + clks_shell_history_nav++; + } else { + clks_shell_history_nav = -1; + } + + clks_shell_history_apply_current(); +} + static void clks_shell_trim(char *line) { usize start = 0U; usize i = 0U; @@ -215,6 +397,8 @@ static void clks_shell_cmd_help(void) { clks_shell_writeln(" cat "); clks_shell_writeln(" exec "); clks_shell_writeln(" clear"); + clks_shell_writeln(" kbdstat"); + clks_shell_writeln("edit keys: Left/Right, Home/End, Up/Down history"); } static void clks_shell_cmd_ls(const char *arg) { @@ -309,6 +493,14 @@ static void clks_shell_cmd_clear(void) { } } +static void clks_shell_cmd_kbdstat(void) { + clks_shell_writeln("kbd stats emitted to kernel log"); + clks_log_hex(CLKS_LOG_INFO, "KBD", "BUFFERED", clks_keyboard_buffered_count()); + clks_log_hex(CLKS_LOG_INFO, "KBD", "PUSHED", clks_keyboard_push_count()); + clks_log_hex(CLKS_LOG_INFO, "KBD", "POPPED", clks_keyboard_pop_count()); + clks_log_hex(CLKS_LOG_INFO, "KBD", "DROPPED", clks_keyboard_drop_count()); +} + static void clks_shell_execute_line(const char *line) { char line_buf[CLKS_SHELL_LINE_MAX]; char cmd[CLKS_SHELL_CMD_MAX]; @@ -358,6 +550,11 @@ static void clks_shell_execute_line(const char *line) { return; } + if (clks_shell_streq(cmd, "kbdstat") == CLKS_TRUE) { + clks_shell_cmd_kbdstat(); + return; + } + clks_shell_writeln("unknown command; type 'help'"); } @@ -369,17 +566,85 @@ static void clks_shell_handle_char(char ch) { if (ch == '\n') { clks_shell_write_char('\n'); clks_shell_line[clks_shell_line_len] = '\0'; + clks_shell_history_push(clks_shell_line); clks_shell_execute_line(clks_shell_line); clks_shell_reset_line(); + clks_shell_history_cancel_nav(); clks_shell_prompt(); return; } - if (ch == '\b' || ch == 127) { - if (clks_shell_line_len > 0U) { + if (ch == CLKS_KEY_UP) { + clks_shell_history_up(); + return; + } + + if (ch == CLKS_KEY_DOWN) { + clks_shell_history_down(); + return; + } + + if (ch == CLKS_KEY_LEFT) { + if (clks_shell_cursor > 0U) { + clks_shell_cursor--; + clks_shell_render_line(); + } + return; + } + + if (ch == CLKS_KEY_RIGHT) { + if (clks_shell_cursor < clks_shell_line_len) { + clks_shell_cursor++; + clks_shell_render_line(); + } + return; + } + + if (ch == CLKS_KEY_HOME) { + if (clks_shell_cursor != 0U) { + clks_shell_cursor = 0U; + clks_shell_render_line(); + } + return; + } + + if (ch == CLKS_KEY_END) { + if (clks_shell_cursor != clks_shell_line_len) { + clks_shell_cursor = clks_shell_line_len; + clks_shell_render_line(); + } + return; + } + + if (ch == '\b') { + if (clks_shell_cursor > 0U && clks_shell_line_len > 0U) { + usize i; + + clks_shell_history_cancel_nav(); + + for (i = clks_shell_cursor - 1U; i < clks_shell_line_len; i++) { + clks_shell_line[i] = clks_shell_line[i + 1U]; + } + clks_shell_line_len--; - clks_shell_line[clks_shell_line_len] = '\0'; - clks_shell_write_char('\b'); + clks_shell_cursor--; + clks_shell_render_line(); + } + return; + } + + if (ch == CLKS_KEY_DELETE) { + if (clks_shell_cursor < clks_shell_line_len) { + usize i; + + clks_shell_history_cancel_nav(); + + for (i = clks_shell_cursor; i < clks_shell_line_len; i++) { + clks_shell_line[i] = clks_shell_line[i + 1U]; + } + + clks_shell_line_len--; + clks_shell_render_line(); } return; } @@ -396,13 +661,35 @@ static void clks_shell_handle_char(char ch) { return; } - clks_shell_line[clks_shell_line_len++] = ch; - clks_shell_line[clks_shell_line_len] = '\0'; - clks_shell_write_char(ch); -} + clks_shell_history_cancel_nav(); + if (clks_shell_cursor == clks_shell_line_len) { + clks_shell_line[clks_shell_line_len++] = ch; + clks_shell_line[clks_shell_line_len] = '\0'; + clks_shell_cursor = clks_shell_line_len; + clks_shell_write_char(ch); + clks_shell_rendered_len = clks_shell_line_len; + return; + } + + { + usize i; + + for (i = clks_shell_line_len; i > clks_shell_cursor; i--) { + clks_shell_line[i] = clks_shell_line[i - 1U]; + } + + clks_shell_line[clks_shell_cursor] = ch; + clks_shell_line_len++; + clks_shell_cursor++; + clks_shell_line[clks_shell_line_len] = '\0'; + clks_shell_render_line(); + } +} void clks_shell_init(void) { clks_shell_reset_line(); + clks_shell_history_count = 0U; + clks_shell_history_cancel_nav(); if (clks_tty_ready() == CLKS_FALSE) { clks_shell_ready = CLKS_FALSE;