ssd: dynamically look up window icons in titlebar for output scales

by introducing scaled_icon_buffer.
This commit is contained in:
tokyo4j 2025-01-12 16:43:49 +09:00 committed by Consolatis
parent 5e29f79258
commit dc474521ab
9 changed files with 225 additions and 102 deletions

View file

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef LABWC_SCALED_ICON_BUFFER_H
#define LABWC_SCALED_ICON_BUFFER_H
#include <stdbool.h>
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 */

View file

@ -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);

View file

@ -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;
};

View file

@ -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',

View file

@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <string.h>
#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;
}

View file

@ -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)
{

View file

@ -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,7 +104,28 @@ 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};
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]) {
@ -112,16 +138,13 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
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;
button->img_buffers[state_set] = img_buffer;
}
/* Initially show non-hover, non-toggled, unrounded variant */
wlr_scene_node_set_enabled(nodes[0], true);
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;
}

View file

@ -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
}

View file

@ -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,