]> git.mdlowis.com Git - proto/labwc.git/commitdiff
scaled-icon-buffer: add icon priority
authorConsolatis <35009135+Consolatis@users.noreply.github.com>
Sun, 15 Jun 2025 18:21:15 +0000 (20:21 +0200)
committerConsolatis <35009135+Consolatis@users.noreply.github.com>
Fri, 27 Jun 2025 12:05:42 +0000 (14:05 +0200)
Co-Authored-By: tokyo4j <hrak1529@gmail.com>
docs/labwc-config.5.scd
include/common/scaled-icon-buffer.h
include/window-rules.h
src/common/scaled-icon-buffer.c
src/config/rcxml.c
src/view.c
src/window-rules.c

index b6f3b439f46958d1e37147a8898fcf41b49d5b7b..97cca8ea11d13cabebaea610342df0921a554368 100644 (file)
@@ -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 *<margin>* settings or exclusive layer-shell clients
        such as panels.
 
+*<windowRules><windowRule iconPriority="">* [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
 
 ```
index b41cfcf9eaaaf834bd1a22297bcf5dc55bfe31d8..3039f15808b8e6ea8c09bec4220db6d65c6d1a32 100644 (file)
@@ -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;
index b93bc367b3dadd6601b65efa06cbbbe18c0088f0..4b7f66736819baf4261512ff18b54804e439ed1d 100644 (file)
@@ -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 */
 };
index a970452f3fda4e92b262dda693e1ef99a5dad8c1..29620c08964c2e4cf8aff9d9f332fd00a1bbd5b1 100644 (file)
@@ -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);
index 697f1f78db462fc6130112d1f56bbfbb7bfb1fb6..39dec8863ce0fa8f07809c9ac9e3bba12db14ee0 100644 (file)
@@ -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")) {
index 27a1d535258270f59b5d469aae610e6cea31c6e0..d8cb9b15b70cd02fb304a40fa210f7bf6844ebbe 100644 (file)
@@ -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
index 0f4f1952d233ffc7796e3efa2ca7c7e5924f136e..fa21f749e93e3de5e32a15f8cc107907568eb06a 100644 (file)
@@ -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;