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
204
kitty/parser.c
204
kitty/parser.c
@ -151,10 +151,10 @@ _report_params(PyObject *dump_callback, const char *name, int *params, unsigned
|
|||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Normal mode {{{
|
// Normal mode {{{
|
||||||
static inline void
|
static void
|
||||||
screen_nel(Screen *screen) { screen_carriage_return(screen); screen_linefeed(screen); }
|
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) {
|
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;
|
#define CALL_SCREEN_HANDLER(name) REPORT_COMMAND(name); name(screen); break;
|
||||||
switch(ch) {
|
switch(ch) {
|
||||||
@ -203,7 +203,7 @@ dispatch_normal_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dum
|
|||||||
} // }}}
|
} // }}}
|
||||||
|
|
||||||
// Esc mode {{{
|
// Esc mode {{{
|
||||||
static inline void
|
static void
|
||||||
dispatch_esc_mode_char(Screen *screen, uint32_t ch, PyObject DUMP_UNUSED *dump_callback) {
|
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_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);
|
#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();
|
screen->pending_mode.activated_at = monotonic();
|
||||||
REPORT_COMMAND(screen_start_pending_mode);
|
REPORT_COMMAND(screen_start_pending_mode);
|
||||||
} else {
|
} 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.
|
// for how stop is detected while in pending mode.
|
||||||
REPORT_ERROR("Pending mode stop command issued while not in pending mode");
|
REPORT_ERROR("Pending mode stop command issued while not in pending mode");
|
||||||
REPORT_COMMAND(screen_stop_pending_mode);
|
REPORT_COMMAND(screen_stop_pending_mode);
|
||||||
@ -1306,110 +1306,107 @@ FLUSH_DRAW;
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static void
|
||||||
_queue_pending_bytes(Screen *screen, const uint8_t *buf, size_t len, PyObject *dump_callback DUMP_UNUSED) {
|
write_pending_char(Screen *screen, uint32_t ch) {
|
||||||
size_t pos = 0;
|
screen->pending_mode.used += encode_utf8(ch, (char*)screen->pending_mode.buf + screen->pending_mode.used);
|
||||||
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;}
|
|
||||||
|
|
||||||
while (pos < len) {
|
static void
|
||||||
uint8_t ch = buf[pos++];
|
pending_normal_mode_char(Screen *screen, uint32_t ch, PyObject *dump_callback UNUSED) {
|
||||||
switch(state) {
|
switch(ch) {
|
||||||
case NORMAL:
|
case ESC: case CSI: case OSC: case DCS: case APC: case PM:
|
||||||
if (ch == ESC) state = MAYBE_DCS_OR_CSI;
|
SET_STATE(ch); break;
|
||||||
else COPY(ch);
|
default:
|
||||||
break;
|
write_pending_char(screen, 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; }
|
static void
|
||||||
else {
|
pending_esc_mode_char(Screen *screen, uint32_t ch, PyObject *dump_callback UNUSED) {
|
||||||
state = NORMAL;
|
if (screen->parser_buf_pos > 0) {
|
||||||
COPY(0x1b); COPY('P'); COPY(ch);
|
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;
|
break;
|
||||||
case IN_CSI:
|
default:
|
||||||
if (ch == '?') { state = EXPECTING_CSI_DATA; pm.stop_buf_pos = 0; }
|
write_pending_char(screen, ESC); write_pending_char(screen, ch);
|
||||||
else {
|
SET_STATE(0); break;
|
||||||
state = NORMAL;
|
|
||||||
COPY(0x1b); COPY('['); COPY(ch);
|
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case EXPECTING_CSI_DATA:
|
|
||||||
if (ch == 'h' || ch == 'l') {
|
#define pb(i) screen->parser_buf[i]
|
||||||
if (pm.stop_buf_pos == 4 && memcmp(pm.stop_buf, "2026", 4) == 0) {
|
static void
|
||||||
if (ch == 'h') { start_pending_mode } else { stop_pending_mode }
|
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 {
|
} 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 {
|
} else {
|
||||||
pm.stop_buf[pm.stop_buf_pos++] = ch;
|
REPORT_COMMAND(screen_reset_mode, 2026, 1);
|
||||||
if (pm.stop_buf_pos >= sizeof(pm.stop_buf)) {
|
screen->pending_mode.activated_at = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} 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:
|
end:
|
||||||
pm.state = state;
|
return i;
|
||||||
return pos;
|
|
||||||
#undef COPY
|
|
||||||
#undef COPY_STOP_BUF
|
|
||||||
#undef stop_pending_mode
|
|
||||||
#undef start_pending_mode
|
|
||||||
#undef pm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
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:
|
case PARSE_PENDING:
|
||||||
_parse_bytes(screen, screen->pending_mode.buf, screen->pending_mode.used, dump_callback);
|
_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
|
screen->pending_mode.activated_at = 0; // ignore any pending starts in the pending bytes
|
||||||
state = START;
|
state = START;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PARSE_READ_BUF:
|
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);
|
read_buf_pos += _parse_bytes_watching_for_pending(screen, read_buf + read_buf_pos, read_buf_sz - read_buf_pos, dump_callback);
|
||||||
state = START;
|
state = START;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case QUEUE_PENDING: {
|
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) {
|
if (screen->pending_mode.capacity >= READ_BUF_SZ) {
|
||||||
// Too much pending data, drain it
|
// Too much pending data, drain it
|
||||||
screen->pending_mode.activated_at = 0;
|
screen->pending_mode.activated_at = 0;
|
||||||
state = START;
|
state = START;
|
||||||
break;
|
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);
|
screen->pending_mode.buf = realloc(screen->pending_mode.buf, screen->pending_mode.capacity);
|
||||||
if (!screen->pending_mode.buf) fatal("Out of memory");
|
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;
|
state = START;
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,11 +116,9 @@ typedef struct {
|
|||||||
CursorRenderInfo cursor_render_info;
|
CursorRenderInfo cursor_render_info;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
size_t capacity, used, stop_buf_pos;
|
size_t capacity, used;
|
||||||
uint8_t *buf;
|
uint8_t *buf;
|
||||||
monotonic_t activated_at, wait_time;
|
monotonic_t activated_at, wait_time;
|
||||||
int state;
|
|
||||||
uint8_t stop_buf[32];
|
|
||||||
} pending_mode;
|
} pending_mode;
|
||||||
DisableLigature disable_ligatures;
|
DisableLigature disable_ligatures;
|
||||||
PyObject *marker;
|
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=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=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=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[?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'))
|
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