diff --git a/kitty/modes.h b/kitty/modes.h index a8aff463d..b4c5a09e0 100644 --- a/kitty/modes.h +++ b/kitty/modes.h @@ -80,3 +80,6 @@ #define BRACKETED_PASTE (2004 << 5) #define BRACKETED_PASTE_START "200~" #define BRACKETED_PASTE_END "201~" + +// Pending updates mode +#define PENDING_UPDATE (2026 << 5) diff --git a/kitty/parser.c b/kitty/parser.c index e541a2bd7..fd0dadf6b 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -1244,7 +1244,7 @@ END_ALLOW_CASE_RANGE handle_esc_mode_char(screen, codepoint, dump_callback); \ break; \ case CSI: \ - if (accumulate_csi(screen, codepoint, dump_callback)) { dispatch_csi(screen, dump_callback); SET_STATE(0); } \ + if (accumulate_csi(screen, codepoint, dump_callback)) { dispatch_csi(screen, dump_callback); SET_STATE(0); watch_for_pending; } \ break; \ case OSC: \ if (accumulate_osc(screen, codepoint, dump_callback)) { dispatch_osc(screen, dump_callback); SET_STATE(0); } \ @@ -1316,78 +1316,110 @@ FLUSH_DRAW; } -static inline size_t +static size_t _queue_pending_bytes(Screen *screen, const uint8_t *buf, size_t len, PyObject *dump_callback DUMP_UNUSED) { size_t pos = 0; - enum STATE { NORMAL, MAYBE_DCS, IN_DCS, EXPECTING_DATA, EXPECTING_SLASH }; - enum STATE state = screen->pending_mode.state; -#define COPY(what) screen->pending_mode.buf[screen->pending_mode.used++] = what -#define COPY_STOP_BUF { \ - COPY(0x1b); COPY('P'); COPY(PENDING_MODE_CHAR); \ - for (size_t i = 0; i < screen->pending_mode.stop_buf_pos; i++) { \ - COPY(screen->pending_mode.stop_buf[i]); \ + 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]); \ } \ - screen->pending_mode.stop_buf_pos = 0;} + pm.stop_buf_pos = 0;} while (pos < len) { uint8_t ch = buf[pos++]; switch(state) { case NORMAL: - if (ch == ESC) state = MAYBE_DCS; + if (ch == ESC) state = MAYBE_DCS_OR_CSI; else COPY(ch); break; - case MAYBE_DCS: + 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; screen->pending_mode.stop_buf_pos = 0; } + if (ch == PENDING_MODE_CHAR) { state = EXPECTING_DATA; pm.stop_buf_pos = 0; } else { state = NORMAL; COPY(0x1b); COPY('P'); COPY(ch); } break; + case IN_CSI: + if (ch == '?') { state = EXPECTING_CSI_DATA; pm.stop_buf_pos = 0; } + else { + state = NORMAL; + COPY(0x1b); COPY('['); COPY(ch); + } + break; + case EXPECTING_CSI_DATA: + if (ch == 'h' || ch == 'l') { + if (pm.stop_buf_pos == 4 && memcmp(pm.stop_buf, "2026", 4) == 0) { + if (ch == 'h') { start_pending_mode } else { stop_pending_mode } + } else { + COPY_STOP_BUF(false); COPY(ch); + } + state = NORMAL; + } else { + pm.stop_buf[pm.stop_buf_pos++] = ch; + if (pm.stop_buf_pos >= sizeof(pm.stop_buf)) { + 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 { - screen->pending_mode.stop_buf[screen->pending_mode.stop_buf_pos++] = ch; - if (screen->pending_mode.stop_buf_pos >= sizeof(screen->pending_mode.stop_buf)) { + if (pm.stop_buf_pos >= sizeof(pm.stop_buf)) { state = NORMAL; - COPY_STOP_BUF; + COPY_STOP_BUF(true); } } break; case EXPECTING_SLASH: if ( ch == '\\' && - screen->pending_mode.stop_buf_pos >= 2 && - (screen->pending_mode.stop_buf[0] == '1' || screen->pending_mode.stop_buf[0] == '2') && - screen->pending_mode.stop_buf[1] == 's' + 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 (screen->pending_mode.stop_buf[0] == '2') { - REPORT_COMMAND(screen_stop_pending_mode); - screen->pending_mode.activated_at = 0; - goto end; - } else { - REPORT_COMMAND(screen_start_pending_mode); - screen->pending_mode.activated_at = monotonic(); - } + if (pm.stop_buf[0] == '2') { stop_pending_mode } else { start_pending_mode } } else { - state = NORMAL; - COPY_STOP_BUF; COPY(ch); + COPY_STOP_BUF(true); COPY(ch); } + state = NORMAL; break; } } end: - screen->pending_mode.state = state; + pm.state = state; return pos; #undef COPY #undef COPY_STOP_BUF +#undef stop_pending_mode +#undef start_pending_mode +#undef pm } static inline void diff --git a/kitty/screen.c b/kitty/screen.c index f7f83fa6a..2cd2810e3 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -815,7 +815,7 @@ screen_toggle_screen_buffer(Screen *self, bool save_cursor, bool clear_alt_scree void screen_normal_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI void screen_alternate_keypad_mode(Screen UNUSED *self) {} // Not implemented as this is handled by the GUI -static inline void +static void set_mode_from_const(Screen *self, unsigned int mode, bool val) { #define SIMPLE_MODE(name) \ case name: \ @@ -884,6 +884,14 @@ set_mode_from_const(Screen *self, unsigned int mode, bool val) { if (val && self->linebuf == self->main_linebuf) screen_toggle_screen_buffer(self, mode == ALTERNATE_SCREEN, mode == ALTERNATE_SCREEN); else if (!val && self->linebuf != self->main_linebuf) screen_toggle_screen_buffer(self, mode == ALTERNATE_SCREEN, mode == ALTERNATE_SCREEN); break; + case PENDING_UPDATE: + if (val) { + self->pending_mode.activated_at = monotonic(); + } else { + if (!self->pending_mode.activated_at) log_error("Pending mode stop command issued while not in pending mode"); + self->pending_mode.activated_at = 0; + } + break; default: private = mode >= 1 << 5; if (private) mode >>= 5; @@ -1626,6 +1634,8 @@ report_mode_status(Screen *self, unsigned int which, bool private) { ans = self->modes.mouse_tracking_mode == ANY_MODE ? 1 : 2; break; case MOUSE_SGR_MODE: ans = self->modes.mouse_tracking_protocol == SGR_PROTOCOL ? 1 : 2; break; + case PENDING_UPDATE: + ans = self->pending_mode.activated_at ? 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, CSI, buf); diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 5366eebfd..af6298c28 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -343,6 +343,7 @@ class TestParser(BaseTest): timeout = 0.1 s.set_pending_timeout(timeout) pb = partial(self.parse_bytes_dump, s) + pb('\033P=1s\033\\', ('screen_start_pending_mode',)) pb('a') self.ae(str(s.line(0)), '') @@ -361,6 +362,12 @@ class TestParser(BaseTest): pb('\033\\', ('screen_stop_pending_mode',), ('draw', 'e')) pb('\033P=1sxyz;.;\033\\''\033P=2skjf".,>