From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sun, 15 Jun 2025 18:21:15 +0000 (+0200) Subject: scaled-icon-buffer: add icon priority X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=acd2a23f1bc9a0a6d9223067b8e8a3e882a3b9bd;p=proto%2Flabwc.git scaled-icon-buffer: add icon priority Co-Authored-By: tokyo4j --- diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index b6f3b439..97cca8ea 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -1178,7 +1178,7 @@ sandboxAppId="" type="" matchOnce="">* *Properties* -Property values can be *yes*, *no* or *default*. +Most property values can be *yes*, *no* or *default*. If a window matches criteria for multiple rules which set the same property, later config entries have higher priority. *default* can be useful in this @@ -1210,6 +1210,16 @@ situation. can be caused by ** settings or exclusive layer-shell clients such as panels. +** [client|server] + By default, labwc tries to find application icons based on their + app-id, either via .desktop file or by finding an icon with the same + name. If that fails labwc will then try to use client supplied icons, + accomplished with the xdg-toplevel-icon protocol for wayland native + applications or the \_NET_WM_ICON property for X11 applications. + + This property allows prioritizing client supplied icons for specific + applications. Default is server. + ## MENU ``` diff --git a/include/common/scaled-icon-buffer.h b/include/common/scaled-icon-buffer.h index b41cfcf9..3039f158 100644 --- a/include/common/scaled-icon-buffer.h +++ b/include/common/scaled-icon-buffer.h @@ -17,6 +17,7 @@ struct scaled_icon_buffer { struct view *view; char *view_app_id; char *view_icon_name; + bool view_icon_prefer_client; struct wl_array view_icon_buffers; struct { struct wl_listener set_icon; diff --git a/include/window-rules.h b/include/window-rules.h index b93bc367..4b7f6673 100644 --- a/include/window-rules.h +++ b/include/window-rules.h @@ -38,6 +38,7 @@ struct window_rule { enum property ignore_focus_request; enum property ignore_configure_request; enum property fixed_position; + enum property icon_prefer_client; struct wl_list link; /* struct rcxml.window_rules */ }; diff --git a/src/common/scaled-icon-buffer.c b/src/common/scaled-icon-buffer.c index a970452f..29620c08 100644 --- a/src/common/scaled-icon-buffer.c +++ b/src/common/scaled-icon-buffer.c @@ -15,6 +15,7 @@ #include "img/img.h" #include "node.h" #include "view.h" +#include "window-rules.h" #if HAVE_LIBSFDO @@ -42,6 +43,57 @@ choose_best_icon_buffer(struct scaled_icon_buffer *self, int icon_size, double s } return best_buffer; } + +static struct lab_data_buffer * +img_to_buffer(struct lab_img *img, int width, int height, int scale) +{ + struct lab_data_buffer *buffer = lab_img_render(img, width, height, scale); + lab_img_destroy(img); + return buffer; +} + +/* + * Load an icon from application-supplied icon name or buffers. + * Wayland apps can provide icon names and buffers via xdg-toplevel-icon protocol. + * X11 apps can provide icon buffers via _NET_WM_ICON property. + */ +static struct lab_data_buffer * +load_client_icon(struct scaled_icon_buffer *self, int icon_size, double scale) +{ + struct lab_img *img = desktop_entry_load_icon(self->server, + self->view_icon_name, icon_size, scale); + if (img) { + wlr_log(WLR_DEBUG, "loaded icon from client icon name"); + return img_to_buffer(img, self->width, self->height, scale); + } + + struct lab_data_buffer *buffer = choose_best_icon_buffer(self, icon_size, scale); + if (buffer) { + wlr_log(WLR_DEBUG, "loaded icon from client buffer"); + return buffer_resize(buffer, self->width, self->height, scale); + } + + return NULL; +} + +/* + * Load an icon by a view's app_id. For example, if the app_id is 'firefox', then + * libsfdo will parse firefox.desktop to get the Icon name and then find that icon + * based on the icon theme specified in rc.xml. + */ +static struct lab_data_buffer * +load_server_icon(struct scaled_icon_buffer *self, int icon_size, double scale) +{ + struct lab_img *img = desktop_entry_load_icon_from_app_id(self->server, + self->view_app_id, icon_size, scale); + if (img) { + wlr_log(WLR_DEBUG, "loaded icon by app_id"); + return img_to_buffer(img, self->width, self->height, scale); + } + + return NULL; +} + #endif /* HAVE_LIBSFDO */ static struct lab_data_buffer * @@ -51,50 +103,48 @@ _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) struct scaled_icon_buffer *self = scaled_buffer->data; int icon_size = MIN(self->width, self->height); struct lab_img *img = NULL; + struct lab_data_buffer *buffer = NULL; if (self->icon_name) { - img = desktop_entry_load_icon(self->server, - self->icon_name, icon_size, scale); - } else if (self->view) { - if (self->view_icon_name) { - wlr_log(WLR_DEBUG, "loading icon by name: %s", - self->view_icon_name); - img = desktop_entry_load_icon(self->server, - self->view_icon_name, icon_size, scale); + /* generic icon (e.g. menu icons) */ + img = desktop_entry_load_icon(self->server, self->icon_name, + icon_size, scale); + if (img) { + wlr_log(WLR_DEBUG, "loaded icon by icon name"); + return img_to_buffer(img, self->width, self->height, scale); + } + return NULL; + } + + /* window icon */ + if (self->view_icon_prefer_client) { + buffer = load_client_icon(self, icon_size, scale); + if (buffer) { + return buffer; } - if (!img) { - struct lab_data_buffer *buffer = - choose_best_icon_buffer(self, icon_size, scale); - if (buffer) { - wlr_log(WLR_DEBUG, "loading icon by buffer"); - return buffer_resize(buffer, - self->width, self->height, scale); - } + buffer = load_server_icon(self, icon_size, scale); + if (buffer) { + return buffer; } - if (!img) { - wlr_log(WLR_DEBUG, "loading icon by app_id"); - img = desktop_entry_load_icon_from_app_id(self->server, - self->view_app_id, icon_size, scale); + } else { + buffer = load_server_icon(self, icon_size, scale); + if (buffer) { + return buffer; } - if (!img) { - wlr_log(WLR_DEBUG, "loading fallback icon"); - img = desktop_entry_load_icon(self->server, - rc.fallback_app_icon_name, icon_size, scale); + buffer = load_client_icon(self, icon_size, scale); + if (buffer) { + return buffer; } } - - if (!img) { - return NULL; + /* If both client and server icons are unavailable, use the fallback icon */ + img = desktop_entry_load_icon(self->server, rc.fallback_app_icon_name, + icon_size, scale); + if (img) { + wlr_log(WLR_DEBUG, "loaded fallback icon"); + return img_to_buffer(img, self->width, self->height, scale); } - - struct lab_data_buffer *buffer = - lab_img_render(img, self->width, self->height, scale); - lab_img_destroy(img); - - return buffer; -#else - return NULL; #endif /* HAVE_LIBSFDO */ + return NULL; } static void @@ -149,6 +199,7 @@ _equal(struct scaled_scene_buffer *scaled_buffer_a, struct scaled_icon_buffer *b = scaled_buffer_b->data; return str_equal(a->view_app_id, b->view_app_id) + && a->view_icon_prefer_client == b->view_icon_prefer_client && str_equal(a->view_icon_name, b->view_icon_name) && icon_buffers_equal(&a->view_icon_buffers, &b->view_icon_buffers) && str_equal(a->icon_name, b->icon_name) @@ -189,6 +240,9 @@ handle_view_set_icon(struct wl_listener *listener, void *data) struct scaled_icon_buffer *self = wl_container_of(listener, self, on_view.set_icon); + self->view_icon_prefer_client = window_rules_get_property( + self->view, "iconPreferClient") == LAB_PROP_TRUE; + /* view_get_string_prop() never returns NULL */ xstrdup_replace(self->view_app_id, view_get_string_prop(self->view, "app_id")); zfree(self->view_icon_name); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 697f1f78..39dec886 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -328,6 +328,15 @@ fill_window_rule(char *nodename, char *content, struct parser_state *state) /* Properties */ } else if (!strcasecmp(nodename, "serverDecoration")) { set_property(content, &state->current_window_rule->server_decoration); + } else if (!strcasecmp(nodename, "iconPriority")) { + if (!strcasecmp(content, "client")) { + state->current_window_rule->icon_prefer_client = LAB_PROP_TRUE; + } else if (!strcasecmp(content, "server")) { + state->current_window_rule->icon_prefer_client = LAB_PROP_FALSE; + } else { + wlr_log(WLR_ERROR, + "Invalid value for window rule property 'iconPriority'"); + } } else if (!strcasecmp(nodename, "skipTaskbar")) { set_property(content, &state->current_window_rule->skip_taskbar); } else if (!strcasecmp(nodename, "skipWindowSwitcher")) { diff --git a/src/view.c b/src/view.c index 27a1d535..d8cb9b15 100644 --- a/src/view.c +++ b/src/view.c @@ -2400,6 +2400,7 @@ view_update_title(struct view *view) assert(view); ssd_update_title(view->ssd); wl_signal_emit_mutable(&view->events.new_title, NULL); + wl_signal_emit_mutable(&view->events.set_icon, NULL); } void diff --git a/src/window-rules.c b/src/window-rules.c index 0f4f1952..fa21f749 100644 --- a/src/window-rules.c +++ b/src/window-rules.c @@ -108,6 +108,10 @@ window_rules_get_property(struct view *view, const char *property) && !strcasecmp(property, "fixedPosition")) { return rule->fixed_position; } + if (rule->icon_prefer_client + && !strcasecmp(property, "iconPreferClient")) { + return rule->icon_prefer_client; + } } } return LAB_PROP_UNSPECIFIED;