Get pending mode working and add a few more tests
This commit is contained in:
parent
52025ff030
commit
065866895c
6 changed files with 65 additions and 31 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// }}}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue