diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 65403b625..50a0d178c 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -416,6 +416,18 @@ animation. ''' ) +opt('cursor_trail_color', 'none', + option_type='to_color_or_none', + ctype='!cursor_trail_color', + long_text=''' +Set the color of the cursor trail when :opt:`cursor_trail` is enabled. +If set to 'none' (the default), the cursor trail will use the cursor's +background color. Otherwise, specify a color value (e.g., #ff0000 for red, +or a named color like 'red'). This allows you to customize the appearance +of the cursor trail independently of the cursor color. +''' +) + egr() # }}} # scrollback {{{ diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 6ef063129..948267f54 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -941,6 +941,9 @@ class Parser: def cursor_trail_decay(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_trail_decay'] = cursor_trail_decay(val) + def cursor_trail_color(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['cursor_trail_color'] = to_color_or_none(val) + def cursor_trail_start_threshold(self, val: str, ans: dict[str, typing.Any]) -> None: ans['cursor_trail_start_threshold'] = positive_int(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 36622a48f..fcfaf231d 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -226,6 +226,19 @@ convert_from_opts_cursor_trail_decay(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_cursor_trail_color(PyObject *val, Options *opts) { + cursor_trail_color(val, opts); +} + +static void +convert_from_opts_cursor_trail_color(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "cursor_trail_color"); + if (ret == NULL) return; + convert_from_python_cursor_trail_color(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_cursor_trail_start_threshold(PyObject *val, Options *opts) { opts->cursor_trail_start_threshold = PyLong_AsLong(val); @@ -1211,6 +1224,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_cursor_trail_decay(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_cursor_trail_color(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_cursor_trail_start_threshold(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_scrollback_indicator_opacity(py_opts, opts); diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index ecfa0c552..2089aa17c 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -215,6 +215,11 @@ cursor_trail_decay(PyObject *src, Options *opts) { opts->cursor_trail_decay_slow = PyFloat_AsFloat(PyTuple_GET_ITEM(src, 1)); } +static inline void +cursor_trail_color(PyObject *src, Options *opts) { + opts->cursor_trail_color = color_or_none_as_int(src); +} + static void parse_font_mod_size(PyObject *val, float *sz, AdjustmentUnit *unit) { PyObject *mv = PyObject_GetAttrString(val, "mod_value"); diff --git a/kitty/options/types.py b/kitty/options/types.py index a960534da..a6e857164 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -337,6 +337,7 @@ option_names = ( 'cursor_text_color', 'cursor_trail', 'cursor_trail_decay', + 'cursor_trail_color', 'cursor_trail_start_threshold', 'cursor_underline_thickness', 'default_pointer_shape', @@ -520,6 +521,7 @@ class Options: cursor_text_color: kitty.fast_data_types.Color | None = Color(17, 17, 17) cursor_trail: int = 0 cursor_trail_decay: tuple[float, float] = (0.1, 0.4) + cursor_trail_color: kitty.fast_data_types.Color | None = None cursor_trail_start_threshold: int = 2 cursor_underline_thickness: float = 2.0 default_pointer_shape: choices_for_default_pointer_shape = 'beam' @@ -1060,4 +1062,5 @@ nullable_colors = frozenset({ }) -secret_options = ('remote_control_password', 'file_transfer_confirmation_bypass') \ No newline at end of file +secret_options = ('remote_control_password', 'file_transfer_confirmation_bypass') + diff --git a/kitty/options/utils.py b/kitty/options/utils.py index 093c7f5e2..2cb44c2fd 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -576,7 +576,6 @@ def cursor_trail_decay(x: str) -> tuple[float, float]: slow = max(slow, fast) return fast, slow - def scrollback_lines(x: str) -> int: ans = int(x) if ans < 0: diff --git a/kitty/shaders.c b/kitty/shaders.c index f6c58b567..cf1e9de14 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -1252,7 +1252,12 @@ draw_cursor_trail(CursorTrail *trail, Window *active_window) { 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); - color_vec3(trail_program_layout.uniforms.trail_color, active_window ? active_window->render_data.screen->last_rendered.cursor_bg : OPT(foreground)); + color_type trail_color = OPT(cursor_trail_color); + if (trail_color == 0) { // 0 means "none" was specified + trail_color = active_window ? active_window->render_data.screen->last_rendered.cursor_bg : OPT(foreground); + } + color_vec3(trail_program_layout.uniforms.trail_color, trail_color); + glUniform1fv(trail_program_layout.uniforms.trail_opacity, 1, &trail->opacity); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); diff --git a/kitty/state.h b/kitty/state.h index 436cd536d..076ac1b44 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -54,6 +54,7 @@ typedef struct { monotonic_t cursor_trail; float cursor_trail_decay_fast; float cursor_trail_decay_slow; + color_type cursor_trail_color; float cursor_trail_start_threshold; unsigned int url_style; unsigned int scrollback_pager_history_size;