Use the main VT parser in pending mode as well
Should get much closer semantics in the two cases and its nice not to have an extra mini VT parser for pending mode. There is a performance hit in pending mode, since now the pending mode bytes are round tripped via utf-8 decoding/encoding, but its worth it for the code simplification.
This commit is contained in:
parent
d6b6d3f59f
commit
e6a17f78b6
202
kitty/parser.c
202
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);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
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 }
|
||||
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 {
|
||||
COPY_STOP_BUF(false); COPY(ch);
|
||||
REPORT_COMMAND(screen_stop_pending_mode);
|
||||
screen->pending_mode.activated_at = 0;
|
||||
}
|
||||
state = NORMAL;
|
||||
} 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 {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -363,6 +363,7 @@ class TestParser(BaseTest):
|
||||
pb('\033P=1sxyz;.;\033\\''\033P=2skjf".,><?_+)98\033\\', ('screen_start_pending_mode',), ('screen_stop_pending_mode',))
|
||||
pb('\033P=1s\033\\f\033P=1s\033\\', ('screen_start_pending_mode',), ('screen_start_pending_mode',))
|
||||
pb('\033P=2s\033\\', ('screen_stop_pending_mode',), ('draw', 'f'))
|
||||
pb('\033P=1s\033\\XXX\033P=2s\033\\', ('screen_start_pending_mode',), ('screen_stop_pending_mode',), ('draw', 'XXX'))
|
||||
|
||||
pb('\033[?2026hXXX\033[?2026l', ('screen_set_mode', 2026, 1), ('screen_reset_mode', 2026, 1), ('draw', 'XXX'))
|
||||
pb('\033[?2026h\033[32ma\033[?2026l', ('screen_set_mode', 2026, 1), ('screen_reset_mode', 2026, 1), ('select_graphic_rendition', '32 '), ('draw', 'a'))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user