diff --git a/kitty/parser.c b/kitty/parser.c index fece24f63..79dc0b4ec 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -151,10 +151,10 @@ _report_params(PyObject *dump_callback, const char *name, int *params, unsigned // }}} // Normal mode {{{ -static inline void +static void screen_nel(Screen *screen) { screen_carriage_return(screen); screen_linefeed(screen); } -static inline void +static void dispatch_normal_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback) { #define CALL_SCREEN_HANDLER(name) REPORT_COMMAND(name); name(screen); break; switch(ch) { @@ -203,7 +203,7 @@ dispatch_normal_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dum } // }}} // Esc mode {{{ -static inline void +static void dispatch_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback) { #define CALL_ED(name) REPORT_COMMAND(name); name(screen); SET_STATE(0); #define CALL_ED1(name, ch) REPORT_COMMAND(name, ch); name(screen, ch); SET_STATE(0); @@ -1011,7 +1011,7 @@ dispatch_dcs(Screen *screen, PyObject DUMP_UNUSED *dump_callback) { screen->pending_mode.activated_at = monotonic(); REPORT_COMMAND(screen_start_pending_mode); } else { - // ignore stop without matching start, see _queue_pending_bytes() + // ignore stop without matching start, see queue_pending_bytes() // for how stop is detected while in pending mode. REPORT_ERROR("Pending mode stop command issued while not in pending mode"); REPORT_COMMAND(screen_stop_pending_mode); @@ -1306,110 +1306,107 @@ FLUSH_DRAW; return i; } -static size_t -_queue_pending_bytes(Screen *screen, const uint8_t *buf, size_t len, PyObject *dump_callback DUMP_UNUSED) { - size_t pos = 0; - enum STATE { NORMAL, MAYBE_DCS_OR_CSI, IN_DCS, IN_CSI, EXPECTING_DATA, EXPECTING_CSI_DATA, EXPECTING_SLASH }; -#define pm screen->pending_mode - enum STATE state = pm.state; -#define stop_pending_mode \ - if (state == EXPECTING_CSI_DATA) { REPORT_COMMAND(screen_reset_mode, 2026, 1); } \ - else { REPORT_COMMAND(screen_stop_pending_mode); } \ - pm.activated_at = 0; \ - goto end; -#define start_pending_mode \ - if (state == EXPECTING_CSI_DATA) { REPORT_COMMAND(screen_set_mode, 2026, 1); } \ - else { REPORT_COMMAND(screen_start_pending_mode); } \ - pm.activated_at = monotonic(); -#define COPY(what) pm.buf[pm.used++] = what -#define COPY_STOP_BUF(for_dcs) { \ - COPY(0x1b); \ - if (for_dcs) { COPY('P'); COPY(PENDING_MODE_CHAR); } \ - else { COPY('['); COPY('?'); } \ - for (size_t i = 0; i < pm.stop_buf_pos; i++) { \ - COPY(pm.stop_buf[i]); \ - } \ - pm.stop_buf_pos = 0;} +static void +write_pending_char(Screen *screen, uint32_t ch) { + screen->pending_mode.used += encode_utf8(ch, (char*)screen->pending_mode.buf + screen->pending_mode.used); +} - while (pos < len) { - uint8_t ch = buf[pos++]; - switch(state) { - case NORMAL: - if (ch == ESC) state = MAYBE_DCS_OR_CSI; - else COPY(ch); - break; - case MAYBE_DCS_OR_CSI: - if (ch == 'P') state = IN_DCS; - else if (ch == '[') state = IN_CSI; - else { - state = NORMAL; - COPY(0x1b); COPY(ch); - } - break; - case IN_DCS: - if (ch == PENDING_MODE_CHAR) { state = EXPECTING_DATA; pm.stop_buf_pos = 0; } - else { - state = NORMAL; - COPY(0x1b); COPY('P'); COPY(ch); - } - break; - case IN_CSI: - if (ch == '?') { state = EXPECTING_CSI_DATA; pm.stop_buf_pos = 0; } - else { - state = NORMAL; - COPY(0x1b); COPY('['); COPY(ch); - } - break; - case EXPECTING_CSI_DATA: - if (ch == 'h' || ch == 'l') { - if (pm.stop_buf_pos == 4 && memcmp(pm.stop_buf, "2026", 4) == 0) { - if (ch == 'h') { start_pending_mode } else { stop_pending_mode } - } else { - COPY_STOP_BUF(false); COPY(ch); - } - state = NORMAL; - } else { - pm.stop_buf[pm.stop_buf_pos++] = ch; - if (pm.stop_buf_pos >= sizeof(pm.stop_buf)) { - state = NORMAL; - COPY_STOP_BUF(false); - } - } - break; - case EXPECTING_DATA: - pm.stop_buf[pm.stop_buf_pos++] = ch; - if (ch == 0x1b) state = EXPECTING_SLASH; - else { - if (pm.stop_buf_pos >= sizeof(pm.stop_buf)) { - state = NORMAL; - COPY_STOP_BUF(true); - } - } - break; - case EXPECTING_SLASH: - if ( - ch == '\\' && - pm.stop_buf_pos >= 2 && - (pm.stop_buf[0] == '1' || pm.stop_buf[0] == '2') && - pm.stop_buf[1] == 's' - ) { - // We found a pending mode sequence - if (pm.stop_buf[0] == '2') { stop_pending_mode } else { start_pending_mode } - } else { - COPY_STOP_BUF(true); COPY(ch); - } - state = NORMAL; - break; - } +static void +pending_normal_mode_char(Screen *screen, uint32_t ch, PyObject *dump_callback UNUSED) { + switch(ch) { + case ESC: case CSI: case OSC: case DCS: case APC: case PM: + SET_STATE(ch); break; + default: + write_pending_char(screen, ch); break; } +} + +static void +pending_esc_mode_char(Screen *screen, uint32_t ch, PyObject *dump_callback UNUSED) { + if (screen->parser_buf_pos > 0) { + write_pending_char(screen, ESC); + write_pending_char(screen, screen->parser_buf[screen->parser_buf_pos]); + write_pending_char(screen, ch); + SET_STATE(0); + return; + } + switch (ch) { + case ESC_DCS: + SET_STATE(DCS); break; + case ESC_OSC: + SET_STATE(OSC); break; + case ESC_CSI: + SET_STATE(CSI); break; + case ESC_APC: + SET_STATE(APC); break; + case ESC_PM: + SET_STATE(PM); break; + case '%': + case '(': + case ')': + case '*': + case '+': + case '-': + case '.': + case '/': + case ' ': + case '#': + screen->parser_buf[screen->parser_buf_pos++] = ch; + break; + default: + write_pending_char(screen, ESC); write_pending_char(screen, ch); + SET_STATE(0); break; + } +} + +#define pb(i) screen->parser_buf[i] +static void +pending_escape_code(Screen *screen, char_type start_ch, char_type end_ch) { + write_pending_char(screen, start_ch); + for (unsigned i = 0; i < screen->parser_buf_pos; i++) write_pending_char(screen, pb(i)); + write_pending_char(screen, end_ch); +} + +static void pending_pm(Screen *screen, PyObject *dump_callback UNUSED) { pending_escape_code(screen, PM, ST); } +static void pending_apc(Screen *screen, PyObject *dump_callback UNUSED) { pending_escape_code(screen, APC, ST); } +static void pending_osc(Screen *screen, PyObject *dump_callback UNUSED) { pending_escape_code(screen, OSC, ST); } + + +static void +pending_dcs(Screen *screen, PyObject *dump_callback DUMP_UNUSED) { + if (screen->parser_buf_pos >= 3 && pb(0) == '=' && (pb(1) == '1' || pb(1) == '2') && pb(2) == 's') { + screen->pending_mode.activated_at = pb(1) == '1' ? monotonic() : 0; + if (pb(1) == '1') { + REPORT_COMMAND(screen_start_pending_mode); + screen->pending_mode.activated_at = monotonic(); + } else { + REPORT_COMMAND(screen_stop_pending_mode); + screen->pending_mode.activated_at = 0; + } + } else pending_escape_code(screen, DCS, ST); +} + +static void +pending_csi(Screen *screen, PyObject *dump_callback DUMP_UNUSED) { + if (screen->parser_buf_pos == 5 && pb(0) == '?' && pb(1) == '2' && pb(2) == '0' && pb(3) == '2' && pb(4) == '6' && (pb(5) == 'h' || pb(5) == 'l')) { + if (pb(5) == 'h') { + REPORT_COMMAND(screen_set_mode, 2026, 1); + screen->pending_mode.activated_at = monotonic(); + } else { + REPORT_COMMAND(screen_reset_mode, 2026, 1); + screen->pending_mode.activated_at = 0; + } + } else pending_escape_code(screen, CSI, pb(screen->parser_buf_pos)); +} + +#undef pb + +static size_t +queue_pending_bytes(Screen *screen, const uint8_t *buf, size_t len, PyObject *dump_callback DUMP_UNUSED) { + unsigned int i; + decode_loop(pending, if (!screen->pending_mode.activated_at) goto end); end: - pm.state = state; - return pos; -#undef COPY -#undef COPY_STOP_BUF -#undef stop_pending_mode -#undef start_pending_mode -#undef pm + return i; } static inline void @@ -1433,30 +1430,31 @@ do_parse_bytes(Screen *screen, const uint8_t *read_buf, const size_t read_buf_sz case PARSE_PENDING: _parse_bytes(screen, screen->pending_mode.buf, screen->pending_mode.used, dump_callback); - screen->pending_mode.used = 0; screen->pending_mode.state = 0; + screen->pending_mode.used = 0; screen->pending_mode.activated_at = 0; // ignore any pending starts in the pending bytes state = START; break; case PARSE_READ_BUF: - screen->pending_mode.activated_at = 0; screen->pending_mode.state = 0; + screen->pending_mode.activated_at = 0; read_buf_pos += _parse_bytes_watching_for_pending(screen, read_buf + read_buf_pos, read_buf_sz - read_buf_pos, dump_callback); state = START; break; case QUEUE_PENDING: { - if (screen->pending_mode.capacity - screen->pending_mode.used < read_buf_sz + sizeof(screen->pending_mode.stop_buf)) { + const size_t needed_space = read_buf_sz * 2; + if (screen->pending_mode.capacity - screen->pending_mode.used < needed_space) { if (screen->pending_mode.capacity >= READ_BUF_SZ) { // Too much pending data, drain it screen->pending_mode.activated_at = 0; state = START; break; } - screen->pending_mode.capacity = MAX(screen->pending_mode.capacity * 2, screen->pending_mode.used + read_buf_sz + sizeof(screen->pending_mode.stop_buf)); + screen->pending_mode.capacity = MAX(screen->pending_mode.capacity * 2, screen->pending_mode.used + needed_space); screen->pending_mode.buf = realloc(screen->pending_mode.buf, screen->pending_mode.capacity); if (!screen->pending_mode.buf) fatal("Out of memory"); } - read_buf_pos += _queue_pending_bytes(screen, read_buf + read_buf_pos, read_buf_sz - read_buf_pos, dump_callback); + read_buf_pos += queue_pending_bytes(screen, read_buf + read_buf_pos, read_buf_sz - read_buf_pos, dump_callback); state = START; } break; } diff --git a/kitty/screen.h b/kitty/screen.h index 36090bd98..bece5ae39 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -116,11 +116,9 @@ typedef struct { CursorRenderInfo cursor_render_info; struct { - size_t capacity, used, stop_buf_pos; + size_t capacity, used; uint8_t *buf; monotonic_t activated_at, wait_time; - int state; - uint8_t stop_buf[32]; } pending_mode; DisableLigature disable_ligatures; PyObject *marker; diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index af6298c28..c232eca61 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -363,6 +363,7 @@ class TestParser(BaseTest): pb('\033P=1sxyz;.;\033\\''\033P=2skjf".,>