diff --git a/kitty/screen.c b/kitty/screen.c index 896df3d03..c3884dd17 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -174,7 +174,7 @@ static Line* range_line_(Screen *self, int y); void screen_reset(Screen *self) { screen_pause_rendering(self, false, 0); - self->extra_cursors.count = 0; + self->extra_cursors.count = 0; zero_at_ptr(&self->extra_cursors.color); self->extra_cursors.dirty = true; self->main_pointer_shape_stack.count = 0; self->alternate_pointer_shape_stack.count = 0; if (self->linebuf == self->alt_linebuf) screen_toggle_screen_buffer(self, true, true); if (screen_is_overlay_active(self)) { @@ -191,10 +191,6 @@ screen_reset(Screen *self) { self->last_graphic_char = 0; self->main_savepoint.is_valid = false; self->alt_savepoint.is_valid = false; - if (self->extra_cursors.count) { - self->extra_cursors.count = 0; - self->extra_cursors.dirty = true; - } linebuf_clear(self->linebuf, BLANK_CHAR); historybuf_clear(self->historybuf); clear_hyperlink_pool(self->hyperlink_pool); @@ -2870,8 +2866,9 @@ void screen_multi_cursor(Screen *self, int queried_shape, int *params, unsigned num_params) { // printf("%d;", queried_shape); for (unsigned i = 0; i < num_params; i++) {printf("%d:", params[i]);} printf("\n"); if (!num_params) { +#define pr(...) { int n = snprintf(p, sz - (p - buf), __VA_ARGS__); if (n >= 0 && (unsigned)n <= (sz - (p - buf))) p += n; } if (params == NULL) { - write_escape_code_to_child(self, ESC_CSI, ">-2;-1;1;2;3 q"); + write_escape_code_to_child(self, ESC_CSI, ">-5;-4;-3;-2;-1;1;2;3 q"); } else if (queried_shape == -2) { size_t sz = self->extra_cursors.count * 32 + 64; RAII_ALLOC(char, buf, malloc(sz)); sz -= 4; @@ -2880,14 +2877,48 @@ screen_multi_cursor(Screen *self, int queried_shape, int *params, unsigned num_p for (unsigned i = 0; i < self->extra_cursors.count; i++) { index_type cell = self->extra_cursors.locations[i].cell, shape = self->extra_cursors.locations[i].shape; index_type y = cell / self->columns, x = cell - (y * self->columns); - int n = snprintf(p, sz - (p - buf), "%d:2:%u:%u;", shape > 3 ? -1 : (int)shape, y+1, x+1); - if (n < 0 || (unsigned)n > (sz - (p - buf))) break; - p += n; + pr("%d:2:%u:%u;", shape > 3 ? -1 : (int)shape, y+1, x+1); } if (*(p-1) == ';') p--; *(p++) = ' '; *(p++) = 'q'; *(p++) = 0; write_escape_code_to_child(self, ESC_CSI, buf); } + } else if (queried_shape == -5) { + char buf[64], *p = buf; size_t sz = sizeof(buf); + pr(">-5;-3:"); ExtraCursorColor ecc = self->extra_cursors.color.cursor; +#define o() { \ + if (ecc.is_set) { \ + if (ecc.is_none) { pr("1"); } \ + else { \ + if (ecc.is_lookup) { pr("5:%u", ecc.color & 0xff); } \ + else { pr("2:%u:%u:%u", (ecc.color >> 16) & 0xff, (ecc.color >> 8) & 0xff, ecc.color & 0xff); } \ + } \ + } else pr("0"); } + + o(); pr(";-4:"); ecc = self->extra_cursors.color.text; o(); +#undef o + pr(" q"); + write_escape_code_to_child(self, ESC_CSI, buf); + } + return; +#undef pr + } + if (queried_shape == -3 || queried_shape == -4) { + ExtraCursorColor *ecc = queried_shape == -3 ? &self->extra_cursors.color.cursor : &self->extra_cursors.color.text; + self->extra_cursors.dirty = true; + switch (params[0]) { + case 0: zero_at_ptr(ecc); break; + case 1: zero_at_ptr(ecc); ecc->is_set = true; ecc->is_none = true; break; + case 2: if (num_params > 3) { + zero_at_ptr(ecc); + ecc->is_set = true; + ecc->color = ((params[1] & 0xff) << 16) | ((params[2] & 0xff) << 8) | (params[3] & 0xff); + } break; + case 5: if (num_params > 1) { + zero_at_ptr(ecc); + ecc->is_set = true; ecc->is_lookup = true; + ecc->color = params[1] & 0xff; + } break; } return; } diff --git a/kitty/screen.h b/kitty/screen.h index 51dbc19c3..76f103b39 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -92,9 +92,15 @@ typedef struct ExtraCursor { index_type cell; } ExtraCursor; +typedef struct ExtraCursorColor { + color_type color; + bool is_set, is_none, is_lookup; +} ExtraCursorColor; + typedef struct ExtraCursors { ExtraCursor *locations; unsigned count, capacity; + struct { ExtraCursorColor cursor, text; } color; bool dirty; } ExtraCursors; diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index 8d7d707b6..36aef7e0c 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -1594,13 +1594,15 @@ class TestScreen(BaseTest): s = self.create_screen() c = s.callbacks # Test detection - parse_bytes(s, b'\x1b[> q') # ] - self.ae(c.wtcbuf, b'\x1b[>-2;-1;1;2;3 q') # ] + def ec(payload=''): + return f'\x1b[>{payload} q'.encode() # ] + parse_bytes(s, ec()) + self.ae(c.wtcbuf, ec('-5;-4;-3;-2;-1;1;2;3')) def current() -> dict[int, tuple[int, int]]: ans = {} c.clear() - parse_bytes(s, '\x1b[>-2 q'.encode()) # ] + parse_bytes(s, ec('-2')) for entry in c.wtcbuf[6:-2].decode().split(';'): if entry: which, _, y, x = map(int, entry.split(':')) @@ -1615,10 +1617,10 @@ class TestScreen(BaseTest): parse_bytes(s, ''.join(buf).encode() + b' q') if region: if region is True: - parse_bytes(s, f'\x1b[>{which};4 q'.encode()) # ] + parse_bytes(s, ec(f'{which};4')) else: left, top, right, bottom = region - parse_bytes(s, f'\x1b[>{which};4:{top+1}:{left+1}:{bottom+1}:{right+1} q'.encode()) # ] + parse_bytes(s, ec(f'{which};4:{top+1}:{left+1}:{bottom+1}:{right+1}')) return current() self.ae(a(1, region=True), {1:{(x, y) for x in range(s.columns) for y in range(s.lines)}}) @@ -1628,13 +1630,33 @@ class TestScreen(BaseTest): self.ae(a(0, (1, 2), (2, 3)), {-1: {(2, 2)}, 2: {(1, 3)}}) self.ae(a(0, region=True), {}) s.cursor.x, s.cursor.y = 1, 2 - parse_bytes(s, b'\x1b[>3;0 q') # ] + parse_bytes(s, ec('3;0')) self.ae(current(), {3: {(1, 2)}}) - parse_bytes(s, b'\x1b[>3;2:3 q') # ] + parse_bytes(s, ec('3;2:3')) self.ae(current(), {3: {(1, 2)}}) - parse_bytes(s, b'\x1b[>0;4:3:1:4 q') # ] + parse_bytes(s, ec('0;4:3:1:4')) self.ae(current(), {}) + def sc(op, r=0, g=0, b=0, slot=-3): + parse_bytes(s, ec(f'{slot};{op}:{r}:{g}:{b}')) + c.clear() + parse_bytes(s, ec('-5')) + for x in c.wtcbuf[3:-2].decode().split(';')[1:]: + parts = x.split(':') + if int(parts[0]) == slot: + if op < 2: + self.ae(op, int(parts[1])) + elif op == 2: + self.ae((op, r, g, b), tuple(map(int, parts[1:]))) + else: + self.ae((op, r), tuple(map(int, parts[1:]))) + break + for slot in (-3, -4): + sc(0, slot=slot) + sc(1, slot=slot) + sc(2, 1, 2, 3, slot=slot) + sc(5, 13, slot=slot) + def detect_url(self, scale=1): s = self.create_screen(cols=30 * scale)