diff --git a/cleonos/c/apps/browser_main.c b/cleonos/c/apps/browser_main.c index 91ab045..ef8d868 100644 --- a/cleonos/c/apps/browser_main.c +++ b/cleonos/c/apps/browser_main.c @@ -31,6 +31,7 @@ #define USH_BROWSER_CSS_TEXT_MAX 4096U #define USH_BROWSER_CSS_RULE_MAX 96U #define USH_BROWSER_CSS_IDENT_MAX 48U +#define USH_BROWSER_OUTPUT_COLS 78U #define USH_BROWSER_ANSI_RESET "\x1B[0m" #define USH_BROWSER_ANSI_BLUE "\x1B[34m" #define USH_BROWSER_ANSI_UNDERLINE "\x1B[4m" @@ -2123,16 +2124,26 @@ static int ush_browser_is_block_tag(GumboTag tag) { case GUMBO_TAG_ARTICLE: case GUMBO_TAG_ASIDE: case GUMBO_TAG_NAV: + case GUMBO_TAG_ADDRESS: case GUMBO_TAG_P: case GUMBO_TAG_UL: case GUMBO_TAG_OL: case GUMBO_TAG_LI: + case GUMBO_TAG_DL: + case GUMBO_TAG_DT: + case GUMBO_TAG_DD: case GUMBO_TAG_TABLE: + case GUMBO_TAG_CAPTION: + case GUMBO_TAG_TBODY: + case GUMBO_TAG_THEAD: + case GUMBO_TAG_TFOOT: case GUMBO_TAG_TR: case GUMBO_TAG_TD: case GUMBO_TAG_TH: case GUMBO_TAG_PRE: case GUMBO_TAG_BLOCKQUOTE: + case GUMBO_TAG_FIGURE: + case GUMBO_TAG_FIGCAPTION: case GUMBO_TAG_H1: case GUMBO_TAG_H2: case GUMBO_TAG_H3: @@ -2142,6 +2153,19 @@ static int ush_browser_is_block_tag(GumboTag tag) { case GUMBO_TAG_FOOTER: case GUMBO_TAG_HEADER: case GUMBO_TAG_MAIN: + case GUMBO_TAG_HGROUP: + case GUMBO_TAG_FORM: + case GUMBO_TAG_FIELDSET: + case GUMBO_TAG_DETAILS: + case GUMBO_TAG_SUMMARY: + case GUMBO_TAG_MENU: + case GUMBO_TAG_DIR: + case GUMBO_TAG_LISTING: + case GUMBO_TAG_XMP: + case GUMBO_TAG_PLAINTEXT: + case GUMBO_TAG_CENTER: + case GUMBO_TAG_DIALOG: + case GUMBO_TAG_SEARCH: return 1; default: return 0; @@ -2494,6 +2518,109 @@ static int ush_browser_render_html(const char *html, u64 html_size) { return 1; } +static int ush_browser_ansi_csi_len(const char *text, u64 pos, u64 len, u64 *out_len) { + u64 i; + + if (text == (const char *)0 || out_len == (u64 *)0 || pos + 1ULL >= len || text[pos] != '\x1B' || + text[pos + 1ULL] != '[') { + return 0; + } + + i = pos + 2ULL; + while (i < len) { + unsigned char ch = (unsigned char)text[i]; + i++; + if (ch >= 0x40U && ch <= 0x7EU) { + *out_len = i - pos; + return 1; + } + } + + return 0; +} + +static void ush_browser_write_span(const char *text, u64 start, u64 end) { + u64 i; + + if (text == (const char *)0 || end <= start) { + return; + } + + for (i = start; i < end; i++) { + (void)fputc(text[i], 1); + } + (void)fputc('\n', 1); +} + +static int ush_browser_print_wrapped_span(const char *text, u64 start, u64 end, u64 *io_line_count) { + u64 pos = start; + + if (text == (const char *)0 || io_line_count == (u64 *)0 || end <= start) { + return 1; + } + + while (pos < end) { + u64 line_start; + u64 line_end; + u64 last_space = (u64)-1; + u64 col = 0ULL; + + while (pos < end && text[pos] == ' ') { + pos++; + } + if (pos >= end) { + break; + } + + line_start = pos; + line_end = pos; + + while (pos < end) { + u64 ansi_len; + char ch = text[pos]; + + if (ush_browser_ansi_csi_len(text, pos, end, &ansi_len) != 0) { + pos += ansi_len; + line_end = pos; + continue; + } + + if (ch == ' ') { + last_space = pos; + } + + if (col >= (u64)USH_BROWSER_OUTPUT_COLS) { + if (last_space != (u64)-1 && last_space > line_start) { + line_end = last_space; + pos = last_space + 1ULL; + } + break; + } + + col++; + pos++; + line_end = pos; + } + + while (line_end > line_start && text[line_end - 1ULL] == ' ') { + line_end--; + } + + if (line_end > line_start) { + ush_browser_write_span(text, line_start, line_end); + (*io_line_count)++; + if (*io_line_count >= 220ULL) { + ush_writeln("[browser] output truncated at 220 lines"); + return 0; + } + } else if (pos < end) { + pos++; + } + } + + return 1; +} + static void ush_browser_print_rendered(const char *source_desc) { u64 i = 0ULL; u64 line_count = 0ULL; @@ -2511,28 +2638,23 @@ static void ush_browser_print_rendered(const char *source_desc) { ush_writeln("------------------------------------------------------------"); while (ush_browser_text_buf[i] != '\0') { - char line[256]; - u64 j = 0ULL; + u64 start = i; - while (ush_browser_text_buf[i] != '\0' && ush_browser_text_buf[i] != '\n' && j + 1ULL < (u64)sizeof(line)) { - line[j++] = ush_browser_text_buf[i++]; - } - line[j] = '\0'; - - if (ush_browser_text_buf[i] == '\n') { + while (ush_browser_text_buf[i] != '\0' && ush_browser_text_buf[i] != '\n') { i++; } - if (line[0] != '\0') { - ush_writeln(line); - line_count++; - if (line_count >= 220ULL) { - ush_writeln("[browser] output truncated at 220 lines"); + if (i > start) { + if (ush_browser_print_wrapped_span(ush_browser_text_buf, start, i, &line_count) == 0) { break; } } else if (line_count > 0ULL) { ush_writeln(""); } + + if (ush_browser_text_buf[i] == '\n') { + i++; + } } if (ush_browser_link_count > 0ULL) { diff --git a/cleonos/c/apps/terminal/terminal.c b/cleonos/c/apps/terminal/terminal.c index 10e7d14..96b9b65 100644 --- a/cleonos/c/apps/terminal/terminal.c +++ b/cleonos/c/apps/terminal/terminal.c @@ -15,9 +15,12 @@ #define TERM_LINES 128U #define TERM_LINE_MAX 160U #define TERM_INPUT_MAX 160U -#define TERM_ANSI_MAX 24U +#define TERM_ANSI_MAX 95U #define TERM_EVENT_BUDGET 128ULL #define TERM_IDLE_SPINS 24 +#define TERM_STYLE_NONE 0U +#define TERM_STYLE_BOLD 1U +#define TERM_STYLE_UNDERLINE 2U #define TERM_COLOR_WHITE 0x00FFFFFFU #define TERM_COLOR_WIN_BLUE 0x000078D7U @@ -38,6 +41,7 @@ ((u64)(r5) << 5U) | (u64)(r6)) typedef unsigned int term_u32; +typedef unsigned char term_u8; typedef struct term_app { int screen_w; @@ -70,9 +74,20 @@ typedef struct term_app { char input[TERM_INPUT_MAX]; u64 input_len; char lines[TERM_LINES][TERM_LINE_MAX]; - term_u32 line_colors[TERM_LINES]; + term_u32 cell_fg[TERM_LINES][TERM_LINE_MAX]; + term_u32 cell_bg[TERM_LINES][TERM_LINE_MAX]; + term_u8 cell_style[TERM_LINES][TERM_LINE_MAX]; u64 line_count; - term_u32 color; + u64 cursor_row; + u64 cursor_col; + u64 saved_row; + u64 saved_col; + term_u32 current_fg; + term_u32 current_bg; + term_u8 current_style; + int ansi_bold; + int ansi_underline; + int ansi_inverse; int ansi_state; char ansi_buf[TERM_ANSI_MAX]; u64 ansi_len; @@ -309,6 +324,36 @@ static void term_draw_char(term_app *app, int x, int y, char ch, int scale, term } } +static void term_draw_char_styled(term_app *app, int x, int y, char ch, term_u32 fg, term_u32 bg, term_u8 style) { + u64 mask = term_glyph_mask(ch); + int row; + + term_fill_rect(app, x, y, 6, 11, bg); + if (mask == 0ULL) { + if ((style & TERM_STYLE_UNDERLINE) != 0U) { + term_fill_rect(app, x, y + 8, 5, 1, fg); + } + return; + } + + for (row = 0; row < 7; row++) { + int col; + for (col = 0; col < 5; col++) { + unsigned int bit_index = (unsigned int)((6 - row) * 5 + (4 - col)); + if ((mask & (1ULL << bit_index)) != 0ULL) { + term_fill_rect(app, x + col, y + row, 1, 1, fg); + if ((style & TERM_STYLE_BOLD) != 0U && col < 4) { + term_fill_rect(app, x + col + 1, y + row, 1, 1, fg); + } + } + } + } + + if ((style & TERM_STYLE_UNDERLINE) != 0U) { + term_fill_rect(app, x, y + 8, 5, 1, fg); + } +} + static void term_draw_text_limit(term_app *app, int x, int y, const char *text, int scale, term_u32 color, int max_x) { int cursor_x = x; @@ -358,93 +403,194 @@ static void term_draw_control_button(term_app *app, int x, int active, int kind) } } -static term_u32 term_ansi_color(int code) { - switch (code) { - case 30: - return 0x00404040U; - case 31: - return 0x00F44747U; - case 32: - return 0x0060D060U; - case 33: - return 0x00DCDCAAU; - case 34: - return 0x00569CD6U; - case 35: - return 0x00C586C0U; - case 36: - return 0x004EC9B0U; - case 37: - return 0x00DCDCDCU; - case 90: - return 0x00808080U; - case 91: - return 0x00FF7070U; - case 92: - return 0x0080FF80U; - case 93: - return 0x00FFFF80U; - case 94: - return 0x0080C0FFU; - case 95: - return 0x00FF80FFU; - case 96: - return 0x0080FFFFU; - case 97: - return 0x00FFFFFFU; - default: +static term_u32 term_ansi_palette(u64 index) { + static const term_u32 palette[16] = {0x00000000U, 0x00CD3131U, 0x000DBC79U, 0x00E5E510U, 0x002472C8U, 0x00BC3FBCU, + 0x0011A8CDU, 0x00E5E5E5U, 0x00666666U, 0x00F14C4CU, 0x0023D18BU, 0x00F5F543U, + 0x003B8EEAU, 0x00D670D6U, 0x0029B8DBU, 0x00FFFFFFU}; + + return (index < 16ULL) ? palette[index] : TERM_COLOR_DEFAULT; +} + +static term_u32 term_ansi_clamp_255(u64 value) { + return (term_u32)((value > 255ULL) ? 255ULL : value); +} + +static term_u32 term_ansi_color_from_256(u64 index) { + if (index < 16ULL) { + return term_ansi_palette(index); + } + if (index <= 231ULL) { + static const term_u32 steps[6] = {0U, 95U, 135U, 175U, 215U, 255U}; + u64 n = index - 16ULL; + u64 r = n / 36ULL; + u64 g = (n / 6ULL) % 6ULL; + u64 b = n % 6ULL; + return (steps[r] << 16U) | (steps[g] << 8U) | steps[b]; + } + if (index <= 255ULL) { + term_u32 gray = (term_u32)(8ULL + ((index - 232ULL) * 10ULL)); + return (gray << 16U) | (gray << 8U) | gray; + } + return TERM_COLOR_DEFAULT; +} + +static void term_reset_style(term_app *app) { + if (app == (term_app *)0) { + return; + } + app->current_fg = TERM_COLOR_DEFAULT; + app->current_bg = TERM_COLOR_BG; + app->current_style = TERM_STYLE_NONE; + app->ansi_bold = 0; + app->ansi_underline = 0; + app->ansi_inverse = 0; +} + +static term_u32 term_effective_fg(const term_app *app) { + if (app == (const term_app *)0) { return TERM_COLOR_DEFAULT; } + return (app->ansi_inverse != 0) ? app->current_bg : app->current_fg; +} + +static term_u32 term_effective_bg(const term_app *app) { + if (app == (const term_app *)0) { + return TERM_COLOR_BG; + } + return (app->ansi_inverse != 0) ? app->current_fg : app->current_bg; +} + +static term_u8 term_effective_style(const term_app *app) { + term_u8 style = TERM_STYLE_NONE; + if (app == (const term_app *)0) { + return style; + } + if (app->ansi_bold != 0) { + style |= TERM_STYLE_BOLD; + } + if (app->ansi_underline != 0) { + style |= TERM_STYLE_UNDERLINE; + } + style |= app->current_style; + return style; +} + +static void term_clear_cell(term_app *app, u64 row, u64 col) { + if (app == (term_app *)0 || row >= (u64)TERM_LINES || col >= (u64)TERM_LINE_MAX) { + return; + } + app->lines[row][col] = ' '; + app->cell_fg[row][col] = term_effective_fg(app); + app->cell_bg[row][col] = term_effective_bg(app); + app->cell_style[row][col] = term_effective_style(app); +} + +static void term_trim_line(term_app *app, u64 row) { + i64 col; + + if (app == (term_app *)0 || row >= (u64)TERM_LINES) { + return; + } + app->lines[row][TERM_LINE_MAX - 1U] = '\0'; + for (col = (i64)TERM_LINE_MAX - 2; col >= 0; col--) { + if (app->lines[row][(u64)col] != ' ' && app->lines[row][(u64)col] != '\0') { + app->lines[row][(u64)col + 1ULL] = '\0'; + return; + } + app->lines[row][(u64)col] = '\0'; + } +} + +static void term_clear_line_range(term_app *app, u64 row, u64 start_col, u64 end_col) { + u64 col; + + if (app == (term_app *)0 || row >= (u64)TERM_LINES) { + return; + } + if (end_col > (u64)TERM_LINE_MAX - 1ULL) { + end_col = (u64)TERM_LINE_MAX - 1ULL; + } + for (col = start_col; col < end_col; col++) { + term_clear_cell(app, row, col); + } + term_trim_line(app, row); } static void term_clear(term_app *app) { + u64 row; + u64 col; + + if (app == (term_app *)0) { + return; + } + for (row = 0ULL; row < (u64)TERM_LINES; row++) { + for (col = 0ULL; col < (u64)TERM_LINE_MAX; col++) { + app->lines[row][col] = '\0'; + app->cell_fg[row][col] = term_effective_fg(app); + app->cell_bg[row][col] = term_effective_bg(app); + app->cell_style[row][col] = term_effective_style(app); + } + } + app->line_count = 0ULL; + app->cursor_row = 0ULL; + app->cursor_col = 0ULL; +} + +static void term_ensure_row(term_app *app, u64 row) { u64 i; if (app == (term_app *)0) { return; } - - ush_zero(app->lines, (u64)sizeof(app->lines)); - for (i = 0ULL; i < (u64)TERM_LINES; i++) { - app->line_colors[i] = app->color; + if (row >= (u64)TERM_LINES) { + row = (u64)TERM_LINES - 1ULL; } - app->line_count = 0ULL; -} - -static void term_ensure_line(term_app *app) { - if (app != (term_app *)0 && app->line_count == 0ULL) { - app->line_count = 1ULL; - app->lines[0][0] = '\0'; - app->line_colors[0] = app->color; + while (app->line_count <= row) { + i = app->line_count; + app->lines[i][0] = '\0'; + app->cell_fg[i][0] = term_effective_fg(app); + app->cell_bg[i][0] = term_effective_bg(app); + app->cell_style[i][0] = term_effective_style(app); + app->line_count++; } } static void term_shift_lines(term_app *app) { u64 i; + u64 col; + if (app == (term_app *)0) { + return; + } for (i = 1ULL; i < (u64)TERM_LINES; i++) { ush_copy(app->lines[i - 1ULL], (u64)TERM_LINE_MAX, app->lines[i]); - app->line_colors[i - 1ULL] = app->line_colors[i]; + for (col = 0ULL; col < (u64)TERM_LINE_MAX; col++) { + app->cell_fg[i - 1ULL][col] = app->cell_fg[i][col]; + app->cell_bg[i - 1ULL][col] = app->cell_bg[i][col]; + app->cell_style[i - 1ULL][col] = app->cell_style[i][col]; + } } app->lines[TERM_LINES - 1U][0] = '\0'; - app->line_colors[TERM_LINES - 1U] = app->color; + for (col = 0ULL; col < (u64)TERM_LINE_MAX; col++) { + app->cell_fg[TERM_LINES - 1U][col] = term_effective_fg(app); + app->cell_bg[TERM_LINES - 1U][col] = term_effective_bg(app); + app->cell_style[TERM_LINES - 1U][col] = term_effective_style(app); + } } static void term_newline(term_app *app) { if (app == (term_app *)0) { return; } - if (app->line_count == 0ULL) { - term_ensure_line(app); - return; + term_ensure_row(app, app->cursor_row); + app->cursor_col = 0ULL; + if (app->cursor_row + 1ULL >= (u64)TERM_LINES) { + term_shift_lines(app); + app->line_count = (u64)TERM_LINES; + } else { + app->cursor_row++; + term_ensure_row(app, app->cursor_row); } - if (app->line_count < (u64)TERM_LINES) { - app->lines[app->line_count][0] = '\0'; - app->line_colors[app->line_count] = app->color; - app->line_count++; - return; - } - term_shift_lines(app); } static void term_append_char(term_app *app, char ch) { @@ -455,6 +601,7 @@ static void term_append_char(term_app *app, char ch) { return; } if (ch == '\r') { + app->cursor_col = 0ULL; return; } if (ch == '\n') { @@ -462,86 +609,257 @@ static void term_append_char(term_app *app, char ch) { return; } if (ch == '\t') { - ch = ' '; + term_append_char(app, ' '); + term_append_char(app, ' '); + term_append_char(app, ' '); + term_append_char(app, ' '); + return; } if (ch < ' ' || ch > '~') { ch = '?'; } - - term_ensure_line(app); - line = app->lines[app->line_count - 1ULL]; - app->line_colors[app->line_count - 1ULL] = app->color; - len = ush_strlen(line); - if (len + 1ULL >= (u64)TERM_LINE_MAX) { + if (app->cursor_col + 1ULL >= (u64)TERM_LINE_MAX) { term_newline(app); - line = app->lines[app->line_count - 1ULL]; - app->line_colors[app->line_count - 1ULL] = app->color; - len = 0ULL; } - line[len] = ch; - line[len + 1ULL] = '\0'; + + term_ensure_row(app, app->cursor_row); + line = app->lines[app->cursor_row]; + len = ush_strlen(line); + while (len < app->cursor_col && len + 1ULL < (u64)TERM_LINE_MAX) { + line[len] = ' '; + app->cell_fg[app->cursor_row][len] = term_effective_fg(app); + app->cell_bg[app->cursor_row][len] = term_effective_bg(app); + app->cell_style[app->cursor_row][len] = term_effective_style(app); + len++; + } + line[app->cursor_col] = ch; + app->cell_fg[app->cursor_row][app->cursor_col] = term_effective_fg(app); + app->cell_bg[app->cursor_row][app->cursor_col] = term_effective_bg(app); + app->cell_style[app->cursor_row][app->cursor_col] = term_effective_style(app); + if (line[app->cursor_col + 1ULL] == '\0') { + line[app->cursor_col + 1ULL] = '\0'; + } + app->cursor_col++; } -static int term_ansi_number(const char *text, u64 *offset, int *out_value) { - int value = 0; - int any = 0; +static u64 term_ansi_parse_params(const char *params, u64 *out_values, u64 max_values) { + u64 count = 0ULL; + u64 value = 0ULL; + int has_digit = 0; + u64 i; - if (text == (const char *)0 || offset == (u64 *)0 || out_value == (int *)0) { - return 0; + if (out_values == (u64 *)0 || max_values == 0ULL) { + return 0ULL; } - while (text[*offset] >= '0' && text[*offset] <= '9') { - value = (value * 10) + (text[*offset] - '0'); - (*offset)++; - any = 1; + if (params == (const char *)0 || params[0] == '\0') { + out_values[0] = 0ULL; + return 1ULL; } - *out_value = value; - return any; + for (i = 0ULL;; i++) { + char ch = params[i]; + if (ch >= '0' && ch <= '9') { + has_digit = 1; + value = (value * 10ULL) + (u64)(ch - '0'); + continue; + } + if (ch == '?' && i == 0ULL) { + continue; + } + if (ch == ';' || ch == '\0') { + if (count < max_values) { + out_values[count++] = (has_digit != 0) ? value : 0ULL; + } + value = 0ULL; + has_digit = 0; + if (ch == '\0') { + break; + } + } + } + return (count == 0ULL) ? 1ULL : count; +} + +static u64 term_ansi_param_or_default(const u64 *params, u64 count, u64 index, u64 default_value) { + if (params == (const u64 *)0 || index >= count || params[index] == 0ULL) { + return default_value; + } + return params[index]; } static void term_apply_sgr(term_app *app, const char *params) { - u64 pos = 0ULL; + u64 values[16]; + u64 count = term_ansi_parse_params(params, values, 16ULL); + u64 i; - if (app == (term_app *)0 || params == (const char *)0 || params[0] == '\0') { - if (app != (term_app *)0) { - app->color = TERM_COLOR_DEFAULT; - } + if (app == (term_app *)0) { return; } + for (i = 0ULL; i < count; i++) { + u64 code = values[i]; - while (params[pos] != '\0') { - int code = 0; - int has_number = term_ansi_number(params, &pos, &code); + if (code == 0ULL) { + term_reset_style(app); + } else if (code == 1ULL) { + app->ansi_bold = 1; + } else if (code == 4ULL) { + app->ansi_underline = 1; + } else if (code == 7ULL) { + app->ansi_inverse = 1; + } else if (code == 21ULL || code == 22ULL) { + app->ansi_bold = 0; + } else if (code == 24ULL) { + app->ansi_underline = 0; + } else if (code == 27ULL) { + app->ansi_inverse = 0; + } else if (code == 39ULL) { + app->current_fg = TERM_COLOR_DEFAULT; + } else if (code == 49ULL) { + app->current_bg = TERM_COLOR_BG; + } else if (code >= 30ULL && code <= 37ULL) { + u64 idx = code - 30ULL; + if (app->ansi_bold != 0) { + idx += 8ULL; + } + app->current_fg = term_ansi_palette(idx); + } else if (code >= 90ULL && code <= 97ULL) { + app->current_fg = term_ansi_palette((code - 90ULL) + 8ULL); + } else if (code >= 40ULL && code <= 47ULL) { + app->current_bg = term_ansi_palette(code - 40ULL); + } else if (code >= 100ULL && code <= 107ULL) { + app->current_bg = term_ansi_palette((code - 100ULL) + 8ULL); + } else if ((code == 38ULL || code == 48ULL) && i + 1ULL < count) { + u64 mode = values[i + 1ULL]; + term_u32 color; - if (has_number == 0) { - code = 0; + if (mode == 5ULL && i + 2ULL < count) { + color = term_ansi_color_from_256(values[i + 2ULL]); + if (code == 38ULL) { + app->current_fg = color; + } else { + app->current_bg = color; + } + i += 2ULL; + } else if (mode == 2ULL && i + 4ULL < count) { + term_u32 r = term_ansi_clamp_255(values[i + 2ULL]); + term_u32 g = term_ansi_clamp_255(values[i + 3ULL]); + term_u32 b = term_ansi_clamp_255(values[i + 4ULL]); + color = (r << 16U) | (g << 8U) | b; + if (code == 38ULL) { + app->current_fg = color; + } else { + app->current_bg = color; + } + i += 4ULL; + } } - if (code == 0 || code == 39) { - app->color = TERM_COLOR_DEFAULT; - } else if ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) { - app->color = term_ansi_color(code); - } - if (params[pos] == ';') { - pos++; - continue; - } - break; } } static void term_apply_ansi(term_app *app, char final_ch) { + u64 values[16]; + u64 count; + int private_mode; + if (app == (term_app *)0) { return; } app->ansi_buf[app->ansi_len] = '\0'; + count = term_ansi_parse_params(app->ansi_buf, values, 16ULL); + private_mode = (app->ansi_buf[0] == '?') ? 1 : 0; + if (final_ch == 'm') { term_apply_sgr(app, app->ansi_buf); } else if (final_ch == 'J') { - u64 pos = 0ULL; - int code = 0; - - if (term_ansi_number(app->ansi_buf, &pos, &code) != 0 && (code == 2 || code == 3)) { + u64 mode = (count == 0ULL) ? 0ULL : values[0]; + if (mode == 0ULL) { + u64 row; + term_clear_line_range(app, app->cursor_row, app->cursor_col, (u64)TERM_LINE_MAX - 1ULL); + for (row = app->cursor_row + 1ULL; row < (u64)TERM_LINES; row++) { + term_clear_line_range(app, row, 0ULL, (u64)TERM_LINE_MAX - 1ULL); + } + } else if (mode == 1ULL) { + u64 row; + for (row = 0ULL; row < app->cursor_row && row < (u64)TERM_LINES; row++) { + term_clear_line_range(app, row, 0ULL, (u64)TERM_LINE_MAX - 1ULL); + } + term_clear_line_range(app, app->cursor_row, 0ULL, app->cursor_col + 1ULL); + } else if (mode == 2ULL || mode == 3ULL) { term_clear(app); } + } else if (final_ch == 'K') { + u64 mode = (count == 0ULL) ? 0ULL : values[0]; + if (mode == 0ULL) { + term_clear_line_range(app, app->cursor_row, app->cursor_col, (u64)TERM_LINE_MAX - 1ULL); + } else if (mode == 1ULL) { + term_clear_line_range(app, app->cursor_row, 0ULL, app->cursor_col + 1ULL); + } else { + term_clear_line_range(app, app->cursor_row, 0ULL, (u64)TERM_LINE_MAX - 1ULL); + } + } else if (final_ch == 'H' || final_ch == 'f') { + u64 row = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + u64 col = term_ansi_param_or_default(values, count, 1ULL, 1ULL); + app->cursor_row = (row > 0ULL) ? row - 1ULL : 0ULL; + app->cursor_col = (col > 0ULL) ? col - 1ULL : 0ULL; + if (app->cursor_row >= (u64)TERM_LINES) { + app->cursor_row = (u64)TERM_LINES - 1ULL; + } + if (app->cursor_col >= (u64)TERM_LINE_MAX - 1ULL) { + app->cursor_col = (u64)TERM_LINE_MAX - 2ULL; + } + term_ensure_row(app, app->cursor_row); + } else if (final_ch == 'A') { + u64 n = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + app->cursor_row = (n > app->cursor_row) ? 0ULL : app->cursor_row - n; + } else if (final_ch == 'B') { + u64 n = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + app->cursor_row += n; + if (app->cursor_row >= (u64)TERM_LINES) { + app->cursor_row = (u64)TERM_LINES - 1ULL; + } + term_ensure_row(app, app->cursor_row); + } else if (final_ch == 'C') { + u64 n = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + app->cursor_col += n; + if (app->cursor_col >= (u64)TERM_LINE_MAX - 1ULL) { + app->cursor_col = (u64)TERM_LINE_MAX - 2ULL; + } + } else if (final_ch == 'D') { + u64 n = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + app->cursor_col = (n > app->cursor_col) ? 0ULL : app->cursor_col - n; + } else if (final_ch == 'E' || final_ch == 'F') { + u64 n = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + if (final_ch == 'E') { + app->cursor_row += n; + if (app->cursor_row >= (u64)TERM_LINES) { + app->cursor_row = (u64)TERM_LINES - 1ULL; + } + } else { + app->cursor_row = (n > app->cursor_row) ? 0ULL : app->cursor_row - n; + } + app->cursor_col = 0ULL; + term_ensure_row(app, app->cursor_row); + } else if (final_ch == 'G') { + u64 col = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + app->cursor_col = (col > 0ULL) ? col - 1ULL : 0ULL; + if (app->cursor_col >= (u64)TERM_LINE_MAX - 1ULL) { + app->cursor_col = (u64)TERM_LINE_MAX - 2ULL; + } + } else if (final_ch == 'd') { + u64 row = term_ansi_param_or_default(values, count, 0ULL, 1ULL); + app->cursor_row = (row > 0ULL) ? row - 1ULL : 0ULL; + if (app->cursor_row >= (u64)TERM_LINES) { + app->cursor_row = (u64)TERM_LINES - 1ULL; + } + term_ensure_row(app, app->cursor_row); + } else if (final_ch == 's') { + app->saved_row = app->cursor_row; + app->saved_col = app->cursor_col; + } else if (final_ch == 'u') { + app->cursor_row = (app->saved_row < (u64)TERM_LINES) ? app->saved_row : (u64)TERM_LINES - 1ULL; + app->cursor_col = (app->saved_col < (u64)TERM_LINE_MAX - 1ULL) ? app->saved_col : (u64)TERM_LINE_MAX - 2ULL; + term_ensure_row(app, app->cursor_row); + } else if ((final_ch == 'h' || final_ch == 'l') && private_mode != 0) { + /* Cursor visibility mode is accepted for compatibility; this terminal does not draw a PTY cursor. */ } app->ansi_state = 0; app->ansi_len = 0ULL; @@ -558,6 +876,21 @@ static void term_append_ansi_char(term_app *app, char ch) { app->ansi_len = 0ULL; return; } + if (ch == '7') { + app->saved_row = app->cursor_row; + app->saved_col = app->cursor_col; + app->ansi_state = 0; + app->ansi_len = 0ULL; + return; + } + if (ch == '8') { + app->cursor_row = (app->saved_row < (u64)TERM_LINES) ? app->saved_row : (u64)TERM_LINES - 1ULL; + app->cursor_col = (app->saved_col < (u64)TERM_LINE_MAX - 1ULL) ? app->saved_col : (u64)TERM_LINE_MAX - 2ULL; + term_ensure_row(app, app->cursor_row); + app->ansi_state = 0; + app->ansi_len = 0ULL; + return; + } app->ansi_state = 0; app->ansi_len = 0ULL; return; @@ -677,15 +1010,23 @@ static void term_render(term_app *app) { for (i = start; i < app->line_count; i++) { int row = (int)(i - start); int y = TERM_TITLE_H + 50 + (row * line_h); - term_u32 color = app->line_colors[i]; + u64 col; + const char *line = app->lines[i]; if (y + 8 >= app->h - TERM_BOTTOM_H) { break; } - if (color == 0U) { - color = TERM_COLOR_DEFAULT; + for (col = 0ULL; line[col] != '\0' && col + 1ULL < (u64)TERM_LINE_MAX; col++) { + int x = 10 + ((int)col * 6); + term_u32 fg = app->cell_fg[i][col]; + term_u32 bg = app->cell_bg[i][col]; + term_u8 style = app->cell_style[i][col]; + + if (x + 5 >= app->w - 10) { + break; + } + term_draw_char_styled(app, x, y, line[col], fg, bg, style); } - term_draw_text_limit(app, 10, y, app->lines[i], 1, color, app->w - 10); } term_fill_rect(app, 0, app->h - TERM_BOTTOM_H, app->w, TERM_BOTTOM_H, TERM_COLOR_BAR); @@ -1336,7 +1677,7 @@ int cleonos_terminal_run(void) { ush_zero(&app, (u64)sizeof(app)); app.running = 1; - app.color = TERM_COLOR_DEFAULT; + term_reset_style(&app); ush_copy(app.cwd, (u64)sizeof(app.cwd), "/"); term_clear(&app); term_append_line(&app, "CLEONOS TERMINAL READY");