diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 9ec8b8e19..d791c84db 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -354,6 +354,15 @@ opt('cursor_stop_blinking_after', '15.0', long_text=''' Stop blinking cursor after the specified number of seconds of keyboard inactivity. Set to zero to never stop blinking. +''' + ) + +opt('cursor_unfocused_shape', 'hollow', + option_type='to_cursor_unfocused_shape', ctype='int', + long_text=''' +Defines the cursor shape when the terminal window is not focused. The unfocused +cursor shape can be one of :code:`block`, :code:`beam`, :code:`underline`, +:code:`hollow`. ''' ) egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 32c75dab2..b4b21fb95 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -17,9 +17,9 @@ from kitty.options.utils import ( remote_control_password, resize_debounce_time, scrollback_lines, scrollback_pager_history_size, shell_integration, store_multiple, symbol_map, tab_activity_symbol, tab_bar_edge, tab_bar_margin_height, tab_bar_min_tabs, tab_fade, tab_font_style, tab_separator, - tab_title_template, titlebar_color, to_cursor_shape, to_font_size, to_layout_names, to_modifiers, - url_prefixes, url_style, visual_window_select_characters, window_border_width, window_logo_scale, - window_size + tab_title_template, titlebar_color, to_cursor_shape, to_cursor_unfocused_shape, to_font_size, + to_layout_names, to_modifiers, url_prefixes, url_style, visual_window_select_characters, + window_border_width, window_logo_scale, window_size ) @@ -929,6 +929,9 @@ class Parser: def cursor_underline_thickness(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['cursor_underline_thickness'] = positive_float(val) + def cursor_unfocused_shape(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['cursor_unfocused_shape'] = to_cursor_unfocused_shape(val) + def default_pointer_shape(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_default_pointer_shape: diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 1b51973fd..d424d327a 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -135,6 +135,19 @@ convert_from_opts_cursor_stop_blinking_after(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_cursor_unfocused_shape(PyObject *val, Options *opts) { + opts->cursor_unfocused_shape = PyLong_AsLong(val); +} + +static void +convert_from_opts_cursor_unfocused_shape(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "cursor_unfocused_shape"); + if (ret == NULL) return; + convert_from_python_cursor_unfocused_shape(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_scrollback_indicator_opacity(PyObject *val, Options *opts) { opts->scrollback_indicator_opacity = PyFloat_AsFloat(val); @@ -1171,6 +1184,8 @@ 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_cursor_unfocused_shape(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/types.py b/kitty/options/types.py index 6f4bac55e..39c90a316 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -333,6 +333,7 @@ option_names = ( # {{{ 'cursor_stop_blinking_after', 'cursor_text_color', 'cursor_underline_thickness', + 'cursor_unfocused_shape', 'default_pointer_shape', 'detect_urls', 'dim_opacity', @@ -506,6 +507,7 @@ class Options: cursor_stop_blinking_after: float = 15.0 cursor_text_color: typing.Optional[kitty.fast_data_types.Color] = Color(17, 17, 17) cursor_underline_thickness: float = 2.0 + cursor_unfocused_shape: int = 0 default_pointer_shape: choices_for_default_pointer_shape = 'beam' detect_urls: bool = True dim_opacity: float = 0.4 diff --git a/kitty/options/utils.py b/kitty/options/utils.py index 4ed496605..394573df0 100644 --- a/kitty/options/utils.py +++ b/kitty/options/utils.py @@ -44,7 +44,7 @@ from kitty.conf.utils import ( unit_float, ) from kitty.constants import is_macos -from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, Color, Shlex, SingleKey +from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, NO_CURSOR_SHAPE, Color, Shlex, SingleKey from kitty.fonts import FontFeature, FontModification, ModificationType, ModificationUnit, ModificationValue from kitty.key_names import character_key_name_aliases, functional_key_name_aliases, get_key_name_lookup from kitty.rgb import color_as_int @@ -525,7 +525,6 @@ cshapes = { 'underline': CURSOR_UNDERLINE } - def to_cursor_shape(x: str) -> int: try: return cshapes[x.lower()] @@ -537,6 +536,24 @@ def to_cursor_shape(x: str) -> int: ) +cshapes_unfocused = { + 'block': CURSOR_BLOCK, + 'beam': CURSOR_BEAM, + 'underline': CURSOR_UNDERLINE, + 'hollow': NO_CURSOR_SHAPE +} + +def to_cursor_unfocused_shape(x: str) -> int: + try: + return cshapes_unfocused[x.lower()] + except KeyError: + raise ValueError( + 'Invalid inactive cursor shape: {} allowed values are {}'.format( + x, ', '.join(cshapes_unfocused) + ) + ) + + def scrollback_lines(x: str) -> int: ans = int(x) if ans < 0: diff --git a/kitty/shaders.c b/kitty/shaders.c index f1ff64840..8805e61ee 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -333,7 +333,18 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c case CURSOR_UNDERLINE: rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break; } - } else rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; + } else { + switch(OPT(cursor_unfocused_shape)) { + default: + rd->cursor_fg_sprite_idx = UNFOCUSED_IDX; break; + case CURSOR_BEAM: + rd->cursor_fg_sprite_idx = BEAM_IDX; break; + case CURSOR_UNDERLINE: + rd->cursor_fg_sprite_idx = UNDERLINE_IDX; break; + case CURSOR_BLOCK: + rd->cursor_fg_sprite_idx = BLOCK_IDX; break; + } + } color_type cell_fg = rd->default_fg, cell_bg = rd->default_bg; index_type cell_color_x = cursor->x; bool reversed = false; diff --git a/kitty/state.h b/kitty/state.h index 64641c14f..10db4c2cd 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -39,6 +39,7 @@ typedef struct { int wheel_scroll_min_lines; bool enable_audio_bell; CursorShape cursor_shape; + CursorShape cursor_unfocused_shape; float cursor_beam_thickness; float cursor_underline_thickness; unsigned int url_style;