diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index fda5470a1..31b652839 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 19ce6c858..a42bc75b3 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 338029491..8142710ed 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -350,6 +350,28 @@ inactivity. Set to zero to never stop blinking. ''' ) +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 46813e8c5..a871056ab 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -10,7 +10,7 @@ from kitty.options.utils import ( 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 @@ class Parser: 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 @@ class Parser: 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 949905b9c..7eb1392e5 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 1240ba678..9aa5c91ba 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 69a194097..0bb9f0c99 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -334,6 +334,7 @@ option_names = ( # {{{ '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 @@ option_names = ( # {{{ '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 eb8c2cd92..5d9aec66a 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 68f482fb7..19a5d4b72 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;