From c393c1679e55204382a193fb0a36d605aff03aaa Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 24 Aug 2025 15:22:14 +0530 Subject: [PATCH] Implement rendering of multi cursors in the shaders --- kitty/cell_vertex.glsl | 19 +++++++++++-------- kitty/child-monitor.c | 13 ++++++------- kitty/data-types.h | 4 ++-- kitty/screen.c | 7 ++++++- kitty/screen.h | 4 +++- kitty/shaders.c | 12 ++++-------- kitty/state.h | 2 -- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 464750d66..32b50de52 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -64,10 +64,8 @@ out float effective_text_alpha; const uint BYTE_MASK = uint(0xFF); const uint SPRITE_INDEX_MASK = uint(0x7fffffff); const uint SPRITE_COLORED_MASK = uint(0x80000000); -const uint SPRITE_COLORED_SHIFT = uint(31); -const uint ZERO = uint(0); -const uint ONE = uint(1); -const uint TWO = uint(2); +const uint SPRITE_COLORED_SHIFT = 31u; +const uint ONE = 1u; const uint DECORATION_MASK = uint({DECORATION_MASK}); vec3 color_to_vec(uint c) { @@ -168,9 +166,14 @@ CellData set_vertex_position() { sprite_pos = to_sprite_pos(pos, sprite_idx[0] & SPRITE_INDEX_MASK); colored_sprite = float((sprite_idx[0] & SPRITE_COLORED_MASK) >> SPRITE_COLORED_SHIFT); #endif - float is_block_cursor = one_if_equal_zero_otherwise(cursor_shape, 1u); - float has_cursor = is_cursor(column, row); - return CellData(has_cursor, has_cursor * is_block_cursor, pos, cursor_shape_map[cursor_shape]); + float has_main_cursor = is_cursor(column, row); + float multicursor_shape = float((is_selected >> 2) & 3u); + float multicursor_uses_main_cursor_shape = float((is_selected >> 4) & ONE); + multicursor_shape = if_one_then(multicursor_uses_main_cursor_shape, cursor_shape, multicursor_shape); + float final_cursor_shape = if_one_then(has_main_cursor, cursor_shape, multicursor_shape); + float has_cursor = zero_or_one(final_cursor_shape); + float is_block_cursor = has_cursor * one_if_equal_zero_otherwise(final_cursor_shape, 1.0); + return CellData(has_cursor, is_block_cursor, pos, cursor_shape_map[int(final_cursor_shape)]); } float background_opacity_for(uint bg, uint colorval, float opacity_if_matched) { // opacity_if_matched if bg == colorval else 1 @@ -261,7 +264,7 @@ void main() { foreground = color_to_vec(fg_as_uint); float has_dim = float((text_attrs >> DIM_SHIFT) & ONE); effective_text_alpha = inactive_text_alpha * mix(1.0, dim_opacity, has_dim); - float in_url = float((is_selected & TWO) >> 1); + float in_url = float((is_selected >> 1) & ONE); decoration_fg = if_one_then(in_url, color_to_vec(url_color), to_color(colors[2], fg_as_uint)); // Selection vec3 selection_color = if_one_then(use_cell_bg_for_selection_fg, bg, color_to_vec(highlight_fg)); diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 4eba3ad1a..356b7a50d 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -652,9 +652,7 @@ pyset_iutf8(ChildMonitor *self, PyObject *args) { static bool cursor_needs_render(Window *w) { -#define cri w->render_data.screen->cursor_render_info - return w->cursor_opacity_at_last_render != cri.opacity || w->render_data.screen->last_rendered.cursor_x != cri.x || w->render_data.screen->last_rendered.cursor_y != cri.y || w->last_cursor_shape != cri.shape; -#undef cri + return memcmp(&w->render_data.screen->last_rendered.cursor, &w->render_data.screen->cursor_render_info, sizeof(CursorRenderInfo)) != 0; } static bool @@ -670,11 +668,13 @@ collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow cursor = rd->screen->paused_rendering.expires_at ? &rd->screen->paused_rendering.cursor : rd->screen->cursor; ans->x = cursor->x; ans->y = cursor->y; } - ans->opacity = 0; - if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return cursor_needs_render(w); + ans->is_visible = false; ans->multicursor_count = 0; ans->opacity = 1; + if (rd->screen->scrolled_by) return cursor_needs_render(w); + ans->multicursor_count = screen_multi_cursor_count(rd->screen); + ans->is_visible = screen_is_cursor_visible(rd->screen); + if (!ans->is_visible && ans->multicursor_count == 0) return cursor_needs_render(w); monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time; bool cursor_blinking = OPT(cursor_blink_interval) > 0 && !cursor->non_blinking && os_window->is_focused && (OPT(cursor_stop_blinking_after) == 0 || time_since_start_blink <= OPT(cursor_stop_blinking_after)); - ans->opacity = 1; if (cursor_blinking) { if (animation_is_valid(OPT(animation.cursor))) { monotonic_t duration = OPT(cursor_blink_interval) * 2; @@ -812,7 +812,6 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co if (is_active_window) active_window = w; draw_cells(&WD, os_window, is_active_window, false, num_of_visible_windows == 1, w); if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT); - w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape; } } setup_os_window_for_rendering(os_window, tab, active_window, false); diff --git a/kitty/data-types.h b/kitty/data-types.h index da05ae8bf..054b8a08e 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -221,9 +221,9 @@ typedef struct { } Cursor; typedef struct { - bool is_focused, render_even_when_unfocused; + bool is_focused, render_even_when_unfocused, is_visible; CursorShape shape; - unsigned int x, y; + unsigned x, y, multicursor_count; float opacity; } CursorRenderInfo; diff --git a/kitty/screen.c b/kitty/screen.c index 3b894159c..61b2321af 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1900,6 +1900,11 @@ screen_is_cursor_visible(const Screen *self) { return self->paused_rendering.expires_at ? self->paused_rendering.cursor_visible : self->modes.mDECTCEM; } +unsigned +screen_multi_cursor_count(const Screen *self) { + return self->paused_rendering.expires_at ? self->paused_rendering.extra_cursors.count : self->extra_cursors.count; +} + void screen_backspace(Screen *self) { screen_cursor_move(self, 1, -1, true); @@ -3470,7 +3475,7 @@ screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE fonts_dat lnum = y - self->scrolled_by; linebuf_init_line(self->linebuf, lnum); if (self->linebuf->line->attrs.has_dirty_text || - (cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor_y == lnum))) { + (cursor_has_moved && (self->cursor->y == lnum || self->last_rendered.cursor.y == lnum))) { render_line(fonts_data, self->linebuf->line, lnum, self->cursor, self->disable_ligatures, self->lc); screen_render_line_graphics(self, self->linebuf->line, y - self->scrolled_by); if (self->linebuf->line->attrs.has_dirty_text && screen_has_marker(self)) mark_text_in_line( diff --git a/kitty/screen.h b/kitty/screen.h index 21cbb39d1..f899dc345 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -109,9 +109,10 @@ typedef struct { id_type window_id; Selections selections, url_ranges; struct { - unsigned int cursor_x, cursor_y, scrolled_by; + unsigned int scrolled_by; index_type lines, columns; color_type cursor_bg; + CursorRenderInfo cursor; } last_rendered; bool is_dirty, scroll_changed, reload_all_gpu_data; Cursor *cursor; @@ -265,6 +266,7 @@ bool screen_has_selection(Screen*); bool screen_invert_colors(Screen *self); void screen_update_cell_data(Screen *self, void *address, FONTS_DATA_HANDLE, bool cursor_has_moved); bool screen_is_cursor_visible(const Screen *self); +unsigned screen_multi_cursor_count(const Screen *self); bool screen_selection_range_for_line(Screen *self, index_type y, index_type *start, index_type *end); bool screen_selection_range_for_word(Screen *self, const index_type x, const index_type y, index_type *, index_type *, index_type *start, index_type *end, bool); void screen_start_selection(Screen *self, index_type x, index_type y, bool, bool, SelectionExtendMode); diff --git a/kitty/shaders.c b/kitty/shaders.c index 7e4d11069..70173ce3d 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -476,7 +476,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, C // Cursor position Line *line_for_cursor = NULL; rd->cursor_opacity = MAX(0, MIN(cursor->opacity, 1)); - if (rd->cursor_opacity != 0) { + if (rd->cursor_opacity != 0 && cursor->is_visible) { rd->cursor_x1 = cursor->x, rd->cursor_y1 = cursor->y; rd->cursor_x2 = cursor->x, rd->cursor_y2 = cursor->y; CursorShape cs = (cursor->is_focused || OPT(cursor_shape_unfocused) == NO_CURSOR_SHAPE) ? cursor->shape : OPT(cursor_shape_unfocused); @@ -559,8 +559,8 @@ cell_prepare_to_render(ssize_t vao_idx, Screen *screen, FONTS_DATA_HANDLE fonts_ ensure_sprite_map(fonts_data); const Cursor *cursor = screen->paused_rendering.expires_at ? &screen->paused_rendering.cursor : screen->cursor; - bool cursor_pos_changed = cursor->x != screen->last_rendered.cursor_x - || cursor->y != screen->last_rendered.cursor_y; + bool cursor_pos_changed = cursor->x != screen->last_rendered.cursor.x \ + || cursor->y != screen->last_rendered.cursor.y; bool disable_ligatures = screen->disable_ligatures == DISABLE_LIGATURES_CURSOR; bool screen_resized = screen->last_rendered.columns != screen->columns || screen->last_rendered.lines != screen->lines; @@ -576,11 +576,6 @@ cell_prepare_to_render(ssize_t vao_idx, Screen *screen, FONTS_DATA_HANDLE fonts_ if (!screen->paused_rendering.cell_data_updated) update_cell_data; } else if (screen->reload_all_gpu_data || screen->scroll_changed || screen->is_dirty || screen_resized || (disable_ligatures && cursor_pos_changed)) update_cell_data; - if (cursor_pos_changed) { - screen->last_rendered.cursor_x = cursor->x; - screen->last_rendered.cursor_y = cursor->y; - } - #define update_selection_data { \ sz = (size_t)screen->lines * screen->columns; \ address = alloc_and_map_vao_buffer(vao_idx, sz, selection_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY); \ @@ -607,6 +602,7 @@ cell_prepare_to_render(ssize_t vao_idx, Screen *screen, FONTS_DATA_HANDLE fonts_ #undef update_cell_data screen->last_rendered.columns = screen->columns; screen->last_rendered.lines = screen->lines; + screen->last_rendered.cursor = screen->cursor_render_info; return changed; } diff --git a/kitty/state.h b/kitty/state.h index 0f6391950..6498be60b 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -193,8 +193,6 @@ typedef struct WindowBarData { typedef struct Window { id_type id; bool visible; - float cursor_opacity_at_last_render; - CursorShape last_cursor_shape; PyObject *title; WindowRenderData render_data; WindowLogoRenderData window_logo;