diff --git a/docs/changelog.rst b/docs/changelog.rst index 9774fa512..08bfc2cff 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -125,6 +125,9 @@ To update |kitty|, :doc:`follow the instructions `. - A new option :opt:`tab_bar_margin_color` to control the color of the tab bar margins +- A new option :opt:`visual_bell_color` to customize the color of the visual bell + (:pull:`4181`) + - Add support for OSC 777 based desktop notifications - Wayland: Fix pasting from applications that use a MIME type of "text/plain" diff --git a/kittens/themes/collection.py b/kittens/themes/collection.py index 508089773..63cbd8f63 100644 --- a/kittens/themes/collection.py +++ b/kittens/themes/collection.py @@ -317,6 +317,7 @@ def patch_conf(raw: str, theme_name: str) -> str: 'tab_bar_background', 'tab_bar_margin_color', 'url_color', + 'visual_bell_color', 'wayland_titlebar_color', # ALL_COLORS_END ) # }}} diff --git a/kitty/colors.c b/kitty/colors.c index d815dc7ff..f3d165e56 100644 --- a/kitty/colors.c +++ b/kitty/colors.c @@ -170,7 +170,7 @@ patch_color_profiles(PyObject *module UNUSED, PyObject *args) { } S(foreground, default_fg); S(background, default_bg); S(cursor, cursor_color); S(selection_foreground, highlight_fg); S(selection_background, highlight_bg); - S(cursor_text_color, cursor_text_color); + S(cursor_text_color, cursor_text_color); S(visual_bell_color, visual_bell_color); #undef SI #undef S Py_RETURN_NONE; @@ -195,11 +195,11 @@ colorprofile_to_color(ColorProfile *self, DynamicColor entry, DynamicColor defva } color_type -colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, DynamicColor defval, DynamicColor fallback, DynamicColor falback_defval) { +colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, DynamicColor defval, DynamicColor fallback, DynamicColor fallback_defval) { switch(entry.type) { case COLOR_NOT_SET: case COLOR_IS_SPECIAL: - if (defval.type == COLOR_IS_SPECIAL) return colorprofile_to_color(self, fallback, falback_defval).rgb; + if (defval.type == COLOR_IS_SPECIAL) return colorprofile_to_color(self, fallback, fallback_defval).rgb; return defval.rgb; case COLOR_IS_RGB: return entry.rgb; @@ -234,7 +234,7 @@ as_dict(ColorProfile *self, PyObject *args UNUSED) { }} D(default_fg, foreground); D(default_bg, background); D(cursor_color, cursor); D(cursor_text_color, cursor_text); D(highlight_fg, selection_foreground); - D(highlight_bg, selection_background); + D(highlight_bg, selection_background); D(visual_bell_color, visual_bell_color); #undef D return ans; @@ -295,13 +295,13 @@ set_color(ColorProfile *self, PyObject *args) { static PyObject* set_configured_colors(ColorProfile *self, PyObject *args) { #define set_configured_colors_doc "Set the configured colors" - unsigned int default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg; - if (!PyArg_ParseTuple(args, "II|IIII", &default_fg, &default_bg, - &cursor_color, &cursor_text_color, &highlight_fg, &highlight_bg)) return NULL; + unsigned int default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color; + if (!PyArg_ParseTuple(args, "II|IIIII", &default_fg, &default_bg, + &cursor_color, &cursor_text_color, &highlight_fg, &highlight_bg, &visual_bell_color)) return NULL; #define S(which) \ self->configured.which.rgb = which & 0xffffff; \ self->configured.which.type = (which & 0xff000000) ? COLOR_IS_RGB : COLOR_IS_SPECIAL; - S(default_fg); S(default_bg); S(cursor_color); S(cursor_text_color); S(highlight_fg); S(highlight_bg); + S(default_fg); S(default_bg); S(cursor_color); S(cursor_text_color); S(highlight_fg); S(highlight_bg); S(visual_bell_color); #undef S self->dirty = true; Py_RETURN_NONE; @@ -412,6 +412,7 @@ CGETSET(cursor_color) CGETSET(cursor_text_color) CGETSET(highlight_fg) CGETSET(highlight_bg) +CGETSET(visual_bell_color) #undef CGETSET static PyGetSetDef cp_getsetters[] = { @@ -421,6 +422,7 @@ static PyGetSetDef cp_getsetters[] = { GETSET(cursor_text_color) GETSET(highlight_fg) GETSET(highlight_bg) + GETSET(visual_bell_color) {NULL} /* Sentinel */ }; diff --git a/kitty/data-types.h b/kitty/data-types.h index b2c490370..06f05b714 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -258,7 +258,7 @@ typedef union DynamicColor { } DynamicColor; typedef struct { - DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg; + DynamicColor default_fg, default_bg, cursor_color, cursor_text_color, highlight_fg, highlight_bg, visual_bell_color; } DynamicColors; diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index c62bc74a6..5b55d4360 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -701,7 +701,7 @@ class ColorProfile: pass def set_configured_colors( - self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, highlight_fg: int = 0, highlight_bg: int = 0 + self, fg: int, bg: int, cursor: int = 0, cursor_text: int = 0, highlight_fg: int = 0, highlight_bg: int = 0, visual_bell_color: int = 0 ) -> None: pass diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 47d518d69..d907e3ccd 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -670,6 +670,14 @@ number of seconds. Set to zero to disable. ''' ) +opt('visual_bell_color', 'none', + option_type='to_color_or_none', + long_text=''' +The color used by visual bell. Set to :code:`none` will fall back to selection +background color. If you feel that the visual bell is too bright, you can +set it to a darker color. +''') + opt('window_alert_on_bell', 'yes', option_type='to_bool', ctype='bool', long_text=''' diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 3356bfef9..45e8d08d0 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1248,6 +1248,9 @@ class Parser: def url_style(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['url_style'] = url_style(val) + def visual_bell_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['visual_bell_color'] = to_color_or_none(val) + def visual_bell_duration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['visual_bell_duration'] = positive_float(val) diff --git a/kitty/options/types.py b/kitty/options/types.py index 154a31b1d..3c8914140 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -431,6 +431,7 @@ option_names = ( # {{{ 'url_excluded_characters', 'url_prefixes', 'url_style', + 'visual_bell_color', 'visual_bell_duration', 'watcher', 'wayland_titlebar_color', @@ -570,6 +571,7 @@ class Options: url_excluded_characters: str = '' url_prefixes: typing.Tuple[str, ...] = ('http', 'https', 'file', 'ftp', 'gemini', 'irc', 'gopher', 'mailto', 'news', 'git') url_style: int = 3 + visual_bell_color: typing.Optional[kitty.fast_data_types.Color] = None visual_bell_duration: float = 0 wayland_titlebar_color: int = 0 wheel_scroll_multiplier: float = 5.0 diff --git a/kitty/rc/set_colors.py b/kitty/rc/set_colors.py index 3ae1c1dda..336de1287 100644 --- a/kitty/rc/set_colors.py +++ b/kitty/rc/set_colors.py @@ -18,6 +18,7 @@ if TYPE_CHECKING: nullable_colors = ( + # generated by gen-config.py DO NOT edit # NULLABLE_COLORS_START 'active_border_color', 'cursor', @@ -26,6 +27,7 @@ nullable_colors = ( 'selection_foreground', 'tab_bar_background', 'tab_bar_margin_color', + 'visual_bell_color', # NULLABLE_COLORS_END ) diff --git a/kitty/shaders.c b/kitty/shaders.c index ca74b8c65..e6547212a 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -635,8 +635,11 @@ draw_visual_bell_flash(GLfloat intensity, GLfloat xstart, GLfloat ystart, GLfloa glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); bind_program(TINT_PROGRAM); GLfloat attenuation = 0.4f; - const color_type flash = colorprofile_to_color_with_fallback( - screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg, screen->color_profile->overridden.default_fg, screen->color_profile->configured.default_fg); +#define IS_SPECIAL_COLOR(name) (screen->color_profile->overridden.name.type == COLOR_IS_SPECIAL || (screen->color_profile->overridden.name.type == COLOR_NOT_SET && screen->color_profile->configured.name.type == COLOR_IS_SPECIAL)) +#define COLOR(name, fallback) colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.name, screen->color_profile->configured.name, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback) + const color_type flash = !IS_SPECIAL_COLOR(highlight_bg) ? COLOR(visual_bell_color, highlight_bg) : COLOR(visual_bell_color, default_fg); +#undef IS_SPECIAL_COLOR +#undef COLOR #define C(shift) ((((GLfloat)((flash >> shift) & 0xFF)) / 255.0f) ) const GLfloat r = C(16), g = C(8), b = C(0); const GLfloat max_channel = r > g ? (r > b ? r : b) : (g > b ? g : b); diff --git a/kitty/window.py b/kitty/window.py index 3bbd5d365..1451caa27 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -268,7 +268,8 @@ def setup_colors(screen: Screen, opts: Options) -> None: screen.color_profile.set_configured_colors( s(opts.foreground), s(opts.background), s(opts.cursor), s(opts.cursor_text_color), - s(opts.selection_foreground), s(opts.selection_background) + s(opts.selection_foreground), s(opts.selection_background), + s(opts.visual_bell_color) )