diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fa7439e..23ddb84f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,12 @@ * `[colors-light]` section to `foot.ini`. Replaces `[colors2]`. * `XTGETTCAP`: added `query-os-name`, returning the OS foot is compiled for (e.g. _'Linux'_) ([#2209][2209]). +* Preliminary (untested) support for background blur via the new + `ext-background-effect-v1` protocol. Enable by setting + `colors-{dark,light}.blur=yes`. Foot needs to have been **built** + against `wayland-protocols >= 1.45`, and the compositor **must** + implement the `ext-background-effect-v1` protocol, **and** the + `blur` effect. [2212]: https://codeberg.org/dnkl/foot/issues/2212 [2209]: https://codeberg.org/dnkl/foot/issues/2209 diff --git a/config.c b/config.c index 14e836c1..a5fc74d9 100644 --- a/config.c +++ b/config.c @@ -1576,6 +1576,9 @@ parse_color_theme(struct context *ctx, struct color_theme *theme) (int *)&theme->dim_blend_towards); } + else if (streq(key, "blur")) + return value_to_bool(ctx, &theme->blur); + else { LOG_CONTEXTUAL_ERR("not valid option"); return false; @@ -3526,6 +3529,7 @@ config_load(struct config *conf, const char *conf_path, .scrollback_indicator = false, .url = false, }, + .blur = false, }, .initial_color_theme = COLOR_THEME_DARK, .cursor = { diff --git a/config.h b/config.h index 9ca47753..a9ec9cb4 100644 --- a/config.h +++ b/config.h @@ -192,6 +192,8 @@ struct color_theme { bool search_box_match:1; uint8_t dim; } use_custom; + + bool blur; }; enum which_color_theme { diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 8bff9629..acc40373 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1097,6 +1097,14 @@ The default theme used is *colors-dark*, unless Default: _default_ +*blur* + Boolean. When enabled, foot will blur the background, when it is + transparent. This feature requires the compositor to implement the + _ext-background-effect-v1_ protocol (and specifically, the _blur_ + effect). + + Default: _no_ + *dim-blend-towards* Which color to blend towards when "auto" dimming a color (see *dim0*..*dim7* above). One of *black* or *white*. Blending towards diff --git a/foot-features.c b/foot-features.c index f701533c..8e332517 100644 --- a/foot-features.c +++ b/foot-features.c @@ -28,6 +28,12 @@ const char version_and_features[] = " -toplevel-tag" #endif +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + " +blur" +#else + " -blur" +#endif + #if !defined(NDEBUG) " +assertions" #else diff --git a/meson.build b/meson.build index aa8342ab..b7377652 100644 --- a/meson.build +++ b/meson.build @@ -188,6 +188,10 @@ if (wayland_protocols.version().version_compare('>=1.43')) wl_proto_xml += [wayland_protocols_datadir / 'staging/xdg-toplevel-tag/xdg-toplevel-tag-v1.xml'] add_project_arguments('-DHAVE_XDG_TOPLEVEL_TAG=1', language: 'c') endif +if (wayland_protocols.version().version_compare('>=1.45')) + wl_proto_xml += [wayland_protocols_datadir / 'staging/ext-background-effect/ext-background-effect-v1.xml'] + add_project_arguments('-DHAVE_EXT_BACKGROUND_EFFECT=1', language: 'c') +endif foreach prot : wl_proto_xml wl_proto_headers += custom_target( diff --git a/osc.c b/osc.c index 9407e7b8..6493cbfc 100644 --- a/osc.c +++ b/osc.c @@ -1458,11 +1458,8 @@ osc_dispatch(struct terminal *term) case 11: term->colors.bg = color; - if (!have_alpha) { - alpha = term->colors.active_theme == COLOR_THEME_DARK - ? term->conf->colors_dark.alpha - : term->conf->colors_light.alpha; - } + if (!have_alpha) + alpha = term_theme_get(term)->alpha; const bool changed = term->colors.alpha != alpha; term->colors.alpha = alpha; @@ -1515,10 +1512,7 @@ osc_dispatch(struct terminal *term) case 104: { /* Reset Color Number 'c' (whole table if no parameter) */ - const struct color_theme *theme = - term->colors.active_theme == COLOR_THEME_DARK - ? &term->conf->colors_dark - : &term->conf->colors_light; + const struct color_theme *theme = term_theme_get(term); if (string[0] == '\0') { LOG_DBG("resetting all colors"); @@ -1558,11 +1552,7 @@ osc_dispatch(struct terminal *term) case 110: /* Reset default text foreground color */ LOG_DBG("resetting foreground color"); - const struct color_theme *theme = - term->colors.active_theme == COLOR_THEME_DARK - ? &term->conf->colors_dark - : &term->conf->colors_light; - + const struct color_theme *theme = term_theme_get(term); term->colors.fg = theme->fg; term_damage_color(term, COLOR_DEFAULT, 0); break; @@ -1570,11 +1560,7 @@ osc_dispatch(struct terminal *term) case 111: { /* Reset default text background color */ LOG_DBG("resetting background color"); - const struct color_theme *theme = - term->colors.active_theme == COLOR_THEME_DARK - ? &term->conf->colors_dark - : &term->conf->colors_light; - + const struct color_theme *theme = term_theme_get(term); bool alpha_changed = term->colors.alpha != theme->alpha; term->colors.bg = theme->bg; @@ -1593,11 +1579,7 @@ osc_dispatch(struct terminal *term) case 112: { LOG_DBG("resetting cursor color"); - const struct color_theme *theme = - term->colors.active_theme == COLOR_THEME_DARK - ? &term->conf->colors_dark - : &term->conf->colors_light; - + const struct color_theme *theme = term_theme_get(term); term->colors.cursor_fg = theme->cursor.text; term->colors.cursor_bg = theme->cursor.cursor; @@ -1613,11 +1595,7 @@ osc_dispatch(struct terminal *term) case 117: { LOG_DBG("resetting selection background color"); - const struct color_theme *theme = - term->colors.active_theme == COLOR_THEME_DARK - ? &term->conf->colors_dark - : &term->conf->colors_light; - + const struct color_theme *theme = term_theme_get(term); term->colors.selection_bg = theme->selection_bg; break; } @@ -1625,11 +1603,7 @@ osc_dispatch(struct terminal *term) case 119: { LOG_DBG("resetting selection foreground color"); - const struct color_theme *theme = - term->colors.active_theme == COLOR_THEME_DARK - ? &term->conf->colors_dark - : &term->conf->colors_light; - + const struct color_theme *theme = term_theme_get(term); term->colors.selection_fg = theme->selection_fg; break; } diff --git a/render.c b/render.c index ac8ece37..c6f5950c 100644 --- a/render.c +++ b/render.c @@ -312,9 +312,7 @@ color_dim(const struct terminal *term, uint32_t color) } } - const struct color_theme *theme = term->colors.active_theme == COLOR_THEME_DARK - ? &conf->colors_dark - : &conf->colors_light; + const struct color_theme *theme = term_theme_get(term); return color_blend_towards( color, diff --git a/terminal.c b/terminal.c index b670d606..97b2cfad 100644 --- a/terminal.c +++ b/terminal.c @@ -4809,3 +4809,11 @@ term_theme_toggle(struct terminal *term) term_damage_margins(term); render_refresh(term); } + +const struct color_theme * +term_theme_get(const struct terminal *term) +{ + return term->colors.active_theme == COLOR_THEME_DARK + ? &term->conf->colors_dark + : &term->conf->colors_light; +} diff --git a/terminal.h b/terminal.h index fe39341d..5a2a57aa 100644 --- a/terminal.h +++ b/terminal.h @@ -997,6 +997,7 @@ void term_send_size_notification(struct terminal *term); void term_theme_switch_to_dark(struct terminal *term); void term_theme_switch_to_light(struct terminal *term); void term_theme_toggle(struct terminal *term); +const struct color_theme *term_theme_get(const struct terminal *term); static inline void term_reset_grapheme_state(struct terminal *term) { diff --git a/tests/test-config.c b/tests/test-config.c index f83a9beb..05c70990 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -774,6 +774,8 @@ test_section_colors_dark(void) &conf.colors_dark.table[i]); } + test_boolean(&ctx, &parse_section_colors, "blur", &conf.colors_dark.blur); + test_invalid_key(&ctx, &parse_section_colors_dark, "256"); /* TODO: alpha (float in range 0-1, converted to uint16_t) */ @@ -853,6 +855,8 @@ test_section_colors_light(void) &conf.colors_light.table[i]); } + test_boolean(&ctx, &parse_section_colors, "blur", &conf.colors_light.blur); + test_invalid_key(&ctx, &parse_section_colors_light, "256"); /* TODO: alpha (float in range 0-1, converted to uint16_t) */ diff --git a/wayland.c b/wayland.c index 6785c52d..5d65700e 100644 --- a/wayland.c +++ b/wayland.c @@ -1196,6 +1196,27 @@ static const struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration .configure = &xdg_toplevel_decoration_configure, }; +#if defined(HAVE_EXT_BACKGROUND_EFFECT) +static void +ext_background_capabilities( + void *data, + struct ext_background_effect_manager_v1 *ext_background_effect_manager_v1, + uint32_t flags) +{ + struct wayland *wayl = data; + + wayl->have_background_blur = + !!(flags & EXT_BACKGROUND_EFFECT_MANAGER_V1_CAPABILITY_BLUR); + + LOG_DBG("compositor supports background blur: %s", + wayl->have_background_blur ? "yes" : "no"); +} + +static const struct ext_background_effect_manager_v1_listener background_manager_listener = { + .capabilities = &ext_background_capabilities, +}; +#endif /* HAVE_EXT_BACKGROUND_EFFECT */ + static bool fdm_repeat(struct fdm *fdm, int fd, int events, void *data) { @@ -1558,6 +1579,20 @@ handle_global(void *data, struct wl_registry *registry, wayl->registry, name, &xdg_toplevel_tag_manager_v1_interface, required); } #endif +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + else if (streq(interface, ext_background_effect_manager_v1_interface.name)) { + const uint32_t required = 1; + if (!verify_iface_version(interface, version, required)) + return; + + wayl->background_effect_manager = wl_registry_bind( + wayl->registry, name, + &ext_background_effect_manager_v1_interface, required); + + ext_background_effect_manager_v1_add_listener( + wayl->background_effect_manager, &background_manager_listener, wayl); + } +#endif #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED else if (streq(interface, zwp_text_input_manager_v3_interface.name)) { @@ -1572,6 +1607,7 @@ handle_global(void *data, struct wl_registry *registry, seat_add_text_input(&it->item); } #endif + } static void @@ -1885,6 +1921,10 @@ wayl_destroy(struct wayland *wayl) if (wayl->toplevel_tag_manager != NULL) xdg_toplevel_tag_manager_v1_destroy(wayl->toplevel_tag_manager); #endif +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + if (wayl->background_effect_manager != NULL) + ext_background_effect_manager_v1_destroy(wayl->background_effect_manager); +#endif if (wayl->color_management.img_description != NULL) wp_image_description_v1_destroy(wayl->color_management.img_description); @@ -1989,8 +2029,6 @@ wayl_win_init(struct terminal *term, const char *token) goto out; } - wayl_win_alpha_changed(win); - wl_surface_add_listener(win->surface.surf, &surface_listener, win); if (wayl->fractional_scale_manager != NULL && wayl->viewporter != NULL) { @@ -2003,6 +2041,16 @@ wayl_win_init(struct terminal *term, const char *token) win->fractional_scale, &fractional_scale_listener, win); } +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + if (wayl->background_effect_manager != NULL) { + win->surface.background_effect = + ext_background_effect_manager_v1_get_background_effect( + wayl->background_effect_manager, win->surface.surf); + } +#endif + + wayl_win_alpha_changed(win); + win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface.surf); xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win); @@ -2207,7 +2255,12 @@ wayl_win_destroy(struct wl_window *win) free(it->item); tll_remove(win->xdg_tokens, it); -} + } + +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + if (win->surface.background_effect != NULL) + ext_background_effect_surface_v1_destroy(win->surface.background_effect); +#endif if (win->surface.color_management != NULL) wp_color_management_surface_v1_destroy(win->surface.color_management); @@ -2447,16 +2500,16 @@ void wayl_win_alpha_changed(struct wl_window *win) { struct terminal *term = win->term; + struct wayland *wayl = term->wl; /* * When fullscreened, transparency is disabled (see render.c). * Update the opaque region to match. */ - bool is_opaque = term->colors.alpha == 0xffff || win->is_fullscreen; + const bool is_opaque = term->colors.alpha == 0xffff || win->is_fullscreen; if (is_opaque) { - struct wl_region *region = wl_compositor_create_region( - term->wl->compositor); + struct wl_region *region = wl_compositor_create_region(wayl->compositor); if (region != NULL) { wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); @@ -2465,6 +2518,38 @@ wayl_win_alpha_changed(struct wl_window *win) } } else wl_surface_set_opaque_region(win->surface.surf, NULL); + +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + if (term_theme_get(term)->blur) { + if (wayl->have_background_blur) { + xassert(win->surface.background_effect != NULL); + + if (is_opaque) { + /* No transparency, disable blur */ + LOG_DBG("disabling background blur"); + ext_background_effect_surface_v1_set_blur_region( + win->surface.background_effect, NULL); + } else { + /* We have transparency, enable blur if user has enabled it */ + struct wl_region *region = wl_compositor_create_region(wayl->compositor); + if (region != NULL) { + LOG_DBG("enabling background blur"); + + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + ext_background_effect_surface_v1_set_blur_region( + win->surface.background_effect, region); + wl_region_destroy(region); + } + } + } else { + static bool have_warned = false; + if (!have_warned) { + LOG_WARN("background blur requested, but compositor does not support it"); + have_warned = true; + } + } + } +#endif /* HAVE_EXT_BACKGROUND_EFFECT */ } static void diff --git a/wayland.h b/wayland.h index 140c2058..72488609 100644 --- a/wayland.h +++ b/wayland.h @@ -26,6 +26,9 @@ #if defined(HAVE_XDG_TOPLEVEL_TAG) #include #endif +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + #include +#endif #include #include @@ -62,6 +65,10 @@ struct wayl_surface { struct wl_surface *surf; struct wp_viewport *viewport; struct wp_color_management_surface_v1 *color_management; + +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + struct ext_background_effect_surface_v1 *background_effect; +#endif }; struct wayl_sub_surface { @@ -488,6 +495,10 @@ struct wayland { #if defined(HAVE_XDG_TOPLEVEL_TAG) struct xdg_toplevel_tag_manager_v1 *toplevel_tag_manager; #endif +#if defined(HAVE_EXT_BACKGROUND_EFFECT) + struct ext_background_effect_manager_v1 *background_effect_manager; + bool have_background_blur; +#endif #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED struct zwp_text_input_manager_v3 *text_input_manager;