mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-11-11 01:28:19 +03:00
Avoid a double parse for pending mode
This commit is contained in:
parent
a4ca143fc5
commit
f42b49e597
@ -17,7 +17,7 @@
|
||||
#define EXTENDED_OSC_SENTINEL ESC
|
||||
#define PARSER_BUF_SZ (8u * 1024u)
|
||||
#define PENDING_BUF_INCREMENT (16u * 1024u)
|
||||
#define PENDING_BUF_LIMIT (32u * READ_BUF_SZ)
|
||||
#define PENDING_BUF_LIMIT READ_BUF_SZ
|
||||
|
||||
// Macros {{{
|
||||
|
||||
@ -142,6 +142,27 @@ typedef enum VTEState {
|
||||
VTE_NORMAL, VTE_ESC = ESC, VTE_CSI = ESC_CSI, VTE_OSC = ESC_OSC, VTE_DCS = ESC_DCS, VTE_APC = ESC_APC, VTE_PM = ESC_PM
|
||||
} VTEState;
|
||||
|
||||
static inline const char*
|
||||
vte_state_name(VTEState s) {
|
||||
switch(s) {
|
||||
case VTE_NORMAL: return "VTE_NORMAL";
|
||||
case VTE_ESC: return "VTE_ESC";
|
||||
case VTE_CSI: return "VTE_CSI";
|
||||
case VTE_OSC: return "VTE_OSC";
|
||||
case VTE_DCS: return "VTE_DCS";
|
||||
case VTE_APC: return "VTE_APC";
|
||||
case VTE_PM: return "VTE_PM";
|
||||
}
|
||||
static char buf[16];
|
||||
snprintf(buf, sizeof(buf), "VTE_0x%x", s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
typedef struct PendingHeader{
|
||||
VTEState which: 8;
|
||||
uint32_t size: 24;
|
||||
} PendingHeader;
|
||||
|
||||
typedef struct PS {
|
||||
id_type window_id;
|
||||
|
||||
@ -155,8 +176,7 @@ typedef struct PS {
|
||||
struct {
|
||||
monotonic_t activated_at, wait_time;
|
||||
unsigned stop_escape_code_type;
|
||||
size_t capacity, used;
|
||||
struct { size_t start, length; } current;
|
||||
size_t capacity, used, current;
|
||||
uint8_t *buf;
|
||||
} pending_mode;
|
||||
|
||||
@ -166,8 +186,9 @@ typedef struct PS {
|
||||
const uint8_t *input_data;
|
||||
size_t input_sz, input_pos;
|
||||
monotonic_t now;
|
||||
uint8_t parser_buf[PARSER_BUF_SZ + 8]; // +8 to ensure we can always zero terminate
|
||||
bool draining_pending;
|
||||
uint8_t *parser_buf;
|
||||
|
||||
uint8_t _parser_buf[PARSER_BUF_SZ + 8]; // +8 to ensure we can always zero terminate
|
||||
} PS;
|
||||
|
||||
// Normal mode {{{
|
||||
@ -193,9 +214,8 @@ draw_byte(PS *self, uint8_t b) {
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_normal_mode_byte(PS *self) {
|
||||
dispatch_normal_mode_byte(PS *self, uint8_t ch, bool from_within_a_csi UNUSED) {
|
||||
#define CALL_SCREEN_HANDLER(name) REPORT_COMMAND(name); name(self->screen); break;
|
||||
uint8_t ch = self->input_data[self->input_pos++];
|
||||
switch(ch) {
|
||||
case BEL:
|
||||
CALL_SCREEN_HANDLER(screen_bell);
|
||||
@ -243,25 +263,35 @@ dispatch_normal_mode_byte(PS *self) {
|
||||
static void
|
||||
screen_nel(Screen *screen) { screen_carriage_return(screen); screen_linefeed(screen); }
|
||||
|
||||
static bool
|
||||
accumulate_esc(PS *self) {
|
||||
uint8_t ch = self->input_data[self->input_pos++];
|
||||
self->parser_buf[self->parser_buf_pos++] = ch;
|
||||
if (self->parser_buf_pos > 1) return true;
|
||||
switch (ch) {
|
||||
default:
|
||||
return true;
|
||||
IS_ESCAPED_CHAR:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_esc_mode_byte(PS *self) {
|
||||
dispatch_esc(PS *self) {
|
||||
#define CALL_ED(name) REPORT_COMMAND(name); name(self->screen); SET_STATE(NORMAL);
|
||||
#define CALL_ED1(name, ch) REPORT_COMMAND(name, ch); name(self->screen, ch); SET_STATE(NORMAL);
|
||||
#define CALL_ED2(name, a, b) REPORT_COMMAND(name, a, b); name(self->screen, a, b); SET_STATE(NORMAL);
|
||||
uint8_t ch = self->input_data[self->input_pos++];
|
||||
uint8_t prev_ch, ch;
|
||||
switch(self->parser_buf_pos) {
|
||||
case 0:
|
||||
case 1:
|
||||
ch = self->parser_buf[0];
|
||||
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 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 ESC_RIS:
|
||||
CALL_ED(screen_reset); break;
|
||||
case ESC_IND:
|
||||
@ -280,16 +310,15 @@ dispatch_esc_mode_byte(PS *self) {
|
||||
CALL_ED(screen_normal_keypad_mode); break;
|
||||
case ESC_DECKPAM:
|
||||
CALL_ED(screen_alternate_keypad_mode); break;
|
||||
IS_ESCAPED_CHAR:
|
||||
self->parser_buf[self->parser_buf_pos++] = ch;
|
||||
break;
|
||||
default:
|
||||
REPORT_ERROR("%s0x%x", "Unknown char after ESC: ", ch);
|
||||
SET_STATE(NORMAL); break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch(self->parser_buf[0]) {
|
||||
prev_ch = self->parser_buf[0];
|
||||
ch = self->parser_buf[1];
|
||||
switch(prev_ch) {
|
||||
case '%':
|
||||
switch(ch) {
|
||||
case '@':
|
||||
@ -331,7 +360,7 @@ dispatch_esc_mode_byte(PS *self) {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
REPORT_ERROR("Unhandled charset related escape code: 0x%x 0x%x", self->parser_buf[0], ch); break;
|
||||
REPORT_ERROR("Unhandled charset related escape code: 0x%x 0x%x", prev_ch, ch); break;
|
||||
}
|
||||
SET_STATE(NORMAL);
|
||||
break;
|
||||
@ -377,7 +406,7 @@ accumulate_osc(PS *self) {
|
||||
}
|
||||
/* fallthrough */
|
||||
default:
|
||||
if (!self->draining_pending && self->parser_buf_pos >= PARSER_BUF_SZ - 1) {
|
||||
if (self->parser_buf_pos >= PARSER_BUF_SZ - 1) {
|
||||
if (handle_extended_osc_code(self)) self->extended_osc_code = true;
|
||||
else REPORT_ERROR("OSC sequence too long (> %d bytes) truncating.", PARSER_BUF_SZ);
|
||||
return true;
|
||||
@ -546,35 +575,6 @@ dispatch_osc(PS *self) {
|
||||
|
||||
// DCS {{{
|
||||
|
||||
static bool
|
||||
accumulate_dcs(PS *self) {
|
||||
uint8_t ch = self->input_data[self->input_pos++];
|
||||
switch(ch) {
|
||||
case NUL:
|
||||
case DEL:
|
||||
break;
|
||||
case ESC:
|
||||
START_ALLOW_CASE_RANGE
|
||||
case 32 ... 126:
|
||||
END_ALLOW_CASE_RANGE
|
||||
if (self->parser_buf_pos > 0 && self->parser_buf[self->parser_buf_pos-1] == ESC) {
|
||||
if (ch == '\\') { self->parser_buf_pos--; return true; }
|
||||
REPORT_ERROR("DCS sequence contained ESC without trailing \\ at pos: %u ignoring the sequence", self->parser_buf_pos);
|
||||
SET_STATE(ESC); return false;
|
||||
}
|
||||
if (self->parser_buf_pos >= PARSER_BUF_SZ - 1) {
|
||||
REPORT_ERROR("DCS sequence too long, truncating.");
|
||||
return true;
|
||||
}
|
||||
self->parser_buf[self->parser_buf_pos++] = ch;
|
||||
break;
|
||||
default:
|
||||
REPORT_ERROR("DCS sequence contained non-printable character: 0x%x ignoring the sequence", ch);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
startswith(const uint8_t *string, ssize_t sz, const char *prefix, ssize_t l) {
|
||||
if (sz < l) return false;
|
||||
@ -725,7 +725,8 @@ END_ALLOW_CASE_RANGE
|
||||
case '|':
|
||||
case '}':
|
||||
case '~':
|
||||
self->parser_buf[self->parser_buf_pos] = ch;
|
||||
ENSURE_SPACE;
|
||||
self->parser_buf[self->parser_buf_pos++] = ch;
|
||||
return true;
|
||||
case BEL:
|
||||
case BS:
|
||||
@ -736,8 +737,7 @@ END_ALLOW_CASE_RANGE
|
||||
case CR:
|
||||
case SO:
|
||||
case SI:
|
||||
self->input_pos--;
|
||||
dispatch_normal_mode_byte(self);
|
||||
dispatch_normal_mode_byte(self, ch, true);
|
||||
break;
|
||||
case NUL:
|
||||
case DEL:
|
||||
@ -1031,8 +1031,8 @@ dispatch_csi(PS *self) {
|
||||
}
|
||||
|
||||
char start_modifier = 0, end_modifier = 0;
|
||||
uint8_t *buf = self->parser_buf, code = self->parser_buf[self->parser_buf_pos];
|
||||
unsigned int num = self->parser_buf_pos, start, i, num_params=0;
|
||||
uint8_t *buf = self->parser_buf, code = self->parser_buf[self->parser_buf_pos - 1];
|
||||
unsigned int num = self->parser_buf_pos - 1, start, i, num_params=0;
|
||||
static int params[MAX_PARAMS] = {0}, p1, p2;
|
||||
bool private;
|
||||
if (buf[0] == '>' || buf[0] == '<' || buf[0] == '?' || buf[0] == '!' || buf[0] == '=') {
|
||||
@ -1350,7 +1350,7 @@ accumulate_oth(PS *self) {
|
||||
#define dispatch_single_byte(dispatch, watch_for_pending) { \
|
||||
switch(self->vte_state) { \
|
||||
case VTE_ESC: \
|
||||
dispatch##_esc_mode_byte(self); \
|
||||
if (accumulate_esc(self)) dispatch##_esc(self); \
|
||||
break; \
|
||||
case VTE_CSI: \
|
||||
if (accumulate_csi(self)) { dispatch##_csi(self); SET_STATE(NORMAL); watch_for_pending; } \
|
||||
@ -1371,16 +1371,18 @@ accumulate_oth(PS *self) {
|
||||
if (accumulate_oth(self)) { dispatch##_pm(self); SET_STATE(NORMAL); } \
|
||||
break; \
|
||||
case VTE_DCS: \
|
||||
if (accumulate_dcs(self)) { dispatch##_dcs(self); SET_STATE(NORMAL); watch_for_pending; } \
|
||||
if (self->vte_state == ESC) { self->input_pos--; dispatch##_esc_mode_byte(self); } \
|
||||
if (accumulate_oth(self)) { dispatch##_dcs(self); SET_STATE(NORMAL); watch_for_pending; } \
|
||||
break; \
|
||||
case VTE_NORMAL: \
|
||||
dispatch##_normal_mode_byte(self); \
|
||||
dispatch##_normal_mode_byte(self, self->input_data[self->input_pos++], false); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
|
||||
// Pending mode {{{
|
||||
#define pending_header ((PendingHeader*)&self->pending_mode.buf[self->pending_mode.current])
|
||||
#define PENDING_END_SENTINEL 0xff
|
||||
|
||||
static void
|
||||
ensure_pending_space(PS *self, size_t amt) {
|
||||
if (self->pending_mode.capacity < self->pending_mode.used + amt) {
|
||||
@ -1392,73 +1394,138 @@ ensure_pending_space(PS *self, size_t amt) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
current_pending_is(PS *self, VTEState state) {
|
||||
return self->pending_mode.current < self->pending_mode.used && pending_header->which == state;
|
||||
}
|
||||
|
||||
static void
|
||||
pending_normal_mode_byte(PS *self) {
|
||||
uint8_t ch = self->input_data[self->input_pos++];
|
||||
align_pending_current(PS *self) {
|
||||
// ensure PendingHeader is aligned
|
||||
size_t off = self->pending_mode.current % sizeof(PendingHeader);
|
||||
if (off) self->pending_mode.current += sizeof(PendingHeader) - off; // 4 byte align
|
||||
}
|
||||
|
||||
static void
|
||||
new_pending_state(PS *self, VTEState state) {
|
||||
if (self->pending_mode.current < self->pending_mode.used) {
|
||||
pending_header->size = self->pending_mode.used - self->pending_mode.current;
|
||||
if (pending_header->size > sizeof(PendingHeader)) {
|
||||
self->pending_mode.current = self->pending_mode.used;
|
||||
align_pending_current(self);
|
||||
}
|
||||
self->pending_mode.used = self->pending_mode.current;
|
||||
}
|
||||
ensure_pending_space(self, sizeof(PendingHeader) + 128);
|
||||
pending_header->which = state;
|
||||
pending_header->size = sizeof(PendingHeader);
|
||||
self->pending_mode.used += sizeof(PendingHeader);
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch_pending_bytes(PS *self) {
|
||||
self->pending_mode.current = 0;
|
||||
uint8_t *orig = self->parser_buf; size_t opos = self->parser_buf_pos;
|
||||
while (pending_header->which != PENDING_END_SENTINEL) {
|
||||
self->parser_buf = self->pending_mode.buf + self->pending_mode.current + sizeof(PendingHeader);
|
||||
self->parser_buf_pos = pending_header->size - sizeof(PendingHeader);
|
||||
if (self->parser_buf_pos) {
|
||||
switch (pending_header->which) {
|
||||
case VTE_ESC: dispatch_esc(self); break;
|
||||
case VTE_CSI: dispatch_csi(self); break;
|
||||
case VTE_OSC: dispatch_osc(self); break;
|
||||
case VTE_DCS: dispatch_dcs(self); break;
|
||||
case VTE_APC: dispatch_apc(self); break;
|
||||
case VTE_PM: dispatch_pm(self); break;
|
||||
case VTE_NORMAL:
|
||||
for (unsigned i = 0; i < self->parser_buf_pos; i++) {
|
||||
dispatch_normal_mode_byte(self, self->parser_buf[i], false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
self->pending_mode.current += pending_header->size;
|
||||
align_pending_current(self);
|
||||
}
|
||||
self->pending_mode.current = 0;
|
||||
self->pending_mode.used = 0;
|
||||
self->pending_mode.activated_at = 0; // ignore any pending starts in the pending bytes
|
||||
self->parser_buf = orig; self->parser_buf_pos = opos;
|
||||
if (self->pending_mode.capacity > PENDING_BUF_LIMIT + PENDING_BUF_INCREMENT) {
|
||||
self->pending_mode.capacity = PENDING_BUF_LIMIT;
|
||||
self->pending_mode.buf = realloc(self->pending_mode.buf, self->pending_mode.capacity);
|
||||
if (!self->pending_mode.buf) fatal("Out of memory");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
ensure_pending_state(PS *self, VTEState state) {
|
||||
if (!current_pending_is(self, state)) new_pending_state(self, state);
|
||||
}
|
||||
|
||||
static void
|
||||
pending_add_to_current(PS *self, uint8_t* data, size_t size) {
|
||||
ensure_pending_space(self, size);
|
||||
memcpy(self->pending_mode.buf + self->pending_mode.used, data, size);
|
||||
self->pending_mode.used += size;
|
||||
}
|
||||
|
||||
static void
|
||||
pending_normal_mode_byte(PS *self, uint8_t ch, bool from_within_a_csi) {
|
||||
switch(ch) {
|
||||
case ESC:
|
||||
SET_STATE(ESC); break;
|
||||
default:
|
||||
ensure_pending_space(self, 1);
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ch;
|
||||
if (!from_within_a_csi) {
|
||||
ensure_pending_state(self, VTE_NORMAL);
|
||||
pending_add_to_current(self, &ch, 1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define pending_add(...) pending_add_to_current(self, (uint8_t[]){__VA_ARGS__}, sizeof((uint8_t[]){__VA_ARGS__}))
|
||||
|
||||
static void
|
||||
pending_esc_mode_byte(PS *self) {
|
||||
uint8_t ch = self->input_data[self->input_pos++];
|
||||
if (self->parser_buf_pos > 0) {
|
||||
ensure_pending_space(self, 3);
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ESC;
|
||||
self->pending_mode.buf[self->pending_mode.used++] = self->parser_buf[self->parser_buf_pos - 1];
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ch;
|
||||
SET_STATE(NORMAL);
|
||||
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;
|
||||
IS_ESCAPED_CHAR:
|
||||
self->parser_buf[self->parser_buf_pos++] = ch;
|
||||
break;
|
||||
default:
|
||||
ensure_pending_space(self, 2);
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ESC;
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ch;
|
||||
SET_STATE(NORMAL); break;
|
||||
pending_esc(PS *self) {
|
||||
if (self->parser_buf_pos == 1) {
|
||||
switch(self->parser_buf[0]) {
|
||||
case ESC_DCS: SET_STATE(DCS); return;
|
||||
case ESC_OSC: SET_STATE(OSC); return;
|
||||
case ESC_CSI: SET_STATE(CSI); return;
|
||||
case ESC_APC: SET_STATE(APC); return;
|
||||
case ESC_PM: SET_STATE(PM); return;
|
||||
}
|
||||
}
|
||||
ensure_pending_state(self, VTE_ESC);
|
||||
if (self->parser_buf_pos == 1) pending_add(self->parser_buf[0]);
|
||||
else pending_add(self->parser_buf[0], self->parser_buf[1]);
|
||||
SET_STATE(NORMAL);
|
||||
}
|
||||
|
||||
#define pb(i) self->parser_buf[i]
|
||||
|
||||
static void
|
||||
pending_escape_code(PS *self, char_type start_ch, char_type end_ch) {
|
||||
pending_escape_code(PS *self, VTEState which) {
|
||||
ensure_pending_space(self, 4 + self->parser_buf_pos);
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ESC;
|
||||
self->pending_mode.buf[self->pending_mode.used++] = start_ch;
|
||||
memcpy(self->pending_mode.buf + self->pending_mode.used, self->parser_buf, self->parser_buf_pos);
|
||||
self->pending_mode.used += self->parser_buf_pos;
|
||||
if (start_ch != ESC_CSI) self->pending_mode.buf[self->pending_mode.used++] = ESC;
|
||||
self->pending_mode.buf[self->pending_mode.used++] = end_ch;
|
||||
new_pending_state(self, which);
|
||||
pending_add_to_current(self, self->parser_buf, self->parser_buf_pos);
|
||||
}
|
||||
|
||||
static void pending_pm(PS *self) { pending_escape_code(self, ESC_PM, ESC_ST); }
|
||||
static void pending_apc(PS *self) { pending_escape_code(self, ESC_APC, ESC_ST); }
|
||||
static void pending_pm(PS *self) { pending_escape_code(self, VTE_PM); }
|
||||
static void pending_apc(PS *self) { pending_escape_code(self, VTE_APC); }
|
||||
static void pending_osc(PS *self) {
|
||||
const bool is_extended = is_extended_osc(self);
|
||||
pending_escape_code(self, VTE_OSC);
|
||||
if (is_extended) continue_osc_52(self);
|
||||
}
|
||||
|
||||
static void
|
||||
pending_osc(PS *self) {
|
||||
const bool extended = is_extended_osc(self);
|
||||
pending_escape_code(self, ESC_OSC, ESC_ST);
|
||||
if (extended) continue_osc_52(self);
|
||||
stop_pending_mode(PS *self, unsigned stop_escape_code_type) {
|
||||
self->pending_mode.stop_escape_code_type = stop_escape_code_type;
|
||||
self->pending_mode.activated_at = 0;
|
||||
new_pending_state(self, PENDING_END_SENTINEL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1468,71 +1535,44 @@ pending_dcs(PS *self) {
|
||||
if (pb(1) == '1') {
|
||||
REPORT_COMMAND(screen_start_pending_mode);
|
||||
self->pending_mode.activated_at = monotonic();
|
||||
} else {
|
||||
self->pending_mode.stop_escape_code_type = ESC_DCS;
|
||||
self->pending_mode.activated_at = 0;
|
||||
}
|
||||
} else pending_escape_code(self, ESC_DCS, ESC_ST);
|
||||
} else stop_pending_mode(self, ESC_DCS);
|
||||
} else pending_escape_code(self, VTE_DCS);
|
||||
}
|
||||
|
||||
static void
|
||||
pending_csi(PS *self) {
|
||||
if (self->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 (self->parser_buf_pos == 6 && 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);
|
||||
self->pending_mode.activated_at = monotonic();
|
||||
} else {
|
||||
self->pending_mode.activated_at = 0;
|
||||
self->pending_mode.stop_escape_code_type = ESC_CSI;
|
||||
}
|
||||
} else pending_escape_code(self, ESC_CSI, pb(self->parser_buf_pos));
|
||||
} else stop_pending_mode(self, ESC_CSI);
|
||||
} else pending_escape_code(self, VTE_CSI);
|
||||
}
|
||||
#undef pb
|
||||
|
||||
static void
|
||||
queue_pending_bytes(PS *self) {
|
||||
#ifdef DUMP_COMMANDS
|
||||
self->pending_mode.stop_escape_code_type = 0;
|
||||
#endif
|
||||
while (self->input_pos < self->input_sz) {
|
||||
dispatch_single_byte(pending, if (!self->pending_mode.activated_at) goto end);
|
||||
dispatch_single_byte(pending, if (!self->pending_mode.activated_at) goto end;);
|
||||
}
|
||||
end:
|
||||
FLUSH_DRAW;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_pending_bytes(PS *self) {
|
||||
const uint8_t *orig_input_data = self->input_data; size_t orig_input_sz = self->input_sz, orig_input_pos = self->input_pos;
|
||||
self->input_data = self->pending_mode.buf; self->input_sz = self->pending_mode.used; self->input_pos = 0;
|
||||
self->draining_pending = true;
|
||||
while (self->input_pos < self->input_sz) {
|
||||
dispatch_single_byte(dispatch, ;);
|
||||
}
|
||||
self->draining_pending = false;
|
||||
self->input_data = orig_input_data; self->input_sz = orig_input_sz; self->input_pos = orig_input_pos;
|
||||
static bool
|
||||
pending_over_limits(PS *self) {
|
||||
return self->pending_mode.used > PENDING_BUF_LIMIT || self->pending_mode.activated_at + self->pending_mode.wait_time < self->now;
|
||||
}
|
||||
|
||||
static void
|
||||
dump_partial_escape_code_to_pending(PS *self) {
|
||||
ensure_pending_space(self, self->parser_buf_pos + 2);
|
||||
if (self->parser_buf_pos) {
|
||||
switch(self->vte_state) {
|
||||
case VTE_NORMAL: case VTE_ESC: break;
|
||||
case VTE_CSI: case VTE_OSC: case VTE_DCS: case VTE_APC: case VTE_PM:
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ESC;
|
||||
self->pending_mode.buf[self->pending_mode.used++] = self->vte_state;
|
||||
break;
|
||||
}
|
||||
memcpy(self->pending_mode.buf + self->pending_mode.used, self->parser_buf, self->parser_buf_pos);
|
||||
self->pending_mode.used += self->parser_buf_pos;
|
||||
} else if (self->vte_state == VTE_ESC) {
|
||||
self->pending_mode.buf[self->pending_mode.used++] = ESC;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
static void
|
||||
parse_bytes_watching_for_pending(PS *self) {
|
||||
while (self->input_pos < self->input_sz) {
|
||||
dispatch_single_byte(dispatch, if (self->pending_mode.activated_at) goto end);
|
||||
dispatch_single_byte(dispatch, if (self->pending_mode.activated_at) goto end;);
|
||||
}
|
||||
end:
|
||||
FLUSH_DRAW;
|
||||
@ -1540,61 +1580,26 @@ FLUSH_DRAW;
|
||||
|
||||
static void
|
||||
do_parse_vt(PS *self) {
|
||||
enum STATE {START, PARSE_PENDING, PARSE_READ_BUF, QUEUE_PENDING};
|
||||
enum STATE state = START;
|
||||
VTEState vte_state_at_start_of_pending = VTE_NORMAL;
|
||||
|
||||
do {
|
||||
switch(state) {
|
||||
case START:
|
||||
if (self->pending_mode.activated_at) {
|
||||
if (self->pending_mode.activated_at + self->pending_mode.wait_time < self->now || self->pending_mode.capacity >= PENDING_BUF_LIMIT) {
|
||||
dump_partial_escape_code_to_pending(self);
|
||||
self->pending_mode.activated_at = 0;
|
||||
state = START;
|
||||
} else state = QUEUE_PENDING;
|
||||
} else {
|
||||
state = self->pending_mode.used ? PARSE_PENDING : PARSE_READ_BUF;
|
||||
}
|
||||
break;
|
||||
|
||||
case PARSE_PENDING:
|
||||
self->vte_state = vte_state_at_start_of_pending;
|
||||
vte_state_at_start_of_pending = VTE_NORMAL;
|
||||
parse_pending_bytes(self);
|
||||
self->pending_mode.used = 0;
|
||||
self->pending_mode.activated_at = 0; // ignore any pending starts in the pending bytes
|
||||
if (self->pending_mode.capacity > READ_BUF_SZ + PENDING_BUF_INCREMENT) {
|
||||
self->pending_mode.capacity = READ_BUF_SZ;
|
||||
self->pending_mode.buf = realloc(self->pending_mode.buf, self->pending_mode.capacity);
|
||||
if (!self->pending_mode.buf) fatal("Out of memory");
|
||||
if (self->pending_mode.activated_at) {
|
||||
queue_pending_bytes(self);
|
||||
if (self->pending_mode.activated_at && pending_over_limits(self)) stop_pending_mode(self, 0);
|
||||
if (!self->pending_mode.activated_at) {
|
||||
if (self->pending_mode.used) {
|
||||
VTEState before = self->vte_state;
|
||||
dispatch_pending_bytes(self);
|
||||
self->vte_state = before;
|
||||
}
|
||||
#ifdef DUMP_COMMANDS
|
||||
if (self->pending_mode.stop_escape_code_type) {
|
||||
if (self->pending_mode.stop_escape_code_type == ESC_DCS) { REPORT_COMMAND(screen_stop_pending_mode); }
|
||||
else if (self->pending_mode.stop_escape_code_type == ESC_CSI) { REPORT_COMMAND(screen_reset_mode, 2026, 1); }
|
||||
self->pending_mode.stop_escape_code_type = 0;
|
||||
}
|
||||
state = START;
|
||||
break;
|
||||
|
||||
case PARSE_READ_BUF:
|
||||
self->pending_mode.activated_at = 0;
|
||||
parse_bytes_watching_for_pending(self);
|
||||
state = START;
|
||||
break;
|
||||
|
||||
case QUEUE_PENDING: {
|
||||
self->pending_mode.stop_escape_code_type = 0;
|
||||
if (self->pending_mode.used >= READ_BUF_SZ) {
|
||||
dump_partial_escape_code_to_pending(self);
|
||||
self->pending_mode.activated_at = 0;
|
||||
state = START;
|
||||
break;
|
||||
}
|
||||
if (!self->pending_mode.used) vte_state_at_start_of_pending = self->vte_state;
|
||||
queue_pending_bytes(self);
|
||||
state = START;
|
||||
} break;
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
parse_bytes_watching_for_pending(self);
|
||||
}
|
||||
} while(self->input_pos < self->input_sz || (!self->pending_mode.activated_at && self->pending_mode.used));
|
||||
}
|
||||
@ -1664,7 +1669,9 @@ reset(PS *self) {
|
||||
self->parser_buf_pos = 0;
|
||||
|
||||
self->pending_mode.activated_at = 0;
|
||||
#ifdef DUMP_COMMANDS
|
||||
self->pending_mode.stop_escape_code_type = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -1707,6 +1714,7 @@ PyTypeObject Parser_Type = {
|
||||
|
||||
Parser*
|
||||
alloc_vt_parser(id_type window_id) {
|
||||
if (false) vte_state_name(VTE_NORMAL);
|
||||
Parser *self = (Parser*)Parser_Type.tp_alloc(&Parser_Type, 1);
|
||||
if (self != NULL) {
|
||||
self->state = calloc(1, sizeof(PS));
|
||||
@ -1714,6 +1722,7 @@ alloc_vt_parser(id_type window_id) {
|
||||
PS *state = (PS*)self->state;
|
||||
state->window_id = window_id;
|
||||
state->pending_mode.wait_time = s_double_to_monotonic_t(2.0);
|
||||
state->parser_buf = state->_parser_buf;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -109,9 +109,10 @@ class TestParser(BaseTest):
|
||||
pb('12\033Da', '12', ('screen_index',), 'a')
|
||||
self.ae(str(s.line(0)), '12')
|
||||
self.ae(str(s.line(1)), ' a')
|
||||
pb('\033x', ('Unknown char after ESC: 0x%x' % ord('x'),))
|
||||
pb('\033xa', ('Unknown char after ESC: 0x%x' % ord('x'),), 'a')
|
||||
pb('\033c123', ('screen_reset', ), '123')
|
||||
self.ae(str(s.line(0)), '123')
|
||||
pb('\033.\033a', ('Unhandled charset related escape code: 0x2e 0x1b',), 'a')
|
||||
|
||||
def test_csi_codes(self):
|
||||
s = self.create_screen()
|
||||
@ -374,7 +375,7 @@ class TestParser(BaseTest):
|
||||
pb('\033P=1s\033\\e', ('screen_start_pending_mode',))
|
||||
pb('\033P'), pb('='), pb('2s')
|
||||
pb('\033\\', ('draw', 'e'), ('screen_stop_pending_mode',))
|
||||
pb('\033P=1sxyz;.;\033\\''\033P=2skjf".,><?_+)98\033\\', ('screen_start_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=2s\033\\', ('draw', 'f'), ('screen_stop_pending_mode',))
|
||||
pb('\033P=1s\033\\XXX\033P=2s\033\\', ('screen_start_pending_mode',), ('draw', 'XXX'), ('screen_stop_pending_mode',))
|
||||
@ -389,9 +390,9 @@ class TestParser(BaseTest):
|
||||
pb('\033[?2026h', ('screen_set_mode', 2026, 1),)
|
||||
pb('\033P+q')
|
||||
time.sleep(1.2 * timeout)
|
||||
pb('544e\033\\', ('screen_request_capabilities', 43, '544e'))
|
||||
pb(
|
||||
'544e' + '\033\\\033P=2s\033\\',
|
||||
('screen_request_capabilities', 43, '544e'),
|
||||
'\033P=2s\033\\',
|
||||
('Pending mode stop command issued while not in pending mode, this can be '
|
||||
'either a bug in the terminal application or caused by a timeout with no '
|
||||
'data received for too long or by too much data in pending mode',),
|
||||
|
@ -917,10 +917,14 @@ class TestScreen(BaseTest):
|
||||
if use_pending_mode:
|
||||
parse_bytes(s, b'\033[?2026h')
|
||||
send(q)
|
||||
del q
|
||||
if use_pending_mode:
|
||||
self.ae(c.cc_buf, [])
|
||||
parse_bytes(s, b'\033[?2026l')
|
||||
self.ae(c.cc_buf, list(expected))
|
||||
try:
|
||||
self.ae(c.cc_buf, list(expected))
|
||||
finally:
|
||||
del expected
|
||||
|
||||
for use_pending_mode in (False, True):
|
||||
t('XYZ', use_pending_mode, ('p;XYZ', False))
|
||||
|
Loading…
Reference in New Issue
Block a user