diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 49d95ad2c..dea2c9772 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -439,7 +439,7 @@ static bool do_parse(ChildMonitor *self, Screen *screen, monotonic_t now, bool flush) { bool input_read = false; screen_mutex(lock, read); - if (screen->read_buf_sz || screen->pending_mode.used) { + if (screen->read_buf_sz || vt_parser_has_pending_data(screen->vt_parser)) { monotonic_t time_since_new_input = now - screen->new_input_at; if (flush || time_since_new_input >= OPT(input_delay)) { bool read_buf_full = screen->read_buf_sz >= READ_BUF_SZ; @@ -447,9 +447,10 @@ do_parse(ChildMonitor *self, Screen *screen, monotonic_t now, bool flush) { parse_func(screen, self->dump_callback, now); if (read_buf_full) wakeup_io_loop(self, false); // Ensure the read fd has POLLIN set screen->new_input_at = 0; - if (screen->pending_mode.activated_at) { - monotonic_t time_since_pending = MAX(0, now - screen->pending_mode.activated_at); - set_maximum_wait(screen->pending_mode.wait_time - time_since_pending); + monotonic_t activated_at = vt_parser_pending_activated_at(screen->vt_parser); + if (activated_at) { + monotonic_t time_since_pending = MAX(0, now - activated_at); + set_maximum_wait(vt_parser_pending_wait_time(screen->vt_parser) - time_since_pending); } } else set_maximum_wait(OPT(input_delay) - time_since_new_input); } diff --git a/kitty/screen.c b/kitty/screen.c index 5b569434d..27b45135c 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -135,7 +135,6 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self->active_hyperlink_id = 0; self->grman = self->main_grman; - self->pending_mode.wait_time = s_double_to_monotonic_t(2.0); self->disable_ligatures = OPT(disable_ligatures); self->main_tabstops = PyMem_Calloc(2 * self->columns, sizeof(bool)); if ( @@ -476,7 +475,6 @@ dealloc(Screen* self) { PyMem_Free(self->overlay_line.original_line.gpu_cells); Py_CLEAR(self->overlay_line.overlay_text); PyMem_Free(self->main_tabstops); - free(self->pending_mode.buf); free(self->selections.items); free(self->url_ranges.items); free_hyperlink_pool(self->hyperlink_pool); @@ -997,13 +995,13 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) { break; case PENDING_UPDATE: if (val) { - self->pending_mode.activated_at = monotonic(); + vt_parser_set_pending_activated_at(self->vt_parser, monotonic()); } else { - if (!self->pending_mode.activated_at) log_error( + if (!vt_parser_pending_activated_at(self->vt_parser)) log_error( "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"); - else self->pending_mode.activated_at = 0; + else vt_parser_set_pending_activated_at(self->vt_parser, 0); } break; case 7727 << 5: @@ -2051,7 +2049,7 @@ report_mode_status(Screen *self, unsigned int which, bool private) { case MOUSE_SGR_PIXEL_MODE: ans = self->modes.mouse_tracking_protocol == SGR_PIXEL_PROTOCOL ? 1 : 2; break; case PENDING_UPDATE: - ans = self->pending_mode.activated_at ? 1 : 2; break; + ans = vt_parser_pending_activated_at(self->vt_parser) ? 1 : 2; break; } int sz = snprintf(buf, sizeof(buf) - 1, "%s%u;%u$y", (private ? "?" : ""), which, ans); if (sz > 0) write_escape_code_to_child(self, ESC_CSI, buf); @@ -3199,8 +3197,16 @@ hyperlink_for_id(Screen *self, PyObject *val) { static PyObject* set_pending_timeout(Screen *self, PyObject *val) { if (!PyFloat_Check(val)) { PyErr_SetString(PyExc_TypeError, "timeout must be a float"); return NULL; } - PyObject *ans = PyFloat_FromDouble(self->pending_mode.wait_time); - self->pending_mode.wait_time = s_double_to_monotonic_t(PyFloat_AS_DOUBLE(val)); + PyObject *ans = PyFloat_FromDouble(monotonic_t_to_s_double(vt_parser_pending_wait_time(self->vt_parser))); + vt_parser_set_pending_wait_time(self->vt_parser, s_double_to_monotonic_t(PyFloat_AS_DOUBLE(val))); + return ans; +} + +static PyObject* +set_pending_activated_at(Screen *self, PyObject *val) { + if (!PyFloat_Check(val)) { PyErr_SetString(PyExc_TypeError, "timeout must be a float"); return NULL; } + PyObject *ans = PyFloat_FromDouble(monotonic_t_to_s_double(vt_parser_pending_activated_at(self->vt_parser))); + vt_parser_set_pending_activated_at(self->vt_parser, s_double_to_monotonic_t(PyFloat_AS_DOUBLE(val))); return ans; } @@ -4491,6 +4497,7 @@ static PyMethodDef methods[] = { {"index", (PyCFunction)xxx_index, METH_VARARGS, ""}, {"has_selection", (PyCFunction)has_selection, METH_VARARGS, ""}, MND(set_pending_timeout, METH_O) + MND(set_pending_activated_at, METH_O) MND(as_text, METH_VARARGS) MND(as_text_non_visual, METH_VARARGS) MND(as_text_for_history_buf, METH_VARARGS) diff --git a/kitty/screen.h b/kitty/screen.h index 58c3dfefc..20764ac43 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -115,12 +115,6 @@ typedef struct { CursorRenderInfo cursor_render_info; unsigned int render_unfocused_cursor; - struct { - size_t capacity, used; - uint8_t *buf; - monotonic_t activated_at, wait_time; - unsigned stop_escape_code_type; - } pending_mode; DisableLigature disable_ligatures; PyObject *marker; bool has_focus; diff --git a/kitty/vt-parser.c b/kitty/vt-parser.c index 59e0f5c25..b196667c9 100644 --- a/kitty/vt-parser.c +++ b/kitty/vt-parser.c @@ -22,9 +22,6 @@ #define PENDING_BUF_INCREMENT (16u * 1024u) // Macros {{{ -#define SAVE_INPUT_DATA const uint8_t *orig_input_data = self->input_data; size_t orig_input_sz = self->input_sz, orig_input_pos = self->input_pos - -#define RESTORE_INPUT_DATA self->input_data = orig_input_data; self->input_sz = orig_input_sz; self->input_pos = orig_input_pos #define SET_STATE(state) self->vte_state = state; self->parser_buf_pos = 0; self->utf8_state = UTF8_ACCEPT; @@ -144,7 +141,7 @@ utoi(const uint8_t *buf, unsigned int sz) { // }}} typedef enum VTEState { - VTE_NORMAL, VTE_ESC, VTE_CSI, VTE_OSC, VTE_DCS, VTE_APC, VTE_PM + 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; typedef struct PS { @@ -1429,13 +1426,16 @@ pending_esc_mode_byte(PS *self) { } } +#define pb(i) self->parser_buf[i] + static void pending_escape_code(PS *self, char_type start_ch, char_type end_ch) { 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.buf[self->pending_mode.used++] = ESC; + 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; } @@ -1449,7 +1449,6 @@ pending_osc(PS *self) { if (extended) continue_osc_52(self); } -#define pb(i) self->parser_buf[i] static void pending_dcs(PS *self) { if (self->parser_buf_pos >= 3 && pb(0) == '=' && (pb(1) == '1' || pb(1) == '2') && pb(2) == 's') { @@ -1489,21 +1488,29 @@ FLUSH_DRAW; static void parse_pending_bytes(PS *self) { - SAVE_INPUT_DATA; - self->input_data = self->pending_mode.buf; self->input_sz = self->pending_mode.used; + 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; while (self->input_pos < self->input_sz) { dispatch_single_byte(dispatch, ;); } - RESTORE_INPUT_DATA; + self->input_data = orig_input_data; self->input_sz = orig_input_sz; self->input_pos = orig_input_pos; } static void dump_partial_escape_code_to_pending(PS *self) { + ensure_pending_space(self, self->parser_buf_pos + 2); if (self->parser_buf_pos) { - ensure_pending_space(self, self->parser_buf_pos + 1); - self->pending_mode.buf[self->pending_mode.used++] = self->vte_state; + 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; } } // }}} @@ -1545,7 +1552,7 @@ do_parse_vt(PS *self) { 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); + self->pending_mode.buf = PyMem_Realloc(self->pending_mode.buf, self->pending_mode.capacity); if (!self->pending_mode.buf) fatal("Out of memory"); } if (self->pending_mode.stop_escape_code_type) { @@ -1684,10 +1691,16 @@ alloc_vt_parser(id_type window_id) { if (!self->state) { Py_CLEAR(self); PyErr_NoMemory(); return NULL; } PS *state = (PS*)self->state; state->window_id = window_id; + state->pending_mode.wait_time = s_double_to_monotonic_t(2.0); } return self; } +bool vt_parser_has_pending_data(Parser* p) { return ((PS*)p->state)->pending_mode.used != 0; } +monotonic_t vt_parser_pending_activated_at(Parser*p) { return ((PS*)p->state)->pending_mode.activated_at; } +void vt_parser_set_pending_activated_at(Parser*p, monotonic_t n) { ((PS*)p->state)->pending_mode.activated_at = n; } +monotonic_t vt_parser_pending_wait_time(Parser*p) { return ((PS*)p->state)->pending_mode.wait_time; } +void vt_parser_set_pending_wait_time(Parser*p, monotonic_t n) { ((PS*)p->state)->pending_mode.wait_time = n; } INIT_TYPE(Parser) #endif // }}} diff --git a/kitty/vt-parser.h b/kitty/vt-parser.h index fdc9995b8..3a0912961 100644 --- a/kitty/vt-parser.h +++ b/kitty/vt-parser.h @@ -20,3 +20,8 @@ typedef struct Parser { Parser* alloc_vt_parser(id_type window_id); void free_vt_parser(Parser*); void reset_vt_parser(Parser*); +bool vt_parser_has_pending_data(Parser*); +monotonic_t vt_parser_pending_activated_at(Parser*); +monotonic_t vt_parser_pending_wait_time(Parser*); +void vt_parser_set_pending_activated_at(Parser*, monotonic_t); +void vt_parser_set_pending_wait_time(Parser*, monotonic_t); diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index c52ca5a32..7efef869b 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -358,7 +358,6 @@ class TestParser(BaseTest): pb('\033[?2026h\033[32ma\033[?2026l', ('screen_set_mode', 2026, 1), ('select_graphic_rendition', '32 '), ('draw', 'a'), ('screen_reset_mode', 2026, 1)) pb('\033[?2026h\033P+q544e\033\\ama\033P=2s\033\\', ('screen_set_mode', 2026, 1), ('screen_request_capabilities', 43, '544e'), ('draw', 'ama'), ('screen_stop_pending_mode',)) - pb('\033P=1s\033\\\033(B\033P=2s\033\\', ('screen_start_pending_mode',), ('screen_designate_charset', 0, 66), ('screen_stop_pending_mode',)) s.reset() s.set_pending_timeout(timeout) @@ -375,6 +374,21 @@ class TestParser(BaseTest): ) self.assertEqual(str(s.line(0)), '') + pb('\033[?2026h', ('screen_set_mode', 2026, 1),) + pb('ab') + s.set_pending_activated_at(0.00001) + pb('cd', ('draw', 'abcd')) + pb('\033[?2026h', ('screen_set_mode', 2026, 1),) + pb('\033') + s.set_pending_activated_at(0.00001) + pb('7', ('screen_save_cursor',)) + pb('\033[?2026h\033]', ('screen_set_mode', 2026, 1),) + s.set_pending_activated_at(0.00001) + pb('8;;\x07', ('set_active_hyperlink', None, None)) + pb('\033[?2026h\033', ('screen_set_mode', 2026, 1),) + s.set_pending_activated_at(0.00001) + pb(']8;;\x07', ('set_active_hyperlink', None, None)) + def test_oth_codes(self): s = self.create_screen() pb = partial(self.parse_bytes_dump, s)