From dc474521ab5bed5518c46b3e01e61242658646ea Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 12 Jan 2025 16:43:49 +0900 Subject: [PATCH] ssd: dynamically look up window icons in titlebar for output scales by introducing scaled_icon_buffer. --- include/common/scaled-icon-buffer.h | 40 +++++++++ include/common/scaled-img-buffer.h | 4 - include/ssd-internal.h | 12 ++- src/common/meson.build | 1 + src/common/scaled-icon-buffer.c | 123 ++++++++++++++++++++++++++++ src/common/scaled-img-buffer.c | 12 --- src/ssd/ssd-part.c | 67 ++++++++++----- src/ssd/ssd-titlebar.c | 56 ++----------- src/theme.c | 12 --- 9 files changed, 225 insertions(+), 102 deletions(-) create mode 100644 include/common/scaled-icon-buffer.h create mode 100644 src/common/scaled-icon-buffer.c diff --git a/include/common/scaled-icon-buffer.h b/include/common/scaled-icon-buffer.h new file mode 100644 index 00000000..54f9f176 --- /dev/null +++ b/include/common/scaled-icon-buffer.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_SCALED_ICON_BUFFER_H +#define LABWC_SCALED_ICON_BUFFER_H + +#include + +struct wlr_scene_tree; +struct wlr_scene_node; +struct wlr_scene_buffer; + +struct scaled_icon_buffer { + struct scaled_scene_buffer *scaled_buffer; + struct wlr_scene_buffer *scene_buffer; + struct server *server; + char *app_id; + char *icon_name; + int width; + int height; +}; + +/* + * Create an auto scaling icon buffer, providing a wlr_scene_buffer node for + * display. It gets destroyed automatically when the backing scaled_scene_buffer + * is being destroyed which in turn happens automatically when the backing + * wlr_scene_buffer (or one of its parents) is being destroyed. + */ +struct scaled_icon_buffer *scaled_icon_buffer_create( + struct wlr_scene_tree *parent, struct server *server, + int width, int height); + +void scaled_icon_buffer_set_app_id(struct scaled_icon_buffer *self, + const char *app_id); + +void scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, + const char *icon_name); + +/* Obtain scaled_icon_buffer from wlr_scene_node */ +struct scaled_icon_buffer *scaled_icon_buffer_from_node(struct wlr_scene_node *node); + +#endif /* LABWC_SCALED_ICON_BUFFER_H */ diff --git a/include/common/scaled-img-buffer.h b/include/common/scaled-img-buffer.h index 27569a24..938baeb2 100644 --- a/include/common/scaled-img-buffer.h +++ b/include/common/scaled-img-buffer.h @@ -66,10 +66,6 @@ struct scaled_img_buffer { struct scaled_img_buffer *scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, int width, int height); -/* Update image, width and height of the scaled_img_buffer */ -void scaled_img_buffer_update(struct scaled_img_buffer *self, - struct lab_img *img, int width, int height); - /* Obtain scaled_img_buffer from wlr_scene_node */ struct scaled_img_buffer *scaled_img_buffer_from_node(struct wlr_scene_node *node); diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 6d1d7ae7..d46e7280 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -24,10 +24,16 @@ struct ssd_button { */ uint8_t state_set; /* - * Button nodes for each combination of hover/toggled/rounded states. - * nodes[state_set] should be displayed. + * Image buffers for each combination of hover/toggled/rounded states. + * img_buffers[state_set] is displayed. Some of these can be NULL + * (e.g. img_buffers[LAB_BS_ROUNDED] is set only for corner buttons). + * + * When "type" is LAB_SSD_BUTTON_WINDOW_ICON, these are all NULL and + * window_icon is used instead. */ - struct wlr_scene_node *nodes[LAB_BS_ALL + 1]; + struct scaled_img_buffer *img_buffers[LAB_BS_ALL + 1]; + + struct scaled_icon_buffer *window_icon; struct wl_listener destroy; }; diff --git a/src/common/meson.build b/src/common/meson.build index 457cffa1..15910f32 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -14,6 +14,7 @@ labwc_sources += files( 'parse-bool.c', 'parse-double.c', 'scaled-font-buffer.c', + 'scaled-icon-buffer.c', 'scaled-img-buffer.c', 'scaled-rect-buffer.c', 'scaled-scene-buffer.c', diff --git a/src/common/scaled-icon-buffer.c b/src/common/scaled-icon-buffer.c new file mode 100644 index 00000000..1ecc12b2 --- /dev/null +++ b/src/common/scaled-icon-buffer.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "common/macros.h" +#include "common/mem.h" +#include "common/scaled-icon-buffer.h" +#include "common/scaled-scene-buffer.h" +#include "common/string-helpers.h" +#include "config.h" +#include "config/rcxml.h" +#include "desktop-entry.h" +#include "img/img.h" +#include "node.h" + +static struct lab_data_buffer * +_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) +{ +#if HAVE_LIBSFDO + struct scaled_icon_buffer *self = scaled_buffer->data; + int icon_size = MIN(self->width, self->height); + struct lab_img *img = NULL; + + if (self->icon_name) { + img = desktop_entry_load_icon(self->server, + self->icon_name, icon_size, scale); + } else if (self->app_id) { + img = desktop_entry_load_icon_from_app_id(self->server, + self->app_id, icon_size, scale); + } + + if (!img) { + return NULL; + } + + struct lab_data_buffer *buffer = + lab_img_render(img, self->width, self->height, scale); + lab_img_destroy(img); + + return buffer; +#else + return NULL; +#endif +} + +static void +_destroy(struct scaled_scene_buffer *scaled_buffer) +{ + struct scaled_icon_buffer *self = scaled_buffer->data; + free(self->app_id); + free(self->icon_name); + free(self); +} + +static bool +_equal(struct scaled_scene_buffer *scaled_buffer_a, + struct scaled_scene_buffer *scaled_buffer_b) +{ + struct scaled_icon_buffer *a = scaled_buffer_a->data; + struct scaled_icon_buffer *b = scaled_buffer_b->data; + + return str_equal(a->app_id, b->app_id) + && str_equal(a->icon_name, b->icon_name) + && a->width == b->width + && a->height == b->height; +} + +static struct scaled_scene_buffer_impl impl = { + .create_buffer = _create_buffer, + .destroy = _destroy, + .equal = _equal, +}; + +struct scaled_icon_buffer * +scaled_icon_buffer_create(struct wlr_scene_tree *parent, struct server *server, + int width, int height) +{ + struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create( + parent, &impl, /* drop_buffer */ true); + struct scaled_icon_buffer *self = znew(*self); + self->scaled_buffer = scaled_buffer; + self->scene_buffer = scaled_buffer->scene_buffer; + self->server = server; + self->width = width; + self->height = height; + + scaled_buffer->data = self; + + return self; +} + +void +scaled_icon_buffer_set_app_id(struct scaled_icon_buffer *self, + const char *app_id) +{ + assert(app_id); + if (str_equal(self->app_id, app_id)) { + return; + } + xstrdup_replace(self->app_id, app_id); + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); +} + +void +scaled_icon_buffer_set_icon_name(struct scaled_icon_buffer *self, + const char *icon_name) +{ + assert(icon_name); + if (str_equal(self->icon_name, icon_name)) { + return; + } + xstrdup_replace(self->icon_name, icon_name); + scaled_scene_buffer_request_update(self->scaled_buffer, self->width, self->height); +} + +struct scaled_icon_buffer * +scaled_icon_buffer_from_node(struct wlr_scene_node *node) +{ + struct scaled_scene_buffer *scaled_buffer = + node_scaled_scene_buffer_from_node(node); + assert(scaled_buffer->impl == &impl); + return scaled_buffer->data; +} diff --git a/src/common/scaled-img-buffer.c b/src/common/scaled-img-buffer.c index 99ab7b24..94465d7d 100644 --- a/src/common/scaled-img-buffer.c +++ b/src/common/scaled-img-buffer.c @@ -66,18 +66,6 @@ scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img, return self; } -void -scaled_img_buffer_update(struct scaled_img_buffer *self, struct lab_img *img, - int width, int height) -{ - assert(img); - lab_img_destroy(self->img); - self->img = lab_img_copy(img); - self->width = width; - self->height = height; - scaled_scene_buffer_request_update(self->scaled_buffer, width, height); -} - struct scaled_img_buffer * scaled_img_buffer_from_node(struct wlr_scene_node *node) { diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 37176cd7..28bd130c 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -5,6 +5,7 @@ #include "common/box.h" #include "common/list.h" #include "common/mem.h" +#include "common/scaled-icon-buffer.h" #include "common/scaled-img-buffer.h" #include "labwc.h" #include "node.h" @@ -92,6 +93,10 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, button_root->node = &parent->node; wlr_scene_node_set_position(button_root->node, x, y); + struct ssd_button *button = ssd_button_descriptor_create(button_root->node); + button->type = type; + button->view = view; + /* Hitbox */ float invisible[4] = { 0, 0, 0, 0 }; add_scene_rect(part_list, type, parent, @@ -99,29 +104,47 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, invisible); /* Icons */ - struct wlr_scene_node *nodes[LAB_BS_ALL + 1] = {0}; - for (uint8_t state_set = LAB_BS_DEFAULT; - state_set <= LAB_BS_ALL; state_set++) { - if (!imgs[state_set]) { - continue; - } - struct ssd_part *icon_part = add_scene_part(part_list, type); - struct scaled_img_buffer *img_buffer = scaled_img_buffer_create( - parent, imgs[state_set], rc.theme->window_button_width, - rc.theme->window_button_height); - assert(img_buffer); - icon_part->node = &img_buffer->scene_buffer->node; - wlr_scene_node_set_enabled(icon_part->node, false); - nodes[state_set] = icon_part->node; - } - /* Initially show non-hover, non-toggled, unrounded variant */ - wlr_scene_node_set_enabled(nodes[0], true); + int button_width = rc.theme->window_button_width; + int button_height = rc.theme->window_button_height; + /* + * Ensure a small amount of horizontal padding within the button + * area (2px on each side with the default 26px button width). + * A new theme setting could be added to configure this. Using + * an existing setting (padding.width or window.button.spacing) + * was considered, but these settings have distinct purposes + * already and are zero by default. + */ + int icon_padding = button_width / 10; + + if (type == LAB_SSD_BUTTON_WINDOW_ICON) { + struct ssd_part *icon_part = add_scene_part(part_list, type); + struct scaled_icon_buffer *icon_buffer = + scaled_icon_buffer_create(parent, view->server, + button_width - 2 * icon_padding, button_height); + assert(icon_buffer); + icon_part->node = &icon_buffer->scene_buffer->node; + wlr_scene_node_set_position(icon_part->node, icon_padding, 0); + button->window_icon = icon_buffer; + } else { + for (uint8_t state_set = LAB_BS_DEFAULT; + state_set <= LAB_BS_ALL; state_set++) { + if (!imgs[state_set]) { + continue; + } + struct ssd_part *icon_part = add_scene_part(part_list, type); + struct scaled_img_buffer *img_buffer = scaled_img_buffer_create( + parent, imgs[state_set], rc.theme->window_button_width, + rc.theme->window_button_height); + assert(img_buffer); + icon_part->node = &img_buffer->scene_buffer->node; + wlr_scene_node_set_enabled(icon_part->node, false); + button->img_buffers[state_set] = img_buffer; + } + /* Initially show non-hover, non-toggled, unrounded variant */ + wlr_scene_node_set_enabled( + &button->img_buffers[LAB_BS_DEFAULT]->scene_buffer->node, true); + } - struct ssd_button *button = ssd_button_descriptor_create(button_root->node); - button->type = type; - button->view = view; - button->state_set = 0; - memcpy(button->nodes, nodes, sizeof(nodes)); return button_root; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 6ffb1ce1..2879b203 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -7,6 +7,7 @@ #include "config.h" #include "common/mem.h" #include "common/scaled-font-buffer.h" +#include "common/scaled-icon-buffer.h" #include "common/scaled-img-buffer.h" #include "common/scene-helpers.h" #include "common/string-helpers.h" @@ -127,11 +128,12 @@ update_button_state(struct ssd_button *button, enum lab_button_state state, /* Switch the displayed icon buffer to the new one */ for (uint8_t state_set = LAB_BS_DEFAULT; state_set <= LAB_BS_ALL; state_set++) { - if (!button->nodes[state_set]) { + struct scaled_img_buffer *buffer = button->img_buffers[state_set]; + if (!buffer) { continue; } - wlr_scene_node_set_enabled( - button->nodes[state_set], button->state_set == state_set); + wlr_scene_node_set_enabled(&buffer->scene_buffer->node, + state_set == button->state_set); } } @@ -584,37 +586,6 @@ ssd_update_window_icon(struct ssd *ssd) free(ssd->state.app_id); ssd->state.app_id = xstrdup(app_id); - struct theme *theme = ssd->view->server->theme; - - /* - * Ensure a small amount of horizontal padding within the button - * area (2px on each side with the default 26px button width). - * A new theme setting could be added to configure this. Using - * an existing setting (padding.width or window.button.spacing) - * was considered, but these settings have distinct purposes - * already and are zero by default. - */ - int icon_padding = theme->window_button_width / 10; - int icon_size = MIN(theme->window_button_width - 2 * icon_padding, - theme->window_button_height); - - /* - * Load/render icons at the max scale of any usable output (at - * this point in time). We don't want to be constantly reloading - * icons as views are moved between outputs. - * - * TODO: currently there's no signal to reload/render icons if - * outputs are reconfigured and the max scale changes. - */ - float icon_scale = output_max_scale(ssd->view->server); - - struct lab_img *icon_img = desktop_entry_load_icon_from_app_id( - ssd->view->server, app_id, icon_size, icon_scale); - if (!icon_img) { - wlr_log(WLR_DEBUG, "icon could not be loaded for %s", app_id); - return; - } - struct ssd_sub_tree *subtree; FOR_EACH_STATE(ssd, subtree) { struct ssd_part *part = ssd_get_part( @@ -623,23 +594,10 @@ ssd_update_window_icon(struct ssd *ssd) break; } - /* Replace all the buffers in the button with the window icon */ struct ssd_button *button = node_ssd_button_from_node(part->node); - for (uint8_t state_set = LAB_BS_DEFAULT; - state_set <= LAB_BS_ALL; state_set++) { - struct wlr_scene_node *node = button->nodes[state_set]; - if (node) { - struct scaled_img_buffer *img_buffer = - scaled_img_buffer_from_node(node); - scaled_img_buffer_update(img_buffer, icon_img, - theme->window_button_width - 2 * icon_padding, - theme->window_button_height); - wlr_scene_node_set_position(node, icon_padding, 0); - } - } + assert(button->window_icon); + scaled_icon_buffer_set_app_id(button->window_icon, app_id); } FOR_EACH_END - - lab_img_destroy(icon_img); #endif } diff --git a/src/theme.c b/src/theme.c index cb7809a7..511bc923 100644 --- a/src/theme.c +++ b/src/theme.c @@ -293,12 +293,6 @@ load_buttons(struct theme *theme) .type = LAB_SSD_BUTTON_WINDOW_MENU, .state_set = 0, .fallback_button = (const char[]){ 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00 }, - }, { - /* menu icon is loaded again as a fallback of window icon */ - .name = "menu", - .type = LAB_SSD_BUTTON_WINDOW_ICON, - .state_set = 0, - .fallback_button = (const char[]){ 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00 }, }, { .name = "iconify", .type = LAB_SSD_BUTTON_ICONIFY, @@ -344,12 +338,6 @@ load_buttons(struct theme *theme) .type = LAB_SSD_BUTTON_WINDOW_MENU, .state_set = LAB_BS_HOVERD, /* no fallback (non-hover variant is used instead) */ - }, { - /* menu_hover icon is loaded again as a fallback of window icon */ - .name = "menu_hover", - .type = LAB_SSD_BUTTON_WINDOW_ICON, - .state_set = LAB_BS_HOVERD, - /* no fallback (non-hover variant is used instead) */ }, { .name = "iconify_hover", .type = LAB_SSD_BUTTON_ICONIFY,