diff --git a/kitty/data-types.h b/kitty/data-types.h index 07cb5fc1c..15d7286a2 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -314,6 +314,12 @@ void screen_index(Screen *self); void screen_reset(Screen *self); void screen_set_tab_stop(Screen *self); void screen_insert_characters(Screen *self, unsigned int count); +void screen_cursor_up(Screen *self, unsigned int count/*=1*/, bool do_carriage_return/*=false*/, int move_direction/*=-1*/); +void screen_cursor_to_column(Screen *self, unsigned int column); +void screen_cursor_down(Screen *self, unsigned int count/*=1*/); +void screen_cursor_forward(Screen *self, unsigned int count/*=1*/); +void screen_cursor_down1(Screen *self, unsigned int count/*=1*/); +void screen_cursor_up1(Screen *self, unsigned int count/*=1*/); #define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen, uint8_t ch); DECLARE_CH_SCREEN_HANDLER(bell) DECLARE_CH_SCREEN_HANDLER(backspace) diff --git a/kitty/parser.c b/kitty/parser.c index c800810a2..f0a65987a 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -220,10 +220,10 @@ HANDLER(esc) { static inline unsigned int fill_params(Screen *screen, unsigned int *params, unsigned int expect) { unsigned int start_pos = 1, i = 1, pi = 0; - uint8_t ch; - screen->parser_buf[screen->parser_buf_pos++] = ';'; + uint8_t ch = 1; + screen->parser_buf[screen->parser_buf_pos] = 0; - while (pi < MIN(MAX_PARAMS, expect) && i < PARSER_BUF_SZ - 1) { + while (pi < MIN(MAX_PARAMS, expect) && i < PARSER_BUF_SZ - 1 && ch != 0) { ch = screen->parser_buf[i++]; if (ch == 0 || ch == ';') { if (start_pos < i - 1) { @@ -235,6 +235,9 @@ static inline unsigned int fill_params(Screen *screen, unsigned int *params, uns return pi; } +static inline void screen_cursor_up2(Screen *s, unsigned int count) { screen_cursor_up(s, count, false, -1); } +static inline void screen_cursor_back1(Screen *s, unsigned int count) { screen_cursor_back(s, count, -1); } + HANDLER(csi) { #define CALL_BASIC_HANDLER(name) REPORT_COMMAND(screen, ch); name(screen, ch); break; #define HANDLE_BASIC_CH \ @@ -253,17 +256,44 @@ HANDLER(csi) { case NUL: \ case DEL: \ break; // no-op -#define CALL_CSI_HANDLER1(name) \ - if (fill_params(screen, params, 1) != 1) { REPORT_ERROR("Expected parameter for CSI escape sequence: %s missing", #name); } \ - else { \ - REPORT_COMMAND(name, params[0]); \ - screen_insert_characters(screen, params[0]); \ - } \ +#define CALL_CSI_HANDLER1(name, defval) \ + p1 = fill_params(screen, params, 1) > 0 ? params[0] : defval; \ + REPORT_COMMAND(name, p1); \ + name(screen, p1); \ SET_STATE(NORMAL_STATE); \ break; +#define CALL_CSI_HANDLER2(name, defval1, defval2) \ + count = fill_params(screen, params, 2); \ + p1 = count > 0 ? params[0] : defval1; \ + p2 = count > 1 ? params[1] : defval2; \ + REPORT_COMMAND(name, p1, p2); \ + name(screen, p1, p2); \ + SET_STATE(NORMAL_STATE); \ + break; + +#define DISPATCH_CSI \ + case ICH: \ + CALL_CSI_HANDLER1(screen_insert_characters, 1); \ + case CUU: \ + CALL_CSI_HANDLER1(screen_cursor_up2, 1); \ + case CUD: \ + CALL_CSI_HANDLER1(screen_cursor_down, 1); \ + case CUF: \ + CALL_CSI_HANDLER1(screen_cursor_forward, 1); \ + case CUB: \ + CALL_CSI_HANDLER1(screen_cursor_back1, 1); \ + case CNL: \ + CALL_CSI_HANDLER1(screen_cursor_down1, 1); \ + case CPL: \ + CALL_CSI_HANDLER1(screen_cursor_up1, 1); \ + case CHA: \ + CALL_CSI_HANDLER1(screen_cursor_to_column, 1); \ + case CUP: \ + CALL_CSI_HANDLER2(screen_cursor_position, 1, 1); + uint8_t ch = buf[(*pos)++]; - unsigned int params[MAX_PARAMS]; + unsigned int params[MAX_PARAMS], p1, p2, count; switch(screen->parser_buf_pos) { case 0: // CSI starting screen->parser_buf[0] = 0; @@ -279,6 +309,7 @@ HANDLER(csi) { screen->parser_buf[0] = ch; screen->parser_buf_pos = 1; break; HANDLE_BASIC_CH + DISPATCH_CSI default: REPORT_ERROR("Invalid first character for CSI: 0x%x", ch); SET_STATE(NORMAL_STATE); @@ -295,12 +326,11 @@ HANDLER(csi) { } else screen->parser_buf[screen->parser_buf_pos++] = ch; break; HANDLE_BASIC_CH + DISPATCH_CSI default: REPORT_ERROR("Invalid character for CSI: 0x%x", ch); SET_STATE(NORMAL_STATE); break; - case ICH: - CALL_CSI_HANDLER1(screen_insert_characters); } break; } diff --git a/kitty/screen.c b/kitty/screen.c index 80b452d14..acd2b01e9 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -399,6 +399,15 @@ void screen_cursor_down1(Screen *self, unsigned int count/*=1*/) { screen_cursor_up(self, count, true, 1); } +void screen_cursor_to_column(Screen *self, unsigned int column) { + unsigned int x = MAX(column, 1) - 1; + if (x != (unsigned int)self->cursor->x) { + self->cursor->x = x; + screen_ensure_bounds(self, false); + tracker_cursor_changed(self->change_tracker); + } +} + void screen_index(Screen *self) { // Move cursor down one line, scrolling screen if needed unsigned int top = self->margin_top, bottom = self->margin_bottom; @@ -485,8 +494,8 @@ void screen_ensure_bounds(Screen *self, bool use_margins/*=false*/) { } void screen_cursor_position(Screen *self, unsigned int line, unsigned int column) { - line = (line || 1) - 1; - column = (column || 1) - 1; + line = (line == 0 ? 1 : line) - 1; + column = (column == 0 ? 1: column) - 1; if (self->modes.mDECOM) { line += self->margin_top; if (line < self->margin_bottom || line > self->margin_top) return; diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index 8c4e3885d..030a34d75 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -63,6 +63,14 @@ class TestScreen(BaseTest): s.cursor_back(5) pb('x\033[2@y', ('screen_insert_characters', 2)) self.ae(str(s.line(0)), 'xy bc') - pb('x\033[2;3@y', ('screen_insert_characters', 2)) - pb('x\033[@y', 'Invalid first character for CSI: 0x%x' % ord('@')) + pb('x\033[2;7@y', ('screen_insert_characters', 2)) + pb('x\033[@y', ('screen_insert_characters', 1)) + pb('x\033[345@y', ('screen_insert_characters', 345)) + pb('x\033[345;@y', ('screen_insert_characters', 345)) + pb('\033[H', ('screen_cursor_position', 1, 1)) + self.ae(s.cursor.x, 0), self.ae(s.cursor.y, 0) + pb('\033[4H', ('screen_cursor_position', 4, 1)) + pb('\033[3;2H', ('screen_cursor_position', 3, 2)) + pb('\033[3;2;H', ('screen_cursor_position', 3, 2)) + self.ae(s.cursor.x, 1), self.ae(s.cursor.y, 2) s.reset()