From c3eb48fc8afad5284263786e01d83728ea1c3d8b Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sat, 12 Oct 2024 19:15:40 +0900 Subject: [PATCH 01/13] add cursor trail --- kitty/child-monitor.c | 60 ++++++++++++++++++++++++++++++++++++++- kitty/data-types.h | 1 + kitty/fast_data_types.pyi | 3 ++ kitty/screen.c | 1 + kitty/shaders.c | 42 +++++++++++++++++++++++++-- kitty/shaders.py | 5 ++++ kitty/state.h | 8 ++++++ kitty/trail_fragment.glsl | 6 ++++ kitty/trail_vertex.glsl | 6 ++++ 9 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 kitty/trail_fragment.glsl create mode 100644 kitty/trail_vertex.glsl diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 2824679f3e8..3113ba52e9c 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -705,6 +705,63 @@ change_menubar_title(PyObject *title UNUSED) { #endif } +inline static float +norm(float x, float y) { + return sqrtf(x * x + y * y); +} + +static bool +update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { +#define WD w->render_data + // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner. + // equivalent to exponential ease out animation. + static const int ci[4][2] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; + float cursor_edge_x[2], cursor_edge_y[2]; + cursor_edge_x[0] = WD.xstart + WD.screen->cursor_render_info.x * WD.dx; + cursor_edge_x[1] = cursor_edge_x[0] + WD.dx; + cursor_edge_y[0] = WD.ystart - WD.screen->cursor_render_info.y * WD.dy; + cursor_edge_y[1] = cursor_edge_y[0] - WD.dy; + + // todo - make these configurable + // the decay time for the trail to reach 1/1024 of its distance from the cursor corner + float decay_fast = 0.10f; + float decay_slow = 0.40f; + + if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { + float cursor_center_x = (cursor_edge_x[0] + cursor_edge_x[1]) * 0.5f; + float cursor_center_y = (cursor_edge_y[0] + cursor_edge_y[1]) * 0.5f; + float cursor_diag_2 = norm(cursor_edge_x[1] - cursor_edge_x[0], cursor_edge_y[1] - cursor_edge_y[0]) * 0.5; + float dt = monotonic_t_to_s_double(now - ct->updated_at); + + for (int i = 0; i < 4; ++i) { + float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; + float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; + float dist = norm(dx, dy); + if (dist == 0) { + continue; + } + float dot = (dx * (cursor_edge_x[ci[i][0]] - cursor_center_x) + + dy * (cursor_edge_y[ci[i][1]] - cursor_center_y)) / + cursor_diag_2 / dist; + + float decay_seconds = decay_slow + (decay_fast - decay_slow) * (1.0f + dot) * 0.5f; + float step = 1.0f - 1.0f / exp2f(10.0f * dt / decay_seconds); + + ct->corner_x[i] += dx * step; + ct->corner_y[i] += dy * step; + } + } + ct->updated_at = now; + for (int i = 0; i < 4; ++i) { + float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; + float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; + if (dx * dx + dy * dy >= 1e-6) { + return true; + } + } + return false; +} + static bool prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg, bool scan_for_animated_images) { #define TD os_window->tab_bar_render_data @@ -728,7 +785,6 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * color_type first_window_bg = 0; for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; -#define WD w->render_data if (w->visible && WD.screen) { screen_check_pause_rendering(WD.screen, now); *num_visible_windows += 1; @@ -751,6 +807,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * WD.screen->cursor_render_info.is_focused = os_window->is_focused; set_os_window_title_from_window(w, os_window); *active_window_bg = window_bg; + if (true && update_cursor_trail(&tab->cursor_trail, w, now)) needs_render = true; } else { if (WD.screen->cursor_render_info.render_even_when_unfocused) { if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true; @@ -808,6 +865,7 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape; } } + if (true) draw_trail(&tab->cursor_trail); if (os_window->live_resize.in_progress) draw_resizing_text(os_window); swap_window_buffers(os_window); os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id; diff --git a/kitty/data-types.h b/kitty/data-types.h index a2d348ff6b5..8ec97ab92ed 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -297,6 +297,7 @@ typedef struct { PyObject_HEAD bool bold, italic, reverse, strikethrough, dim, non_blinking; + monotonic_t updated_at; unsigned int x, y; uint8_t decoration; CursorShape shape; diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 8fcd806da4a..4b0e8a0aec8 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -296,6 +296,7 @@ FC_WIDTH_NORMAL: int FC_SLANT_ROMAN: int FC_SLANT_ITALIC: int BORDERS_PROGRAM: int +TRAIL_PROGRAM: int PRESS: int RELEASE: int DRAG: int @@ -540,6 +541,8 @@ def add_borders_rect( def init_borders_program() -> None: pass +def init_trail_program() -> None: + pass def os_window_has_background_image(os_window_id: int) -> bool: pass diff --git a/kitty/screen.c b/kitty/screen.c index 4abf3441e10..cec584db15a 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1765,6 +1765,7 @@ screen_cursor_position(Screen *self, unsigned int line, unsigned int column) { line += self->margin_top; line = MAX(self->margin_top, MIN(line, self->margin_bottom)); } + self->cursor->updated_at = monotonic(); self->cursor->x = column; self->cursor->y = line; screen_ensure_bounds(self, false, in_margins); } diff --git a/kitty/shaders.c b/kitty/shaders.c index 92957b674a1..25fb2f20d52 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -18,7 +18,7 @@ #define BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // blending onto opaque colors with final color having alpha 1 #define BLEND_PREMULT glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blending of pre-multiplied colors -enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BGIMAGE_PROGRAM, TINT_PROGRAM, NUM_PROGRAMS }; +enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BGIMAGE_PROGRAM, TINT_PROGRAM, TRAIL_PROGRAM, NUM_PROGRAMS }; enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BGIMAGE_UNIT }; // Sprites {{{ @@ -1133,6 +1133,41 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu // }}} +// Cursor Trail {{{ +typedef struct { + TrailUniforms uniforms; +} TrailProgramLayout; +static TrailProgramLayout trail_program_layout; + +static void +init_trail_program(void) { + get_uniform_locations_trail(TRAIL_PROGRAM, &trail_program_layout.uniforms); +} + +void +draw_trail(CursorTrail *trail) { + glEnable(GL_BLEND); + // BLEND_PREMULT + /*glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);*/ + BLEND_ONTO_OPAQUE; + + bind_program(TRAIL_PROGRAM); + + glUniform4fv(trail_program_layout.uniforms.x_coords, 1, trail->corner_x); + glUniform4fv(trail_program_layout.uniforms.y_coords, 1, trail->corner_y); + + // todo - get cursor color from opt + float trail_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + glUniform4fv(trail_program_layout.uniforms.trail_color, 1, trail_color); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glDisable(GL_BLEND); + unbind_program(); +} + +// }}} + // Python API {{{ static bool @@ -1205,6 +1240,8 @@ NO_ARG(init_borders_program) NO_ARG(init_cell_program) +NO_ARG(init_trail_program) + static PyObject* sprite_map_set_limits(PyObject UNUSED *self, PyObject *args) { unsigned int w, h; @@ -1229,6 +1266,7 @@ static PyMethodDef module_methods[] = { MW(unbind_program, METH_NOARGS), MW(init_borders_program, METH_NOARGS), MW(init_cell_program, METH_NOARGS), + MW(init_trail_program, METH_NOARGS), {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -1241,7 +1279,7 @@ finalize(void) { bool init_shaders(PyObject *module) { #define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; } - C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM); C(BGIMAGE_PROGRAM); C(TINT_PROGRAM); + C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM); C(BGIMAGE_PROGRAM); C(TINT_PROGRAM); C(TRAIL_PROGRAM); C(GLSL_VERSION); C(GL_VERSION); C(GL_VENDOR); diff --git a/kitty/shaders.py b/kitty/shaders.py index fef40cde162..bea5fcb3cc0 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -27,9 +27,11 @@ REVERSE, STRIKETHROUGH, TINT_PROGRAM, + TRAIL_PROGRAM, compile_program, get_options, init_cell_program, + init_trail_program, ) @@ -201,5 +203,8 @@ def resolve_graphics_fragment_defines(which: str, f: str) -> str: program_for('tint').compile(TINT_PROGRAM, allow_recompile) init_cell_program() + program_for('trail').compile(TRAIL_PROGRAM, allow_recompile) + init_trail_program() + load_shader_programs = LoadShaderPrograms() diff --git a/kitty/state.h b/kitty/state.h index efae4d90963..02afa7d40f6 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -212,11 +212,18 @@ typedef struct { ssize_t vao_idx; } BorderRects; +typedef struct { + monotonic_t updated_at; + float corner_x[4]; + float corner_y[4]; +} CursorTrail; + typedef struct { id_type id; unsigned int active_window, num_windows, capacity; Window *windows; BorderRects border_rects; + CursorTrail cursor_trail; } Tab; enum RENDER_STATE { RENDER_FRAME_NOT_REQUESTED, RENDER_FRAME_REQUESTED, RENDER_FRAME_READY }; @@ -352,6 +359,7 @@ ssize_t create_border_vao(void); bool send_cell_data_to_gpu(ssize_t, float, float, float, float, Screen *, OSWindow *); void draw_cells(ssize_t, const WindowRenderData*, OSWindow *, bool, bool, bool, Window*); void draw_centered_alpha_mask(OSWindow *w, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas, float); +void draw_trail(CursorTrail *trail); void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); void free_framebuffer(uint32_t*); diff --git a/kitty/trail_fragment.glsl b/kitty/trail_fragment.glsl new file mode 100644 index 00000000000..9b516b45586 --- /dev/null +++ b/kitty/trail_fragment.glsl @@ -0,0 +1,6 @@ +uniform vec4 trail_color; +out vec4 color; + +void main() { + color = trail_color; +} diff --git a/kitty/trail_vertex.glsl b/kitty/trail_vertex.glsl new file mode 100644 index 00000000000..781db88052c --- /dev/null +++ b/kitty/trail_vertex.glsl @@ -0,0 +1,6 @@ +uniform vec4 x_coords; +uniform vec4 y_coords; + +void main() { + gl_Position = vec4(x_coords[gl_VertexID], y_coords[gl_VertexID], -1.0, 1.0); +} From 761fb6f233cb89889b04ae791842ea67ff772461 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 13:48:02 +0900 Subject: [PATCH 02/13] move update_cursor_trail out of child_monitor_c --- kitty/child-monitor.c | 60 ++----------------------------------------- kitty/cursor_trail.c | 58 +++++++++++++++++++++++++++++++++++++++++ kitty/shaders.c | 2 +- kitty/state.h | 3 ++- 4 files changed, 63 insertions(+), 60 deletions(-) create mode 100644 kitty/cursor_trail.c diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 3113ba52e9c..bde96ccaddd 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -705,63 +705,6 @@ change_menubar_title(PyObject *title UNUSED) { #endif } -inline static float -norm(float x, float y) { - return sqrtf(x * x + y * y); -} - -static bool -update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { -#define WD w->render_data - // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner. - // equivalent to exponential ease out animation. - static const int ci[4][2] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; - float cursor_edge_x[2], cursor_edge_y[2]; - cursor_edge_x[0] = WD.xstart + WD.screen->cursor_render_info.x * WD.dx; - cursor_edge_x[1] = cursor_edge_x[0] + WD.dx; - cursor_edge_y[0] = WD.ystart - WD.screen->cursor_render_info.y * WD.dy; - cursor_edge_y[1] = cursor_edge_y[0] - WD.dy; - - // todo - make these configurable - // the decay time for the trail to reach 1/1024 of its distance from the cursor corner - float decay_fast = 0.10f; - float decay_slow = 0.40f; - - if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { - float cursor_center_x = (cursor_edge_x[0] + cursor_edge_x[1]) * 0.5f; - float cursor_center_y = (cursor_edge_y[0] + cursor_edge_y[1]) * 0.5f; - float cursor_diag_2 = norm(cursor_edge_x[1] - cursor_edge_x[0], cursor_edge_y[1] - cursor_edge_y[0]) * 0.5; - float dt = monotonic_t_to_s_double(now - ct->updated_at); - - for (int i = 0; i < 4; ++i) { - float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; - float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; - float dist = norm(dx, dy); - if (dist == 0) { - continue; - } - float dot = (dx * (cursor_edge_x[ci[i][0]] - cursor_center_x) + - dy * (cursor_edge_y[ci[i][1]] - cursor_center_y)) / - cursor_diag_2 / dist; - - float decay_seconds = decay_slow + (decay_fast - decay_slow) * (1.0f + dot) * 0.5f; - float step = 1.0f - 1.0f / exp2f(10.0f * dt / decay_seconds); - - ct->corner_x[i] += dx * step; - ct->corner_y[i] += dy * step; - } - } - ct->updated_at = now; - for (int i = 0; i < 4; ++i) { - float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; - float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; - if (dx * dx + dy * dy >= 1e-6) { - return true; - } - } - return false; -} - static bool prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg, bool scan_for_animated_images) { #define TD os_window->tab_bar_render_data @@ -785,6 +728,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * color_type first_window_bg = 0; for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; +#define WD w->render_data if (w->visible && WD.screen) { screen_check_pause_rendering(WD.screen, now); *num_visible_windows += 1; @@ -865,7 +809,7 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape; } } - if (true) draw_trail(&tab->cursor_trail); + if (true) draw_cursor_trail(&tab->cursor_trail); if (os_window->live_resize.in_progress) draw_resizing_text(os_window); swap_window_buffers(os_window); os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id; diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c new file mode 100644 index 00000000000..97d1587e592 --- /dev/null +++ b/kitty/cursor_trail.c @@ -0,0 +1,58 @@ +#include "state.h" + +inline static float +norm(float x, float y) { + return sqrtf(x * x + y * y); +} + +bool +update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { +#define WD w->render_data + // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner. + // equivalent to exponential ease out animation. + static const int ci[4][2] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; + float cursor_edge_x[2], cursor_edge_y[2]; + cursor_edge_x[0] = WD.xstart + WD.screen->cursor_render_info.x * WD.dx; + cursor_edge_x[1] = cursor_edge_x[0] + WD.dx; + cursor_edge_y[0] = WD.ystart - WD.screen->cursor_render_info.y * WD.dy; + cursor_edge_y[1] = cursor_edge_y[0] - WD.dy; + + // todo - make these configurable + // the decay time for the trail to reach 1/1024 of its distance from the cursor corner + float decay_fast = 0.10f; + float decay_slow = 0.40f; + + if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { + float cursor_center_x = (cursor_edge_x[0] + cursor_edge_x[1]) * 0.5f; + float cursor_center_y = (cursor_edge_y[0] + cursor_edge_y[1]) * 0.5f; + float cursor_diag_2 = norm(cursor_edge_x[1] - cursor_edge_x[0], cursor_edge_y[1] - cursor_edge_y[0]) * 0.5; + float dt = monotonic_t_to_s_double(now - ct->updated_at); + + for (int i = 0; i < 4; ++i) { + float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; + float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; + float dist = norm(dx, dy); + if (dist == 0) { + continue; + } + float dot = (dx * (cursor_edge_x[ci[i][0]] - cursor_center_x) + + dy * (cursor_edge_y[ci[i][1]] - cursor_center_y)) / + cursor_diag_2 / dist; + + float decay_seconds = decay_slow + (decay_fast - decay_slow) * (1.0f + dot) * 0.5f; + float step = 1.0f - 1.0f / exp2f(10.0f * dt / decay_seconds); + + ct->corner_x[i] += dx * step; + ct->corner_y[i] += dy * step; + } + } + ct->updated_at = now; + for (int i = 0; i < 4; ++i) { + float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; + float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; + if (dx * dx + dy * dy >= 1e-6) { + return true; + } + } + return false; +} diff --git a/kitty/shaders.c b/kitty/shaders.c index 25fb2f20d52..3af97735210 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -1145,7 +1145,7 @@ init_trail_program(void) { } void -draw_trail(CursorTrail *trail) { +draw_cursor_trail(CursorTrail *trail) { glEnable(GL_BLEND); // BLEND_PREMULT /*glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);*/ diff --git a/kitty/state.h b/kitty/state.h index 02afa7d40f6..0f46dfa1e6b 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -359,7 +359,8 @@ ssize_t create_border_vao(void); bool send_cell_data_to_gpu(ssize_t, float, float, float, float, Screen *, OSWindow *); void draw_cells(ssize_t, const WindowRenderData*, OSWindow *, bool, bool, bool, Window*); void draw_centered_alpha_mask(OSWindow *w, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas, float); -void draw_trail(CursorTrail *trail); +void draw_cursor_trail(CursorTrail *trail); +bool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now); void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); void free_framebuffer(uint32_t*); From 45fb7b5026e9443a03df13c10036dd52f7c3f91e Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 14:36:10 +0900 Subject: [PATCH 03/13] cursor trail support cursor shape beam and underline --- kitty/cursor_trail.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index 97d1587e592..7540f830ed8 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -5,17 +5,40 @@ norm(float x, float y) { return sqrtf(x * x + y * y); } +inline static bool +get_cursor_edge(float *left, float *right, float *top, float *bottom, Window *w) { +#define WD w->render_data + *left = WD.xstart + WD.screen->cursor_render_info.x * WD.dx; + *bottom = WD.ystart - (WD.screen->cursor_render_info.y + 1) * WD.dy; + switch (WD.screen->cursor->shape) { + case CURSOR_BLOCK: + *right = *left + WD.dx; + *top = *bottom + WD.dy; + return true; + case CURSOR_BEAM: + *right = *left + WD.dx / WD.screen->cell_size.width * OPT(cursor_beam_thickness); + *top = *bottom + WD.dy; + return true; + case CURSOR_UNDERLINE: + *right = *left + WD.dx; + *top = *bottom + WD.dy / WD.screen->cell_size.height * OPT(cursor_underline_thickness); + return true; + case CURSOR_HOLLOW: + // TODO - implement + default: + return false; + } +} + bool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { -#define WD w->render_data // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner. // equivalent to exponential ease out animation. static const int ci[4][2] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; float cursor_edge_x[2], cursor_edge_y[2]; - cursor_edge_x[0] = WD.xstart + WD.screen->cursor_render_info.x * WD.dx; - cursor_edge_x[1] = cursor_edge_x[0] + WD.dx; - cursor_edge_y[0] = WD.ystart - WD.screen->cursor_render_info.y * WD.dy; - cursor_edge_y[1] = cursor_edge_y[0] - WD.dy; + if (!get_cursor_edge(cursor_edge_x, cursor_edge_x + 1, cursor_edge_y, cursor_edge_y + 1, w)) { + return false; + } // todo - make these configurable // the decay time for the trail to reach 1/1024 of its distance from the cursor corner @@ -54,5 +77,6 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { return true; } } +#undef WD return false; } From 2e716cd4beae58107f3f1edbe5c3ee65209789e8 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 14:58:07 +0900 Subject: [PATCH 04/13] use pixel size for threshold instead of arbitrary number --- kitty/cursor_trail.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index 7540f830ed8..e8d308062fe 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -34,17 +34,21 @@ bool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner. // equivalent to exponential ease out animation. + static const int ci[4][2] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; + const float dx_threshold = WD.dx / WD.screen->cell_size.width; + const float dy_threshold = WD.dy / WD.screen->cell_size.height; + float cursor_edge_x[2], cursor_edge_y[2]; - if (!get_cursor_edge(cursor_edge_x, cursor_edge_x + 1, cursor_edge_y, cursor_edge_y + 1, w)) { - return false; + if (!get_cursor_edge(&cursor_edge_x[0], &cursor_edge_x[1], + &cursor_edge_y[0], &cursor_edge_y[1], w)) { + return false; } // todo - make these configurable // the decay time for the trail to reach 1/1024 of its distance from the cursor corner float decay_fast = 0.10f; float decay_slow = 0.40f; - if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { float cursor_center_x = (cursor_edge_x[0] + cursor_edge_x[1]) * 0.5f; float cursor_center_y = (cursor_edge_y[0] + cursor_edge_y[1]) * 0.5f; @@ -54,13 +58,17 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { for (int i = 0; i < 4; ++i) { float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; - float dist = norm(dx, dy); - if (dist == 0) { + if (fabsf(dx) < dx_threshold && fabsf(dy) < dy_threshold) { + ct->corner_x[i] = cursor_edge_x[ci[i][0]]; + ct->corner_y[i] = cursor_edge_y[ci[i][1]]; continue; } + + // Corner that is closer to the cursor moves faster. + // It creates dynamic effect that looks like the trail is being pulled towards the cursor. float dot = (dx * (cursor_edge_x[ci[i][0]] - cursor_center_x) + dy * (cursor_edge_y[ci[i][1]] - cursor_center_y)) / - cursor_diag_2 / dist; + cursor_diag_2 / norm(dx, dy); float decay_seconds = decay_slow + (decay_fast - decay_slow) * (1.0f + dot) * 0.5f; float step = 1.0f - 1.0f / exp2f(10.0f * dt / decay_seconds); @@ -71,10 +79,10 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { } ct->updated_at = now; for (int i = 0; i < 4; ++i) { - float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; - float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; - if (dx * dx + dy * dy >= 1e-6) { - return true; + float dx = fabsf(cursor_edge_x[ci[i][0]] - ct->corner_x[i]); + float dy = fabsf(cursor_edge_y[ci[i][1]] - ct->corner_y[i]); + if (dx_threshold <= dx || dy_threshold <= dy) { + return true; } } #undef WD From 89b9c6da8bb27974ee718b802c1b61120d769b97 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 16:37:52 +0900 Subject: [PATCH 05/13] don't draw cursor trail over the real cursor --- kitty/child-monitor.c | 2 +- kitty/cursor_trail.c | 39 +++++++++++++++++++++------------------ kitty/shaders.c | 6 +++--- kitty/state.h | 3 +++ kitty/trail_fragment.glsl | 12 ++++++++++-- kitty/trail_vertex.glsl | 5 ++++- 6 files changed, 42 insertions(+), 25 deletions(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index bde96ccaddd..7464a0d3e01 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -809,7 +809,7 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape; } } - if (true) draw_cursor_trail(&tab->cursor_trail); + if (true && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail); if (os_window->live_resize.in_progress) draw_resizing_text(os_window); swap_window_buffers(os_window); os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id; diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index e8d308062fe..abe4f441aee 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -38,36 +38,37 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { static const int ci[4][2] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}}; const float dx_threshold = WD.dx / WD.screen->cell_size.width; const float dy_threshold = WD.dy / WD.screen->cell_size.height; + bool needs_render_prev = ct->needs_render; + ct->needs_render = false; - float cursor_edge_x[2], cursor_edge_y[2]; - if (!get_cursor_edge(&cursor_edge_x[0], &cursor_edge_x[1], - &cursor_edge_y[0], &cursor_edge_y[1], w)) { - return false; + if (!get_cursor_edge(&ct->cursor_edge_x[0], &ct->cursor_edge_x[1], + &ct->cursor_edge_y[0], &ct->cursor_edge_y[1], w)) { + return needs_render_prev; } // todo - make these configurable // the decay time for the trail to reach 1/1024 of its distance from the cursor corner float decay_fast = 0.10f; - float decay_slow = 0.40f; + float decay_slow = 0.80f; if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { - float cursor_center_x = (cursor_edge_x[0] + cursor_edge_x[1]) * 0.5f; - float cursor_center_y = (cursor_edge_y[0] + cursor_edge_y[1]) * 0.5f; - float cursor_diag_2 = norm(cursor_edge_x[1] - cursor_edge_x[0], cursor_edge_y[1] - cursor_edge_y[0]) * 0.5; + float cursor_center_x = (ct->cursor_edge_x[0] + ct->cursor_edge_x[1]) * 0.5f; + float cursor_center_y = (ct->cursor_edge_y[0] + ct->cursor_edge_y[1]) * 0.5f; + float cursor_diag_2 = norm(ct->cursor_edge_x[1] - ct->cursor_edge_x[0], ct->cursor_edge_y[1] - ct->cursor_edge_y[0]) * 0.5; float dt = monotonic_t_to_s_double(now - ct->updated_at); for (int i = 0; i < 4; ++i) { - float dx = cursor_edge_x[ci[i][0]] - ct->corner_x[i]; - float dy = cursor_edge_y[ci[i][1]] - ct->corner_y[i]; + float dx = ct->cursor_edge_x[ci[i][0]] - ct->corner_x[i]; + float dy = ct->cursor_edge_y[ci[i][1]] - ct->corner_y[i]; if (fabsf(dx) < dx_threshold && fabsf(dy) < dy_threshold) { - ct->corner_x[i] = cursor_edge_x[ci[i][0]]; - ct->corner_y[i] = cursor_edge_y[ci[i][1]]; + ct->corner_x[i] = ct->cursor_edge_x[ci[i][0]]; + ct->corner_y[i] = ct->cursor_edge_y[ci[i][1]]; continue; } // Corner that is closer to the cursor moves faster. // It creates dynamic effect that looks like the trail is being pulled towards the cursor. - float dot = (dx * (cursor_edge_x[ci[i][0]] - cursor_center_x) + - dy * (cursor_edge_y[ci[i][1]] - cursor_center_y)) / + float dot = (dx * (ct->cursor_edge_x[ci[i][0]] - cursor_center_x) + + dy * (ct->cursor_edge_y[ci[i][1]] - cursor_center_y)) / cursor_diag_2 / norm(dx, dy); float decay_seconds = decay_slow + (decay_fast - decay_slow) * (1.0f + dot) * 0.5f; @@ -79,12 +80,14 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { } ct->updated_at = now; for (int i = 0; i < 4; ++i) { - float dx = fabsf(cursor_edge_x[ci[i][0]] - ct->corner_x[i]); - float dy = fabsf(cursor_edge_y[ci[i][1]] - ct->corner_y[i]); + float dx = fabsf(ct->cursor_edge_x[ci[i][0]] - ct->corner_x[i]); + float dy = fabsf(ct->cursor_edge_y[ci[i][1]] - ct->corner_y[i]); if (dx_threshold <= dx || dy_threshold <= dy) { - return true; + ct->needs_render = true; + break; } } #undef WD - return false; + // returning true here will cause the cells to be drawn + return ct->needs_render || needs_render_prev; } diff --git a/kitty/shaders.c b/kitty/shaders.c index 3af97735210..da8c2614ef6 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -1147,15 +1147,15 @@ init_trail_program(void) { void draw_cursor_trail(CursorTrail *trail) { glEnable(GL_BLEND); - // BLEND_PREMULT - /*glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);*/ BLEND_ONTO_OPAQUE; - bind_program(TRAIL_PROGRAM); glUniform4fv(trail_program_layout.uniforms.x_coords, 1, trail->corner_x); glUniform4fv(trail_program_layout.uniforms.y_coords, 1, trail->corner_y); + glUniform2fv(trail_program_layout.uniforms.cursor_edge_x, 1, trail->cursor_edge_x); + glUniform2fv(trail_program_layout.uniforms.cursor_edge_y, 1, trail->cursor_edge_y); + // todo - get cursor color from opt float trail_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; glUniform4fv(trail_program_layout.uniforms.trail_color, 1, trail_color); diff --git a/kitty/state.h b/kitty/state.h index 0f46dfa1e6b..ae6f83b1ad7 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -214,8 +214,11 @@ typedef struct { typedef struct { monotonic_t updated_at; + bool needs_render; float corner_x[4]; float corner_y[4]; + float cursor_edge_x[2]; + float cursor_edge_y[2]; } CursorTrail; typedef struct { diff --git a/kitty/trail_fragment.glsl b/kitty/trail_fragment.glsl index 9b516b45586..5f64ee230e5 100644 --- a/kitty/trail_fragment.glsl +++ b/kitty/trail_fragment.glsl @@ -1,6 +1,14 @@ +uniform vec2 cursor_edge_x; +uniform vec2 cursor_edge_y; uniform vec4 trail_color; -out vec4 color; +in vec2 frag_pos; +out vec4 final_color; void main() { - color = trail_color; + if (cursor_edge_x[0] <= frag_pos.x && frag_pos.x <= cursor_edge_x[1] && + cursor_edge_y[1] <= frag_pos.y && frag_pos.y <= cursor_edge_y[0]) { + discard; + } else { + final_color = trail_color; + } } diff --git a/kitty/trail_vertex.glsl b/kitty/trail_vertex.glsl index 781db88052c..a4610bebfa2 100644 --- a/kitty/trail_vertex.glsl +++ b/kitty/trail_vertex.glsl @@ -1,6 +1,9 @@ uniform vec4 x_coords; uniform vec4 y_coords; +out vec2 frag_pos; + void main() { - gl_Position = vec4(x_coords[gl_VertexID], y_coords[gl_VertexID], -1.0, 1.0); + frag_pos = vec2(x_coords[gl_VertexID], y_coords[gl_VertexID]); + gl_Position = vec4(x_coords[gl_VertexID], y_coords[gl_VertexID], 1.0, 1.0); } From fbd596a61f6cf747c63412c983797f945c20e632 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 18:10:24 +0900 Subject: [PATCH 06/13] set trail color with cursor bg color --- kitty/cursor_trail.c | 11 ++++++++--- kitty/shaders.c | 7 +------ kitty/state.h | 3 ++- kitty/trail_fragment.glsl | 5 +++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index abe4f441aee..429597eabbb 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -12,6 +12,7 @@ get_cursor_edge(float *left, float *right, float *top, float *bottom, Window *w) *bottom = WD.ystart - (WD.screen->cursor_render_info.y + 1) * WD.dy; switch (WD.screen->cursor->shape) { case CURSOR_BLOCK: + case CURSOR_HOLLOW: *right = *left + WD.dx; *top = *bottom + WD.dy; return true; @@ -23,8 +24,6 @@ get_cursor_edge(float *left, float *right, float *top, float *bottom, Window *w) *right = *left + WD.dx; *top = *bottom + WD.dy / WD.screen->cell_size.height * OPT(cursor_underline_thickness); return true; - case CURSOR_HOLLOW: - // TODO - implement default: return false; } @@ -49,7 +48,7 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { // todo - make these configurable // the decay time for the trail to reach 1/1024 of its distance from the cursor corner float decay_fast = 0.10f; - float decay_slow = 0.80f; + float decay_slow = 0.30f; if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { float cursor_center_x = (ct->cursor_edge_x[0] + ct->cursor_edge_x[1]) * 0.5f; float cursor_center_y = (ct->cursor_edge_y[0] + ct->cursor_edge_y[1]) * 0.5f; @@ -87,6 +86,12 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { break; } } + + if (ct->needs_render) { + ColorProfile *cp = WD.screen->color_profile; + ct->color = colorprofile_to_color(cp, cp->overridden.cursor_color, cp->configured.cursor_color).rgb; + } + #undef WD // returning true here will cause the cells to be drawn return ct->needs_render || needs_render_prev; diff --git a/kitty/shaders.c b/kitty/shaders.c index da8c2614ef6..38a5cc3ab5a 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -1146,8 +1146,6 @@ init_trail_program(void) { void draw_cursor_trail(CursorTrail *trail) { - glEnable(GL_BLEND); - BLEND_ONTO_OPAQUE; bind_program(TRAIL_PROGRAM); glUniform4fv(trail_program_layout.uniforms.x_coords, 1, trail->corner_x); @@ -1156,13 +1154,10 @@ draw_cursor_trail(CursorTrail *trail) { glUniform2fv(trail_program_layout.uniforms.cursor_edge_x, 1, trail->cursor_edge_x); glUniform2fv(trail_program_layout.uniforms.cursor_edge_y, 1, trail->cursor_edge_y); - // todo - get cursor color from opt - float trail_color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - glUniform4fv(trail_program_layout.uniforms.trail_color, 1, trail_color); + color_vec3(trail_program_layout.uniforms.trail_color, trail->color); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisable(GL_BLEND); unbind_program(); } diff --git a/kitty/state.h b/kitty/state.h index ae6f83b1ad7..b9d487c7c63 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -213,8 +213,9 @@ typedef struct { } BorderRects; typedef struct { - monotonic_t updated_at; bool needs_render; + monotonic_t updated_at; + color_type color; float corner_x[4]; float corner_y[4]; float cursor_edge_x[2]; diff --git a/kitty/trail_fragment.glsl b/kitty/trail_fragment.glsl index 5f64ee230e5..b9dadb866a4 100644 --- a/kitty/trail_fragment.glsl +++ b/kitty/trail_fragment.glsl @@ -1,6 +1,7 @@ uniform vec2 cursor_edge_x; uniform vec2 cursor_edge_y; -uniform vec4 trail_color; +uniform vec3 trail_color; + in vec2 frag_pos; out vec4 final_color; @@ -9,6 +10,6 @@ void main() { cursor_edge_y[1] <= frag_pos.y && frag_pos.y <= cursor_edge_y[0]) { discard; } else { - final_color = trail_color; + final_color = vec4(trail_color, 1.0); } } From 20ba5e6918150e7dee90dfa2896635cb9a5f2349 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 18:15:42 +0900 Subject: [PATCH 07/13] disable cursor trail while live resize in progress --- kitty/child-monitor.c | 2 +- kitty/cursor_trail.c | 11 +++++++++-- kitty/state.h | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 7464a0d3e01..fda5470a16d 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -751,7 +751,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * WD.screen->cursor_render_info.is_focused = os_window->is_focused; set_os_window_title_from_window(w, os_window); *active_window_bg = window_bg; - if (true && update_cursor_trail(&tab->cursor_trail, w, now)) needs_render = true; + if (true && update_cursor_trail(&tab->cursor_trail, w, now, os_window)) needs_render = true; } else { if (WD.screen->cursor_render_info.render_even_when_unfocused) { if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true; diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index 429597eabbb..5c84bf4cee4 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -30,7 +30,7 @@ get_cursor_edge(float *left, float *right, float *top, float *bottom, Window *w) } bool -update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { +update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window) { // the trail corners move towards the cursor corner at a speed proportional to their distance from the cursor corner. // equivalent to exponential ease out animation. @@ -49,7 +49,14 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now) { // the decay time for the trail to reach 1/1024 of its distance from the cursor corner float decay_fast = 0.10f; float decay_slow = 0.30f; - if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { + + if (os_window->live_resize.in_progress) { + for (int i = 0; i < 4; ++i) { + ct->corner_x[i] = ct->cursor_edge_x[ci[i][0]]; + ct->corner_y[i] = ct->cursor_edge_y[ci[i][1]]; + } + } + else if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { float cursor_center_x = (ct->cursor_edge_x[0] + ct->cursor_edge_x[1]) * 0.5f; float cursor_center_y = (ct->cursor_edge_y[0] + ct->cursor_edge_y[1]) * 0.5f; float cursor_diag_2 = norm(ct->cursor_edge_x[1] - ct->cursor_edge_x[0], ct->cursor_edge_y[1] - ct->cursor_edge_y[0]) * 0.5; diff --git a/kitty/state.h b/kitty/state.h index b9d487c7c63..68f482fb70d 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -364,7 +364,7 @@ bool send_cell_data_to_gpu(ssize_t, float, float, float, float, Screen *, OSWind void draw_cells(ssize_t, const WindowRenderData*, OSWindow *, bool, bool, bool, Window*); void draw_centered_alpha_mask(OSWindow *w, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas, float); void draw_cursor_trail(CursorTrail *trail); -bool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now); +bool update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_window); void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); void free_framebuffer(uint32_t*); From 5c5a3c32cfe47d4ca025b265ad9f1334d2436e9c Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 18:46:45 +0900 Subject: [PATCH 08/13] fix - trail now visible when cursor is underline --- kitty/cursor_trail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index 5c84bf4cee4..19ce6c85860 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -10,7 +10,7 @@ get_cursor_edge(float *left, float *right, float *top, float *bottom, Window *w) #define WD w->render_data *left = WD.xstart + WD.screen->cursor_render_info.x * WD.dx; *bottom = WD.ystart - (WD.screen->cursor_render_info.y + 1) * WD.dy; - switch (WD.screen->cursor->shape) { + switch (WD.screen->cursor_render_info.shape) { case CURSOR_BLOCK: case CURSOR_HOLLOW: *right = *left + WD.dx; From 2bd008f8ca463202b9d2d2e03ed927b70faf53a8 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Sun, 13 Oct 2024 21:10:20 +0900 Subject: [PATCH 09/13] expose cursor_trail options --- kitty/child-monitor.c | 4 ++-- kitty/cursor_trail.c | 5 ++--- kitty/options/definition.py | 22 ++++++++++++++++++++++ kitty/options/parse.py | 8 +++++++- kitty/options/to-c-generated.h | 30 ++++++++++++++++++++++++++++++ kitty/options/to-c.h | 5 +++++ kitty/options/types.py | 4 ++++ kitty/options/utils.py | 5 +++++ kitty/state.h | 3 +++ 9 files changed, 80 insertions(+), 6 deletions(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index fda5470a16d..31b65283989 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -751,7 +751,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * WD.screen->cursor_render_info.is_focused = os_window->is_focused; set_os_window_title_from_window(w, os_window); *active_window_bg = window_bg; - if (true && update_cursor_trail(&tab->cursor_trail, w, now, os_window)) needs_render = true; + if (OPT(enable_cursor_trail) && update_cursor_trail(&tab->cursor_trail, w, now, os_window)) needs_render = true; } else { if (WD.screen->cursor_render_info.render_even_when_unfocused) { if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true; @@ -809,7 +809,7 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co w->cursor_opacity_at_last_render = WD.screen->cursor_render_info.opacity; w->last_cursor_shape = WD.screen->cursor_render_info.shape; } } - if (true && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail); + if (OPT(enable_cursor_trail) && tab->cursor_trail.needs_render) draw_cursor_trail(&tab->cursor_trail); if (os_window->live_resize.in_progress) draw_resizing_text(os_window); swap_window_buffers(os_window); os_window->last_active_tab = os_window->active_tab; os_window->last_num_tabs = os_window->num_tabs; os_window->last_active_window_id = active_window_id; diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index 19ce6c85860..a42bc75b36e 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -45,10 +45,9 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi return needs_render_prev; } - // todo - make these configurable // the decay time for the trail to reach 1/1024 of its distance from the cursor corner - float decay_fast = 0.10f; - float decay_slow = 0.30f; + float decay_fast = OPT(cursor_trail_decay_fast); + float decay_slow = OPT(cursor_trail_decay_slow); if (os_window->live_resize.in_progress) { for (int i = 0; i < 4; ++i) { diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 3380294916e..8142710ed62 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -350,6 +350,28 @@ ''' ) +opt('enable_cursor_trail', 'no', + option_type='to_bool', ctype='bool', + long_text=''' +Enables or disables the cursor trail effect. When set to `yes`, a trailing +effect is rendered behind the cursor as it moves, creating a motion trail. +''' + ) + +opt('cursor_trail_decay', '0.1 0.3', + option_type='cursor_trail_decay', + ctype='!cursor_trail_decay', + long_text=''' +Controls the decay times for the cursor trail effect when :code:`enable_cursor_trail` +is set to :code:`yes`. This option accepts two positive float values specifying the +fastest and slowest decay times in seconds. The first value corresponds to the +fastest decay time (minimum), and the second value corresponds to the slowest +decay time (maximum). The second value must be equal to or greater than the +first value. Smaller values result in a faster decay of the cursor trail. +Adjust these values to control how quickly the cursor trail fades away. +''', + ) + egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 46813e8c501..a871056ab70 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -10,7 +10,7 @@ action_alias, active_tab_title_template, allow_hyperlinks, bell_on_tab, box_drawing_scale, clear_all_mouse_actions, clear_all_shortcuts, clipboard_control, clone_source_strategies, config_or_absolute_path, copy_on_select, cursor_blink_interval, cursor_text_color, - deprecated_adjust_line_height, deprecated_hide_window_decorations_aliases, + cursor_trail_decay, deprecated_adjust_line_height, deprecated_hide_window_decorations_aliases, deprecated_macos_show_window_title_in_menubar_alias, deprecated_send_text, disable_ligatures, edge_width, env, filter_notification, font_features, hide_window_decorations, macos_option_as_alt, macos_titlebar_color, menu_map, modify_font, narrow_symbols, notify_on_cmd_finish, @@ -931,6 +931,9 @@ def cursor_stop_blinking_after(self, val: str, ans: typing.Dict[str, typing.Any] def cursor_text_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['cursor_text_color'] = cursor_text_color(val) + def cursor_trail_decay(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['cursor_trail_decay'] = cursor_trail_decay(val) + def cursor_underline_thickness(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['cursor_underline_thickness'] = positive_float(val) @@ -963,6 +966,9 @@ def editor(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: def enable_audio_bell(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['enable_audio_bell'] = to_bool(val) + def enable_cursor_trail(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['enable_cursor_trail'] = to_bool(val) + def enabled_layouts(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['enabled_layouts'] = to_layout_names(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 949905b9c2f..7eb1392e5ef 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -161,6 +161,32 @@ convert_from_opts_cursor_stop_blinking_after(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_enable_cursor_trail(PyObject *val, Options *opts) { + opts->enable_cursor_trail = PyObject_IsTrue(val); +} + +static void +convert_from_opts_enable_cursor_trail(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "enable_cursor_trail"); + if (ret == NULL) return; + convert_from_python_enable_cursor_trail(ret, opts); + Py_DECREF(ret); +} + +static void +convert_from_python_cursor_trail_decay(PyObject *val, Options *opts) { + cursor_trail_decay(val, opts); +} + +static void +convert_from_opts_cursor_trail_decay(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "cursor_trail_decay"); + if (ret == NULL) return; + convert_from_python_cursor_trail_decay(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_scrollback_indicator_opacity(PyObject *val, Options *opts) { opts->scrollback_indicator_opacity = PyFloat_AsFloat(val); @@ -1123,6 +1149,10 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_cursor_stop_blinking_after(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_enable_cursor_trail(py_opts, opts); + if (PyErr_Occurred()) return false; + convert_from_opts_cursor_trail_decay(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_scrollback_indicator_opacity(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_scrollback_pager_history_size(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 1240ba678cb..9aa5c91ba8c 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -180,6 +180,11 @@ visual_bell_duration(PyObject *src, Options *opts) { #undef parse_animation +static inline void +cursor_trail_decay(PyObject *src, Options *opts) { + opts->cursor_trail_decay_fast = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 0)); + opts->cursor_trail_decay_slow = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 1)); +} static void parse_font_mod_size(PyObject *val, float *sz, AdjustmentUnit *unit) { diff --git a/kitty/options/types.py b/kitty/options/types.py index 69a19409798..0bb9f0c99fa 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -334,6 +334,7 @@ 'cursor_shape_unfocused', 'cursor_stop_blinking_after', 'cursor_text_color', + 'cursor_trail_decay', 'cursor_underline_thickness', 'default_pointer_shape', 'detect_urls', @@ -343,6 +344,7 @@ 'dynamic_background_opacity', 'editor', 'enable_audio_bell', + 'enable_cursor_trail', 'enabled_layouts', 'env', 'exe_search_path', @@ -510,6 +512,7 @@ class Options: cursor_shape_unfocused: int = 4 cursor_stop_blinking_after: float = 15.0 cursor_text_color: typing.Optional[kitty.fast_data_types.Color] = Color(17, 17, 17) + cursor_trail_decay: typing.Tuple[float, float] = (0.1, 0.3) cursor_underline_thickness: float = 2.0 default_pointer_shape: choices_for_default_pointer_shape = 'beam' detect_urls: bool = True @@ -519,6 +522,7 @@ class Options: dynamic_background_opacity: bool = False editor: str = '.' enable_audio_bell: bool = True + enable_cursor_trail: bool = False enabled_layouts: typing.List[str] = ['fat', 'grid', 'horizontal', 'splits', 'stack', 'tall', 'vertical'] file_transfer_confirmation_bypass: str = '' focus_follows_mouse: bool = False diff --git a/kitty/options/utils.py b/kitty/options/utils.py index eb8c2cd922f..5d9aec66af8 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -558,6 +558,11 @@ def to_cursor_unfocused_shape(x: str) -> int: ) ) +def cursor_trail_decay(x: str) -> Tuple[float, float]: + fast, slow = map(positive_float, x.split()) + slow = max(slow, fast) + return fast, slow + def scrollback_lines(x: str) -> int: ans = int(x) diff --git a/kitty/state.h b/kitty/state.h index 68f482fb70d..19a5d4b721b 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -46,6 +46,9 @@ typedef struct { CursorShape cursor_shape, cursor_shape_unfocused; float cursor_beam_thickness; float cursor_underline_thickness; + bool enable_cursor_trail; + float cursor_trail_decay_fast; + float cursor_trail_decay_slow; unsigned int url_style; unsigned int scrollback_pager_history_size; bool scrollback_fill_enlarged_window; From e07bcc221809255f002861f025e6c9b1a2bfad08 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Mon, 14 Oct 2024 09:30:40 +0900 Subject: [PATCH 10/13] fix build error --- kitty/cursor_trail.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index a42bc75b36e..d207ae19ac5 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -58,8 +58,8 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi else if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { float cursor_center_x = (ct->cursor_edge_x[0] + ct->cursor_edge_x[1]) * 0.5f; float cursor_center_y = (ct->cursor_edge_y[0] + ct->cursor_edge_y[1]) * 0.5f; - float cursor_diag_2 = norm(ct->cursor_edge_x[1] - ct->cursor_edge_x[0], ct->cursor_edge_y[1] - ct->cursor_edge_y[0]) * 0.5; - float dt = monotonic_t_to_s_double(now - ct->updated_at); + float cursor_diag_2 = norm(ct->cursor_edge_x[1] - ct->cursor_edge_x[0], ct->cursor_edge_y[1] - ct->cursor_edge_y[0]) * 0.5f; + float dt = (float)monotonic_t_to_s_double(now - ct->updated_at); for (int i = 0; i < 4; ++i) { float dx = ct->cursor_edge_x[ci[i][0]] - ct->corner_x[i]; From aafc083219792aac19061fb4c60272a329e9ef15 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Mon, 14 Oct 2024 10:37:33 +0900 Subject: [PATCH 11/13] fix - trail animation not working smoothly in linux --- kitty/child-monitor.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 31b65283989..d2c6707d34a 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -751,7 +751,10 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * WD.screen->cursor_render_info.is_focused = os_window->is_focused; set_os_window_title_from_window(w, os_window); *active_window_bg = window_bg; - if (OPT(enable_cursor_trail) && update_cursor_trail(&tab->cursor_trail, w, now, os_window)) needs_render = true; + if (OPT(enable_cursor_trail) && update_cursor_trail(&tab->cursor_trail, w, now, os_window)) { + needs_render = true; + set_maximum_wait(OPT(repaint_delay)); + } } else { if (WD.screen->cursor_render_info.render_even_when_unfocused) { if (collect_cursor_info(&WD.screen->cursor_render_info, w, now, os_window)) needs_render = true; From bf7f0ebc464d7e921795f6fd29216f110e435850 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Wed, 16 Oct 2024 08:03:12 +0900 Subject: [PATCH 12/13] handle paused_rendering state --- kitty/cursor_trail.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index d207ae19ac5..3805d5bd626 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -40,7 +40,8 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi bool needs_render_prev = ct->needs_render; ct->needs_render = false; - if (!get_cursor_edge(&ct->cursor_edge_x[0], &ct->cursor_edge_x[1], + if (!WD.screen->paused_rendering.expires_at && + !get_cursor_edge(&ct->cursor_edge_x[0], &ct->cursor_edge_x[1], &ct->cursor_edge_y[0], &ct->cursor_edge_y[1], w)) { return needs_render_prev; } From c31f5cf24a7ba590a82fb1d4cbd966058624e1f9 Mon Sep 17 00:00:00 2001 From: Rick Choi Date: Wed, 16 Oct 2024 08:21:01 +0900 Subject: [PATCH 13/13] abbreviate ct->cursor_edge_x using macro --- kitty/cursor_trail.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/kitty/cursor_trail.c b/kitty/cursor_trail.c index 3805d5bd626..c90e6b6c018 100644 --- a/kitty/cursor_trail.c +++ b/kitty/cursor_trail.c @@ -40,9 +40,9 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi bool needs_render_prev = ct->needs_render; ct->needs_render = false; - if (!WD.screen->paused_rendering.expires_at && - !get_cursor_edge(&ct->cursor_edge_x[0], &ct->cursor_edge_x[1], - &ct->cursor_edge_y[0], &ct->cursor_edge_y[1], w)) { +#define EDGE(axis, index) ct->cursor_edge_##axis[index] + + if (!WD.screen->paused_rendering.expires_at && !get_cursor_edge(&EDGE(x, 0), &EDGE(x, 1), &EDGE(y, 0), &EDGE(y, 1), w)) { return needs_render_prev; } @@ -52,29 +52,29 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi if (os_window->live_resize.in_progress) { for (int i = 0; i < 4; ++i) { - ct->corner_x[i] = ct->cursor_edge_x[ci[i][0]]; - ct->corner_y[i] = ct->cursor_edge_y[ci[i][1]]; + ct->corner_x[i] = EDGE(x, ci[i][0]); + ct->corner_y[i] = EDGE(y, ci[i][1]); } } else if (OPT(input_delay) < now - WD.screen->cursor->updated_at && ct->updated_at < now) { - float cursor_center_x = (ct->cursor_edge_x[0] + ct->cursor_edge_x[1]) * 0.5f; - float cursor_center_y = (ct->cursor_edge_y[0] + ct->cursor_edge_y[1]) * 0.5f; - float cursor_diag_2 = norm(ct->cursor_edge_x[1] - ct->cursor_edge_x[0], ct->cursor_edge_y[1] - ct->cursor_edge_y[0]) * 0.5f; + float cursor_center_x = (EDGE(x, 0) + EDGE(x, 1)) * 0.5f; + float cursor_center_y = (EDGE(y, 0) + EDGE(y, 1)) * 0.5f; + float cursor_diag_2 = norm(EDGE(x, 1) - EDGE(x, 0), EDGE(y, 1) - EDGE(y, 0)) * 0.5f; float dt = (float)monotonic_t_to_s_double(now - ct->updated_at); for (int i = 0; i < 4; ++i) { - float dx = ct->cursor_edge_x[ci[i][0]] - ct->corner_x[i]; - float dy = ct->cursor_edge_y[ci[i][1]] - ct->corner_y[i]; + float dx = EDGE(x, ci[i][0]) - ct->corner_x[i]; + float dy = EDGE(y, ci[i][1]) - ct->corner_y[i]; if (fabsf(dx) < dx_threshold && fabsf(dy) < dy_threshold) { - ct->corner_x[i] = ct->cursor_edge_x[ci[i][0]]; - ct->corner_y[i] = ct->cursor_edge_y[ci[i][1]]; + ct->corner_x[i] = EDGE(x, ci[i][0]); + ct->corner_y[i] = EDGE(y, ci[i][1]); continue; } // Corner that is closer to the cursor moves faster. // It creates dynamic effect that looks like the trail is being pulled towards the cursor. - float dot = (dx * (ct->cursor_edge_x[ci[i][0]] - cursor_center_x) + - dy * (ct->cursor_edge_y[ci[i][1]] - cursor_center_y)) / + float dot = (dx * (EDGE(x, ci[i][0]) - cursor_center_x) + + dy * (EDGE(y, ci[i][1]) - cursor_center_y)) / cursor_diag_2 / norm(dx, dy); float decay_seconds = decay_slow + (decay_fast - decay_slow) * (1.0f + dot) * 0.5f; @@ -86,8 +86,8 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi } ct->updated_at = now; for (int i = 0; i < 4; ++i) { - float dx = fabsf(ct->cursor_edge_x[ci[i][0]] - ct->corner_x[i]); - float dy = fabsf(ct->cursor_edge_y[ci[i][1]] - ct->corner_y[i]); + float dx = fabsf(EDGE(x, ci[i][0]) - ct->corner_x[i]); + float dy = fabsf(EDGE(y, ci[i][1]) - ct->corner_y[i]); if (dx_threshold <= dx || dy_threshold <= dy) { ct->needs_render = true; break; @@ -100,6 +100,7 @@ update_cursor_trail(CursorTrail *ct, Window *w, monotonic_t now, OSWindow *os_wi } #undef WD +#undef EDGE // returning true here will cause the cells to be drawn return ct->needs_render || needs_render_prev; }