Get pending mode working and add a few more tests

This commit is contained in:
Kovid Goyal 2023-10-29 20:22:01 +05:30
parent 52025ff030
commit 065866895c
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
6 changed files with 65 additions and 31 deletions

View file

@ -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);
}

View file

@ -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)

View file

@ -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;

View file

@ -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
// }}}

View file

@ -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);

View file

@ -358,7 +358,6 @@ def test_pending(self):
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 @@ def test_pending(self):
)
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)