diff options
Diffstat (limited to 'src/fastcsi.c')
| -rw-r--r-- | src/fastcsi.c | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/src/fastcsi.c b/src/fastcsi.c new file mode 100644 index 0000000..d8983dc --- /dev/null +++ b/src/fastcsi.c @@ -0,0 +1,378 @@ +#include "compat.h" +#include "core.h" +#include "kernels.h" +#include "pen.h" +#include "screen.h" + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +TERM_ALWAYS_INLINE +static inline const char *parse_color_sequence(const char *p, const char *end, uint32_t *color) +{ + const char *s = p + 5; + uint32_t r, g, b; + + if (TERM_EXPECT(s >= end, 0)) return NULL; + unsigned char c0 = (unsigned char)*s++; + unsigned char d0 = (unsigned char)(c0 - '0'); + if (TERM_EXPECT(d0 > 9, 0)) return NULL; + if (TERM_EXPECT(s >= end, 0)) return NULL; + unsigned char c1 = (unsigned char)*s++; + if (c1 == ';') { + r = d0; + } else { + unsigned char d1 = (unsigned char)(c1 - '0'); + if (TERM_EXPECT(d1 > 9, 0)) return NULL; + if (TERM_EXPECT(s >= end, 0)) return NULL; + unsigned char c2 = (unsigned char)*s++; + if (c2 == ';') { + r = d0 * 10 + d1; + } else { + unsigned char d2 = (unsigned char)(c2 - '0'); + if (TERM_EXPECT(d2 > 9 || s >= end || *s++ != ';', 0)) return NULL; + r = d0 * 100 + d1 * 10 + d2; + if (TERM_EXPECT(r > 255, 0)) return NULL; + } + } + + if (TERM_EXPECT(s >= end, 0)) return NULL; + c0 = (unsigned char)*s++; + d0 = (unsigned char)(c0 - '0'); + if (TERM_EXPECT(d0 > 9, 0)) return NULL; + if (TERM_EXPECT(s >= end, 0)) return NULL; + c1 = (unsigned char)*s++; + if (c1 == ';') { + g = d0; + } else { + unsigned char d1 = (unsigned char)(c1 - '0'); + if (TERM_EXPECT(d1 > 9, 0)) return NULL; + if (TERM_EXPECT(s >= end, 0)) return NULL; + unsigned char c2 = (unsigned char)*s++; + if (c2 == ';') { + g = d0 * 10 + d1; + } else { + unsigned char d2 = (unsigned char)(c2 - '0'); + if (TERM_EXPECT(d2 > 9 || s >= end || *s++ != ';', 0)) return NULL; + g = d0 * 100 + d1 * 10 + d2; + if (TERM_EXPECT(g > 255, 0)) return NULL; + } + } + + if (TERM_EXPECT(s >= end, 0)) return NULL; + c0 = (unsigned char)*s++; + d0 = (unsigned char)(c0 - '0'); + if (TERM_EXPECT(d0 > 9, 0)) return NULL; + if (TERM_EXPECT(s >= end, 0)) return NULL; + c1 = (unsigned char)*s++; + if (c1 == 'm') { + b = d0; + } else { + unsigned char d1 = (unsigned char)(c1 - '0'); + if (TERM_EXPECT(d1 > 9, 0)) return NULL; + if (TERM_EXPECT(s >= end, 0)) return NULL; + unsigned char c2 = (unsigned char)*s++; + if (c2 == 'm') { + b = d0 * 10 + d1; + } else { + unsigned char d2 = (unsigned char)(c2 - '0'); + if (TERM_EXPECT(d2 > 9 || s >= end || *s++ != 'm', 0)) return NULL; + b = d0 * 100 + d1 * 10 + d2; + if (TERM_EXPECT(b > 255, 0)) return NULL; + } + } + + *color = term_rgb((int)r, (int)g, (int)b); + return s; +} + +static const char *parse_256color(const char *p, const char *end, uint32_t *color) +{ + const char *q = p + 2; + unsigned v = 0; + int nd = 0; + while (q < end && (unsigned char)(*q - '0') <= 9) { + v = v * 10 + (unsigned char)(*q++ - '0'); + if (++nd > 3) + return NULL; + } + if (!nd || q >= end || *q != 'm' || v > 255) + return NULL; + *color = term_color_index(v); + return q + 1; +} + +TERM_NOINLINE +int term_try_fast_csi(struct term *t, const char *buf, size_t rem) +{ + const char *end = buf + rem; + const char *p = buf; + const char *esc = buf; + const int cols = t->cols; + const int rows = t->rows; + uint32_t fg = t->cur_fg; + uint32_t bg = t->cur_bg; + uint32_t flags_pad = 0; + bool flags_ok = false; + + while (p < end) { + if (p + 5 <= end && (p[0] == '3' || p[0] == '4') && p[1] == '8' && + p[2] == ';' && (p[3] == '2' || p[3] == '5') && p[4] == ';') { + bool is_fg = p[0] == '3'; + uint32_t *slot = is_fg ? &fg : &bg; + uint32_t *other = is_fg ? &bg : &fg; + const char *next = p[3] == '2' ? parse_color_sequence(p, end, slot) + : parse_256color(p + 3, end, slot); + if (!next) { + p = esc; + break; + } + p = next; + if (p + 7 <= end && p[0] == '\033' && p[1] == '[' && + p[2] == (is_fg ? '4' : '3') && p[3] == '8' && + p[4] == ';' && (p[5] == '2' || p[5] == '5') && p[6] == ';') { + next = p[5] == '2' ? parse_color_sequence(p + 2, end, other) + : parse_256color(p + 5, end, other); + if (!next) break; + p = next; + } + if (p < end && (unsigned char)*p >= 0x20 && (unsigned char)*p < 0x7f) { + char ch = *p++; + if (TERM_EXPECT(!flags_ok, 0)) { + flags_pad = term_flags_pad(t); + flags_ok = true; + } + if (TERM_EXPECT(!sc_put1_fast(t, ch, fg, bg, flags_pad), 0)) { + t->cur_fg = fg; + t->cur_bg = bg; + sc_print_ascii(t, &ch, 1); + } + if (p + 2 <= end && p[0] == '\033' && p[1] == '[') { + esc = p; + p += 2; + continue; + } + } + } else if (p[0] == 'm' || (p[0] == '0' && p + 1 < end && p[1] == 'm')) { + reset_sgr(t); + fg = t->cur_fg; + bg = t->cur_bg; + /* Recompute instead of zeroing. The cached flags carry the link + * id, and reset_sgr leaves cur_link set, so zeroing here would + * drop the hyperlink from every cell written after the reset. */ + flags_pad = term_flags_pad(t); + flags_ok = true; + p += p[0] == 'm' ? 1 : 2; + } else if (p[0] == 'J' || + (p[0] >= '0' && p[0] <= '3' && p + 1 < end && p[1] == 'J')) { + t->cur_fg = fg; + t->cur_bg = bg; + sc_erase_display(t, p[0] == 'J' ? 0 : p[0] - '0'); + p += p[0] == 'J' ? 1 : 2; + } else if (p[0] == 'K' || + (p[0] >= '0' && p[0] <= '2' && p + 1 < end && p[1] == 'K')) { + t->cur_fg = fg; + t->cur_bg = bg; + sc_erase_line(t, p[0] == 'K' ? 0 : p[0] - '0'); + p += p[0] == 'K' ? 1 : 2; + } else if (!t->origin_mode && !t->lr_margin_mode && + ((*p >= '0' && *p <= '9') || *p == ';' || + *p == 'A' || *p == 'B' || *p == 'C' || *p == 'D' || + *p == 'E' || *p == 'F' || *p == 'G' || *p == '`' || + *p == 'a' || *p == 'd' || *p == 'e' || + *p == 'H' || *p == 'f' || *p == 'm' || + *p == 'J' || *p == 'K' || *p == 'X' || + *p == 'P' || *p == '@' || *p == 's' || *p == 'u')) { + const char *q = p; + int pv[12] = { 0 }; + int np = 0; + while (q < end && ((*q >= '0' && *q <= '9') || *q == ';')) { + if (*q == ';') { + if (np == 11) { q = NULL; break; } + np++; + q++; + continue; + } + if (pv[np] < 100000) + pv[np] = pv[np] * 10 + (*q - '0'); + q++; + } + if (!q || q >= end) { + p = esc; + break; + } + int nparams = q > p ? np + 1 : 0; + int n = pv[0] > 0 ? pv[0] : 1; + int nx = t->cursor_x, ny = t->cursor_y; + switch (*q) { + case 'A': ny -= n; goto vmove; + case 'B': case 'e': ny += n; goto vmove; + case 'E': ny += n; nx = 0; goto vmove; + case 'F': ny -= n; nx = 0; goto vmove; + vmove: + if (TERM_EXPECT(t->scroll_top != 0 || t->scroll_bottom != rows - 1, 0)) { + p = esc; + goto done; + } + goto move; + case 'C': case 'a': nx += n; goto move; + case 'D': nx -= n; goto move; + case 'G': case '`': nx = n - 1; goto move; + case 'd': ny = n - 1; goto move; + case 'H': case 'f': + ny = n - 1; + nx = np && pv[1] > 0 ? pv[1] - 1 : 0; + goto move; + move: + sc_move(t, nx, ny); + p = q + 1; + break; + case 'm': { + for (int j = 0; j < nparams; j++) { + int v = pv[j]; + switch (v) { + case 0: reset_sgr(t); fg = t->cur_fg; bg = t->cur_bg; break; + case 1: t->bold = true; break; + case 2: t->faint = true; break; + case 3: t->italic = true; break; + case 4: t->underline = 1; break; + case 7: t->inverse = true; break; + case 9: t->strike = true; break; + case 21: t->underline = 2; break; + case 22: t->bold = false; t->faint = false; break; + case 23: t->italic = false; break; + case 24: t->underline = 0; break; + case 27: t->inverse = false; break; + case 29: t->strike = false; break; + case 38: case 48: case 58: { + uint32_t *slot = v == 38 ? &fg : v == 48 ? &bg : &t->cur_ul; + if (j + 2 < nparams && pv[j + 1] == 5) { + if (pv[j + 2] >= 0 && pv[j + 2] <= 255) + *slot = term_color_index(pv[j + 2]); + j += 2; + } else if (j + 1 < nparams && pv[j + 1] == 2 && + j + 4 < nparams) { + *slot = term_rgb(pv[j + 2], pv[j + 3], pv[j + 4]); + j += 4; + } + break; + } + case 39: fg = TERM_COLOR_FG; break; + case 49: bg = TERM_COLOR_BG; break; + case 53: t->overline = true; break; + case 55: t->overline = false; break; + case 59: t->cur_ul = 0; break; + default: + if ((unsigned)(v - 30) < 8) fg = term_color_index(v - 30); + else if ((unsigned)(v - 40) < 8) bg = term_color_index(v - 40); + else if ((unsigned)(v - 90) < 8) fg = term_color_index(v - 90 + 8); + else if ((unsigned)(v - 100) < 8) bg = term_color_index(v - 100 + 8); + break; + } + } + flags_ok = false; + p = q + 1; + break; + } + case 'J': + t->cur_fg = fg; + t->cur_bg = bg; + sc_erase_display(t, pv[0]); + p = q + 1; + break; + case 'K': + t->cur_fg = fg; + t->cur_bg = bg; + sc_erase_line(t, pv[0]); + p = q + 1; + break; + case 'X': + t->cur_fg = fg; + t->cur_bg = bg; + sc_erase_span(t, t->cursor_y, t->cursor_x, + n > cols - t->cursor_x ? cols : t->cursor_x + n); + p = q + 1; + break; + case 'P': + t->cur_fg = fg; + t->cur_bg = bg; + sc_delete_chars(t, n); + p = q + 1; + break; + case '@': + t->cur_fg = fg; + t->cur_bg = bg; + sc_insert_chars(t, n); + p = q + 1; + break; + case 's': + t->cur_fg = fg; + t->cur_bg = bg; + t->saved = term_cursor_save(t); + p = q + 1; + break; + case 'u': + t->cur_fg = fg; + t->cur_bg = bg; + sc_restore_cursor(t, &t->saved); + fg = t->cur_fg; + bg = t->cur_bg; + flags_ok = false; + p = q + 1; + if (t->origin_mode || t->graphics) + goto done; + break; + default: + p = esc; + goto done; + } + } else { + p = esc; + break; + } +tail: + if (p < end && (unsigned char)(*p - 0x20) < 0x5f && !t->pending_wrap) { + t->cur_fg = fg; + t->cur_bg = bg; + int avail = cols - t->cursor_x; + int limit = end - p < (ptrdiff_t)avail ? (int)(end - p) : avail; + int run = scan_ascii_run(p, limit); + if (TERM_EXPECT(!flags_ok, 0)) { + flags_pad = term_flags_pad(t); + flags_ok = true; + } + if (!sc_ascii_fast(t, p, run, fg, bg, flags_pad)) + sc_print_ascii(t, p, run); + p += run; + } + if (p + 2 <= end && p[0] == '\033' && p[1] == '[') { + esc = p; + p += 2; + continue; + } + if (p < end && *p == '\r') { + sc_cr(t); + p++; + goto tail; + } + if (p < end && *p == '\n') { + t->cur_fg = fg; + t->cur_bg = bg; + if (term_lnm(t)) sc_cr(t); + sc_linefeed(t, 1); + p++; + goto tail; + } + break; + } +done: + if (p == end && esc != buf && esc == p - 2) + p = esc; + t->cur_fg = fg; + t->cur_bg = bg; + if (p > buf) + return (int)(p - buf); + return 0; +} |
