diff --git a/cleonos/c/apps/shell/shell_input.c b/cleonos/c/apps/shell/shell_input.c index 2034a43..dde2ead 100644 --- a/cleonos/c/apps/shell/shell_input.c +++ b/cleonos/c/apps/shell/shell_input.c @@ -1,5 +1,170 @@ #include "shell_internal.h" +static char ush_input_clipboard[USH_LINE_MAX]; +static u64 ush_input_clipboard_len = 0ULL; +static u64 ush_input_sel_start = 0ULL; +static u64 ush_input_sel_end = 0ULL; +static int ush_input_sel_active = 0; + +static void ush_input_selection_clear(void) { + ush_input_sel_active = 0; + ush_input_sel_start = 0ULL; + ush_input_sel_end = 0ULL; +} + +static void ush_input_selection_select_all(const ush_state *sh) { + if (sh == (const ush_state *)0 || sh->line_len == 0ULL) { + ush_input_selection_clear(); + return; + } + + ush_input_sel_active = 1; + ush_input_sel_start = 0ULL; + ush_input_sel_end = sh->line_len; +} + +static int ush_input_selection_has_range(const ush_state *sh, u64 *out_start, u64 *out_end) { + u64 start; + u64 end; + + if (sh == (const ush_state *)0 || ush_input_sel_active == 0) { + return 0; + } + + start = ush_input_sel_start; + end = ush_input_sel_end; + + if (start > sh->line_len) { + start = sh->line_len; + } + + if (end > sh->line_len) { + end = sh->line_len; + } + + if (start > end) { + u64 tmp = start; + start = end; + end = tmp; + } + + if (start == end) { + return 0; + } + + if (out_start != (u64 *)0) { + *out_start = start; + } + + if (out_end != (u64 *)0) { + *out_end = end; + } + + return 1; +} + +static void ush_input_delete_range(ush_state *sh, u64 start, u64 end) { + u64 delta; + u64 i; + + if (sh == (ush_state *)0 || start >= end || start >= sh->line_len) { + ush_input_selection_clear(); + return; + } + + if (end > sh->line_len) { + end = sh->line_len; + } + + delta = end - start; + + for (i = start; i + delta <= sh->line_len; i++) { + sh->line[i] = sh->line[i + delta]; + } + + sh->line_len -= delta; + + if (sh->cursor > end) { + sh->cursor -= delta; + } else if (sh->cursor > start) { + sh->cursor = start; + } + + if (sh->cursor > sh->line_len) { + sh->cursor = sh->line_len; + } + + ush_input_selection_clear(); +} + +static int ush_input_delete_selection(ush_state *sh) { + u64 start; + u64 end; + + if (ush_input_selection_has_range(sh, &start, &end) == 0) { + return 0; + } + + ush_input_delete_range(sh, start, end); + return 1; +} + +static void ush_input_copy_selection(const ush_state *sh) { + u64 start; + u64 end; + u64 len; + u64 i; + + if (ush_input_selection_has_range(sh, &start, &end) == 0) { + return; + } + + len = end - start; + if (len > USH_LINE_MAX - 1ULL) { + len = USH_LINE_MAX - 1ULL; + } + + for (i = 0ULL; i < len; i++) { + ush_input_clipboard[i] = sh->line[start + i]; + } + + ush_input_clipboard[len] = '\0'; + ush_input_clipboard_len = len; +} + +static void ush_input_insert_text(ush_state *sh, const char *text, u64 text_len) { + u64 available; + u64 i; + + if (sh == (ush_state *)0 || text == (const char *)0 || text_len == 0ULL) { + return; + } + + if (sh->cursor > sh->line_len) { + sh->cursor = sh->line_len; + } + + available = (USH_LINE_MAX - 1ULL) - sh->line_len; + if (text_len > available) { + text_len = available; + } + + if (text_len == 0ULL) { + return; + } + + for (i = sh->line_len + 1ULL; i > sh->cursor; i--) { + sh->line[i + text_len - 1ULL] = sh->line[i - 1ULL]; + } + + for (i = 0ULL; i < text_len; i++) { + sh->line[sh->cursor + i] = text[i]; + } + + sh->line_len += text_len; + sh->cursor += text_len; +} + static void ush_history_cancel_nav(ush_state *sh) { if (sh == (ush_state *)0) { return; @@ -20,6 +185,7 @@ static void ush_reset_line(ush_state *sh) { sh->cursor = 0ULL; sh->rendered_len = 0ULL; sh->line[0] = '\0'; + ush_input_selection_clear(); } static void ush_load_line(ush_state *sh, const char *line) { @@ -35,6 +201,43 @@ static void ush_load_line(ush_state *sh, const char *line) { ush_copy(sh->line, (u64)sizeof(sh->line), line); sh->line_len = ush_strlen(sh->line); sh->cursor = sh->line_len; + ush_input_selection_clear(); +} + +static void ush_render_line_segment(const ush_state *sh, u64 limit) { + u64 i; + u64 sel_start = 0ULL; + u64 sel_end = 0ULL; + int has_sel; + int inverse_on = 0; + + if (sh == (const ush_state *)0) { + return; + } + + if (limit > sh->line_len) { + limit = sh->line_len; + } + + has_sel = ush_input_selection_has_range(sh, &sel_start, &sel_end); + + for (i = 0ULL; i < limit; i++) { + if (has_sel != 0 && inverse_on == 0 && i == sel_start) { + ush_write("\x1B[7m"); + inverse_on = 1; + } + + if (has_sel != 0 && inverse_on != 0 && i == sel_end) { + ush_write("\x1B[27m"); + inverse_on = 0; + } + + ush_write_char(sh->line[i]); + } + + if (inverse_on != 0) { + ush_write("\x1B[27m"); + } } static void ush_render_line(ush_state *sh) { @@ -46,10 +249,7 @@ static void ush_render_line(ush_state *sh) { ush_write_char('\r'); ush_prompt(sh); - - for (i = 0ULL; i < sh->line_len; i++) { - ush_write_char(sh->line[i]); - } + ush_render_line_segment(sh, sh->line_len); for (i = sh->line_len; i < sh->rendered_len; i++) { ush_write_char(' '); @@ -57,10 +257,7 @@ static void ush_render_line(ush_state *sh) { ush_write_char('\r'); ush_prompt(sh); - - for (i = 0ULL; i < sh->cursor; i++) { - ush_write_char(sh->line[i]); - } + ush_render_line_segment(sh, sh->cursor); sh->rendered_len = sh->line_len; } @@ -131,6 +328,7 @@ static void ush_history_apply_current(ush_state *sh) { if (sh->cursor > sh->line_len) { sh->cursor = sh->line_len; } + ush_input_selection_clear(); } ush_render_line(sh); @@ -207,17 +405,42 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { return; } + if (ch == USH_KEY_SELECT_ALL) { + ush_input_selection_select_all(sh); + sh->cursor = sh->line_len; + ush_render_line(sh); + continue; + } + + if (ch == USH_KEY_COPY) { + ush_input_copy_selection(sh); + continue; + } + + if (ch == USH_KEY_PASTE) { + if (ush_input_clipboard_len > 0ULL) { + ush_history_cancel_nav(sh); + (void)ush_input_delete_selection(sh); + ush_input_insert_text(sh, ush_input_clipboard, ush_input_clipboard_len); + ush_render_line(sh); + } + continue; + } + if (ch == USH_KEY_UP) { + ush_input_selection_clear(); ush_history_up(sh); continue; } if (ch == USH_KEY_DOWN) { + ush_input_selection_clear(); ush_history_down(sh); continue; } if (ch == USH_KEY_LEFT) { + ush_input_selection_clear(); if (sh->cursor > 0ULL) { sh->cursor--; ush_render_line(sh); @@ -226,6 +449,7 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { } if (ch == USH_KEY_RIGHT) { + ush_input_selection_clear(); if (sh->cursor < sh->line_len) { sh->cursor++; ush_render_line(sh); @@ -234,6 +458,7 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { } if (ch == USH_KEY_HOME) { + ush_input_selection_clear(); if (sh->cursor != 0ULL) { sh->cursor = 0ULL; ush_render_line(sh); @@ -242,6 +467,7 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { } if (ch == USH_KEY_END) { + ush_input_selection_clear(); if (sh->cursor != sh->line_len) { sh->cursor = sh->line_len; ush_render_line(sh); @@ -250,10 +476,17 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { } if (ch == '\b' || ch == 127) { + if (ush_input_delete_selection(sh) != 0) { + ush_history_cancel_nav(sh); + ush_render_line(sh); + continue; + } + if (sh->cursor > 0ULL && sh->line_len > 0ULL) { u64 i; ush_history_cancel_nav(sh); + ush_input_selection_clear(); for (i = sh->cursor - 1ULL; i < sh->line_len; i++) { sh->line[i] = sh->line[i + 1ULL]; @@ -267,10 +500,17 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { } if (ch == USH_KEY_DELETE) { + if (ush_input_delete_selection(sh) != 0) { + ush_history_cancel_nav(sh); + ush_render_line(sh); + continue; + } + if (sh->cursor < sh->line_len) { u64 i; ush_history_cancel_nav(sh); + ush_input_selection_clear(); for (i = sh->cursor; i < sh->line_len; i++) { sh->line[i] = sh->line[i + 1ULL]; @@ -290,32 +530,25 @@ void ush_read_line(ush_state *sh, char *out_line, u64 out_size) { continue; } - if (sh->line_len + 1ULL >= USH_LINE_MAX) { - continue; - } - ush_history_cancel_nav(sh); - if (sh->cursor == sh->line_len) { - sh->line[sh->line_len++] = ch; - sh->line[sh->line_len] = '\0'; - sh->cursor = sh->line_len; - ush_write_char(ch); - sh->rendered_len = sh->line_len; - continue; - } - { - u64 i; + int replaced_selection = ush_input_delete_selection(sh); - for (i = sh->line_len; i > sh->cursor; i--) { - sh->line[i] = sh->line[i - 1ULL]; + if (sh->line_len + 1ULL >= USH_LINE_MAX) { + continue; } - sh->line[sh->cursor] = ch; - sh->line_len++; - sh->cursor++; - sh->line[sh->line_len] = '\0'; + if (replaced_selection == 0 && sh->cursor == sh->line_len) { + sh->line[sh->line_len++] = ch; + sh->line[sh->line_len] = '\0'; + sh->cursor = sh->line_len; + ush_write_char(ch); + sh->rendered_len = sh->line_len; + continue; + } + + ush_input_insert_text(sh, &ch, 1ULL); ush_render_line(sh); } } diff --git a/cleonos/c/apps/shell/shell_internal.h b/cleonos/c/apps/shell/shell_internal.h index 2c56202..cfece21 100644 --- a/cleonos/c/apps/shell/shell_internal.h +++ b/cleonos/c/apps/shell/shell_internal.h @@ -21,6 +21,9 @@ typedef long long i64; #define USH_KEY_HOME ((char)0x05) #define USH_KEY_END ((char)0x06) #define USH_KEY_DELETE ((char)0x07) +#define USH_KEY_SELECT_ALL ((char)0x10) +#define USH_KEY_COPY ((char)0x11) +#define USH_KEY_PASTE ((char)0x12) #define USH_CMD_CTX_PATH "/temp/.ush_cmd_ctx.bin" #define USH_CMD_RET_PATH "/temp/.ush_cmd_ret.bin" @@ -100,3 +103,4 @@ int ush_command_program_main(const char *command_name); int ush_try_exec_external(ush_state *sh, const char *cmd, const char *arg, int *out_success); #endif + diff --git a/clks/include/clks/keyboard.h b/clks/include/clks/keyboard.h index 2a62508..e6e1553 100644 --- a/clks/include/clks/keyboard.h +++ b/clks/include/clks/keyboard.h @@ -10,6 +10,9 @@ #define CLKS_KEY_HOME ((char)0x05) #define CLKS_KEY_END ((char)0x06) #define CLKS_KEY_DELETE ((char)0x07) +#define CLKS_KEY_SELECT_ALL ((char)0x10) +#define CLKS_KEY_COPY ((char)0x11) +#define CLKS_KEY_PASTE ((char)0x12) void clks_keyboard_init(void); void clks_keyboard_handle_scancode(u8 scancode); @@ -22,3 +25,4 @@ u64 clks_keyboard_push_count(void); u64 clks_keyboard_pop_count(void); #endif + diff --git a/clks/kernel/keyboard.c b/clks/kernel/keyboard.c index 27f8a01..c64d7c5 100644 --- a/clks/kernel/keyboard.c +++ b/clks/kernel/keyboard.c @@ -8,6 +8,7 @@ #define CLKS_SC_ALT 0x38U #define CLKS_SC_LSHIFT 0x2AU #define CLKS_SC_RSHIFT 0x36U +#define CLKS_SC_CTRL 0x1DU #define CLKS_SC_F1 0x3BU #define CLKS_SC_F2 0x3CU #define CLKS_SC_F3 0x3DU @@ -58,6 +59,8 @@ static u16 clks_kbd_input_count[CLKS_KBD_TTY_MAX]; static clks_bool clks_kbd_alt_down = CLKS_FALSE; static clks_bool clks_kbd_lshift_down = CLKS_FALSE; static clks_bool clks_kbd_rshift_down = CLKS_FALSE; +static clks_bool clks_kbd_lctrl_down = CLKS_FALSE; +static clks_bool clks_kbd_rctrl_down = CLKS_FALSE; static clks_bool clks_kbd_e0_prefix = CLKS_FALSE; static u64 clks_kbd_hotkey_switches = 0ULL; @@ -160,6 +163,39 @@ static char clks_keyboard_translate_scancode(u8 code) { return clks_kbd_map[code]; } +static clks_bool clks_keyboard_ctrl_active(void) { + return (clks_kbd_lctrl_down == CLKS_TRUE || clks_kbd_rctrl_down == CLKS_TRUE) ? CLKS_TRUE : CLKS_FALSE; +} + +static clks_bool clks_keyboard_try_emit_ctrl_shortcut(u8 code, u32 tty_index) { + char shortcut = '\0'; + + if (clks_keyboard_ctrl_active() == CLKS_FALSE) { + return CLKS_FALSE; + } + + switch (code) { + case 0x1EU: + shortcut = CLKS_KEY_SELECT_ALL; + break; + case 0x2EU: + shortcut = CLKS_KEY_COPY; + break; + case 0x2FU: + shortcut = CLKS_KEY_PASTE; + break; + default: + return CLKS_FALSE; + } + + if (clks_keyboard_queue_push_for_tty(tty_index, shortcut) == CLKS_TRUE && + clks_keyboard_should_pump_shell_now() == CLKS_TRUE) { + clks_shell_pump_input(1U); + } + + return CLKS_TRUE; +} + void clks_keyboard_init(void) { u32 tty; @@ -172,6 +208,8 @@ void clks_keyboard_init(void) { clks_kbd_alt_down = CLKS_FALSE; clks_kbd_lshift_down = CLKS_FALSE; clks_kbd_rshift_down = CLKS_FALSE; + clks_kbd_lctrl_down = CLKS_FALSE; + clks_kbd_rctrl_down = CLKS_FALSE; clks_kbd_e0_prefix = CLKS_FALSE; clks_kbd_hotkey_switches = 0ULL; clks_kbd_push_count = 0ULL; @@ -195,8 +233,24 @@ void clks_keyboard_handle_scancode(u8 scancode) { released = ((scancode & 0x80U) != 0U) ? CLKS_TRUE : CLKS_FALSE; code = (u8)(scancode & 0x7FU); + if (code == CLKS_SC_CTRL) { + if (clks_kbd_e0_prefix == CLKS_TRUE) { + clks_kbd_rctrl_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE; + clks_kbd_e0_prefix = CLKS_FALSE; + return; + } + + clks_kbd_lctrl_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE; + return; + } + if (code == CLKS_SC_ALT) { clks_kbd_alt_down = (released == CLKS_FALSE) ? CLKS_TRUE : CLKS_FALSE; + + if (clks_kbd_e0_prefix == CLKS_TRUE) { + clks_kbd_e0_prefix = CLKS_FALSE; + } + return; } @@ -264,8 +318,14 @@ void clks_keyboard_handle_scancode(u8 scancode) { } { - char translated = clks_keyboard_translate_scancode(code); u32 active_tty = clks_tty_active(); + char translated; + + if (clks_keyboard_try_emit_ctrl_shortcut(code, active_tty) == CLKS_TRUE) { + return; + } + + translated = clks_keyboard_translate_scancode(code); if (translated != '\0') { if (clks_keyboard_queue_push_for_tty(active_tty, translated) == CLKS_TRUE && @@ -320,3 +380,4 @@ u64 clks_keyboard_push_count(void) { u64 clks_keyboard_pop_count(void) { return clks_kbd_pop_count; } +