From 75eb370d3153848b43dfe15a80b0016decea4a6c Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 14 Mar 2025 16:53:40 +0900 Subject: [PATCH] osd: draw window switcher with scene-nodes ...rather than drawing it onto a single texture. --- include/labwc.h | 6 ++ src/osd.c | 229 +++++++++++++++++++++--------------------------- src/output.c | 1 + 3 files changed, 109 insertions(+), 127 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 8d9fe8cc..16df6adf 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -412,6 +412,12 @@ struct output { struct wlr_scene_tree *osd_tree; struct wlr_scene_tree *session_lock_tree; struct wlr_scene_buffer *workspace_osd; + + struct osd_scene { + struct wl_array items; /* struct osd_scene_item */ + struct wlr_scene_tree *tree; + } osd_scene; + /* In output-relative scene coordinates */ struct wlr_box usable_area; diff --git a/src/osd.c b/src/osd.c index a89490b8..8d5e8950 100644 --- a/src/osd.c +++ b/src/osd.c @@ -1,16 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only -#include "config.h" #include #include -#include -#include #include #include -#include "buffer.h" #include "common/array.h" #include "common/buf.h" #include "common/font.h" -#include "common/graphic-helpers.h" +#include "common/macros.h" +#include "common/scaled-font-buffer.h" +#include "common/scaled-rect-buffer.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "labwc.h" @@ -21,15 +19,23 @@ #include "window-rules.h" #include "workspaces.h" +struct osd_scene_item { + struct view *view; + struct wlr_scene_node *highlight_outline; +}; + static void update_osd(struct server *server); static void -destroy_osd_nodes(struct output *output) +destroy_osd_scenes(struct server *server) { - struct wlr_scene_node *child, *next; - struct wl_list *children = &output->osd_tree->children; - wl_list_for_each_safe(child, next, children, link) { - wlr_scene_node_destroy(child); + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + wlr_scene_node_destroy(&output->osd_scene.tree->node); + output->osd_scene.tree = NULL; + + wl_array_release(&output->osd_scene.items); + wl_array_init(&output->osd_scene.items); } } @@ -120,7 +126,8 @@ osd_on_view_destroy(struct view *view) } if (osd_state->cycle_view) { - /* Update the OSD to reflect the view has now gone. */ + /* Recreate the OSD to reflect the view has now gone. */ + destroy_osd_scenes(view->server); update_osd(view->server); } @@ -200,11 +207,8 @@ osd_finish(struct server *server) server->osd_state.preview_anchor = NULL; server->osd_state.cycle_view = NULL; - struct output *output; - wl_list_for_each(output, &server->outputs, link) { - destroy_osd_nodes(output); - wlr_scene_node_set_enabled(&output->osd_tree->node, false); - } + destroy_osd_scenes(server); + if (server->osd_state.preview_outline) { /* Destroy the whole multi_rect so we can easily react to new themes */ wlr_scene_node_destroy(&server->osd_state.preview_outline->tree->node); @@ -257,57 +261,52 @@ preview_cycled_view(struct view *view) } static void -render_osd(struct server *server, cairo_t *cairo, int w, int h, - bool show_workspace, const char *workspace_name, - struct wl_array *views) +create_osd_scene(struct output *output, struct wl_array *views) { - struct view *cycle_view = server->osd_state.cycle_view; + struct server *server = output->server; struct theme *theme = server->theme; + bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; + const char *workspace_name = server->workspaces.current->name; - cairo_surface_t *surf = cairo_get_target(cairo); - - /* Draw background */ - set_cairo_color(cairo, theme->osd_bg_color); - cairo_rectangle(cairo, 0, 0, w, h); - cairo_fill(cairo); - - /* Draw border */ - set_cairo_color(cairo, theme->osd_border_color); - struct wlr_fbox fbox = { - .width = w, - .height = h, - }; - draw_cairo_border(cairo, fbox, theme->osd_border_width); + int w = theme->osd_window_switcher_width; + if (theme->osd_window_switcher_width_is_percent) { + w = output->wlr_output->width + * theme->osd_window_switcher_width / 100; + } + int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height + + 2 * rc.theme->osd_border_width + + 2 * rc.theme->osd_window_switcher_padding; + if (show_workspace) { + /* workspace indicator */ + h += theme->osd_window_switcher_item_height; + } - /* Set up text rendering */ - set_cairo_color(cairo, theme->osd_label_text_color); - PangoLayout *layout = pango_cairo_create_layout(cairo); - pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); - pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + output->osd_scene.tree = wlr_scene_tree_create(output->osd_tree); - PangoFontDescription *desc = font_to_pango_desc(&rc.font_osd); - pango_layout_set_font_description(layout, desc); + float *text_color = theme->osd_label_text_color; + float *bg_color = theme->osd_bg_color; - pango_cairo_update_layout(cairo, layout); + /* Draw background */ + scaled_rect_buffer_create(output->osd_scene.tree, w, h, + theme->osd_border_width, bg_color, theme->osd_border_color); int y = theme->osd_border_width + theme->osd_window_switcher_padding; /* Draw workspace indicator */ if (show_workspace) { + struct font font = rc.font_osd; + font.weight = FONT_WEIGHT_BOLD; + /* Center workspace indicator on the x axis */ - int x = font_width(&rc.font_osd, workspace_name); - x = (w - x) / 2; - cairo_move_to(cairo, x, y + theme->osd_window_switcher_item_active_border_width); - PangoWeight weight = pango_font_description_get_weight(desc); - pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); - pango_layout_set_font_description(layout, desc); - pango_layout_set_text(layout, workspace_name, -1); - pango_cairo_show_layout(cairo, layout); - pango_font_description_set_weight(desc, weight); - pango_layout_set_font_description(layout, desc); + int x = (w - font_width(&font, workspace_name)) / 2; + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(output->osd_scene.tree); + wlr_scene_node_set_position(&font_buffer->scene_buffer->node, + x, y + theme->osd_window_switcher_item_active_border_width); + scaled_font_buffer_update(font_buffer, workspace_name, 0, + &font, text_color, bg_color); y += theme->osd_window_switcher_item_height; } - pango_font_description_free(desc); struct buf buf = BUF_INIT; @@ -319,6 +318,9 @@ render_osd(struct server *server, cairo_t *cairo, int w, int h, /* Draw text for each node */ struct view **view; wl_array_for_each(view, views) { + struct osd_scene_item *item = + wl_array_add(&output->osd_scene.items, sizeof(*item)); + item->view = *view; /* * OSD border * +---------------------------------+ @@ -339,97 +341,66 @@ render_osd(struct server *server, cairo_t *cairo, int w, int h, + theme->osd_window_switcher_padding + theme->osd_window_switcher_item_active_border_width + theme->osd_window_switcher_item_padding_x; + struct wlr_scene_tree *item_root = + wlr_scene_tree_create(output->osd_scene.tree); int nr_fields = wl_list_length(&rc.window_switcher.fields); struct window_switcher_field *field; wl_list_for_each(field, &rc.window_switcher.fields, link) { - buf_clear(&buf); - cairo_move_to(cairo, x, y - + theme->osd_window_switcher_item_padding_y - + theme->osd_window_switcher_item_active_border_width); - - osd_field_get_content(field, &buf, *view); - int field_width = (available_width - (nr_fields + 1) * theme->osd_window_switcher_item_padding_x) * field->width / 100.0; - pango_layout_set_width(layout, field_width * PANGO_SCALE); - pango_layout_set_text(layout, buf.data, -1); - pango_cairo_show_layout(cairo, layout); + + buf_clear(&buf); + osd_field_get_content(field, &buf, *view); + + struct scaled_font_buffer *font_buffer = + scaled_font_buffer_create(item_root); + scaled_font_buffer_update(font_buffer, buf.data, field_width, + &rc.font_osd, text_color, bg_color); + + wlr_scene_node_set_position(&font_buffer->scene_buffer->node, + x, y + theme->osd_window_switcher_item_padding_y + + theme->osd_window_switcher_item_active_border_width); x += field_width + theme->osd_window_switcher_item_padding_x; } - if (*view == cycle_view) { - /* Highlight current window */ - struct wlr_fbox fbox = { - .x = theme->osd_border_width + theme->osd_window_switcher_padding, - .y = y, - .width = w - - 2 * theme->osd_border_width - - 2 * theme->osd_window_switcher_padding, - .height = theme->osd_window_switcher_item_height, - }; - draw_cairo_border(cairo, fbox, - theme->osd_window_switcher_item_active_border_width); - cairo_stroke(cairo); - } + /* Highlight around selected window's item */ + int highlight_w = w - 2 * theme->osd_border_width + - 2 * theme->osd_window_switcher_padding; + int highlight_h = theme->osd_window_switcher_item_height; + int highlight_x = theme->osd_border_width + + theme->osd_window_switcher_padding; + int border_width = theme->osd_window_switcher_item_active_border_width; + float transparent[4] = {0}; + + struct scaled_rect_buffer *highlight_buffer = scaled_rect_buffer_create( + output->osd_scene.tree, highlight_w, highlight_h, + border_width, transparent, text_color); + assert(highlight_buffer); + item->highlight_outline = &highlight_buffer->scene_buffer->node; + wlr_scene_node_set_position(item->highlight_outline, highlight_x, y); + wlr_scene_node_set_enabled(item->highlight_outline, false); y += theme->osd_window_switcher_item_height; } buf_reset(&buf); - g_object_unref(layout); - cairo_surface_flush(surf); + /* Center OSD */ + struct wlr_box usable = output_usable_area_in_layout_coords(output); + wlr_scene_node_set_position(&output->osd_scene.tree->node, + usable.x + usable.width / 2 - w / 2, + usable.y + usable.height / 2 - h / 2); } static void -display_osd(struct output *output, struct wl_array *views) +update_item_highlight(struct output *output) { - struct server *server = output->server; - struct theme *theme = server->theme; - bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; - const char *workspace_name = server->workspaces.current->name; - - float scale = output->wlr_output->scale; - int w = theme->osd_window_switcher_width; - if (theme->osd_window_switcher_width_is_percent) { - w = output->wlr_output->width / output->wlr_output->scale - * theme->osd_window_switcher_width / 100; + struct osd_scene_item *item; + wl_array_for_each(item, &output->osd_scene.items) { + wlr_scene_node_set_enabled(item->highlight_outline, + item->view == output->server->osd_state.cycle_view); } - int h = wl_array_len(views) * rc.theme->osd_window_switcher_item_height - + 2 * rc.theme->osd_border_width - + 2 * rc.theme->osd_window_switcher_padding; - if (show_workspace) { - /* workspace indicator */ - h += theme->osd_window_switcher_item_height; - } - - struct lab_data_buffer *buffer = buffer_create_cairo(w, h, scale); - if (!buffer) { - wlr_log(WLR_ERROR, "Failed to allocate cairo buffer for the window switcher"); - return; - } - - /* Render OSD image */ - cairo_t *cairo = cairo_create(buffer->surface); - render_osd(server, cairo, w, h, show_workspace, workspace_name, views); - cairo_destroy(cairo); - - struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_create( - output->osd_tree, &buffer->base); - wlr_buffer_drop(&buffer->base); - wlr_scene_buffer_set_dest_size(scene_buffer, w, h); - - /* Center OSD */ - struct wlr_box output_box; - wlr_output_layout_get_box(output->server->output_layout, - output->wlr_output, &output_box); - int lx = output->usable_area.x + output->usable_area.width / 2 - - w / 2 + output_box.x; - int ly = output->usable_area.y + output->usable_area.height / 2 - - h / 2 + output_box.y; - wlr_scene_node_set_position(&scene_buffer->node, lx, ly); - wlr_scene_node_set_enabled(&output->osd_tree->node, true); } static void @@ -448,10 +419,14 @@ update_osd(struct server *server) /* Display the actual OSD */ struct output *output; wl_list_for_each(output, &server->outputs, link) { - destroy_osd_nodes(output); - if (output_is_usable(output)) { - display_osd(output, &views); + if (!output_is_usable(output)) { + continue; + } + if (!output->osd_scene.tree) { + create_osd_scene(output, &views); + assert(output->osd_scene.tree); } + update_item_highlight(output); } } diff --git a/src/output.c b/src/output.c index 1221376c..e72d14cf 100644 --- a/src/output.c +++ b/src/output.c @@ -486,6 +486,7 @@ new_output_notify(struct wl_listener *listener, void *data) wl_signal_add(&wlr_output->events.request_state, &output->request_state); wl_list_init(&output->regions); + wl_array_init(&output->osd_scene.items); /* * Create layer-trees (background, bottom, top and overlay) and -- 2.52.0