]> git.mdlowis.com Git - proto/labwc.git/commitdiff
ssd: support window icons
authortokyo4j <hrak1529@gmail.com>
Fri, 6 Sep 2024 08:00:40 +0000 (17:00 +0900)
committerHiroaki Yamamoto <hrak1529@gmail.com>
Wed, 25 Sep 2024 09:04:09 +0000 (18:04 +0900)
The default `titleLayout` is updated to `icon:iconify,max,close` which
replaces the window menu button with the window icon.

When the icon file is not found or could not be loaded, the window menu
icon as before is shown.

The icon theme can be selected with `<theme><icon>`.

This commit adds libsfdo as an optional dependency. `-Dicon=disabled` can
be passsed to `meson setup` command in order to disable window icon, in
which case the window icon is always replaced with a window menu button.

32 files changed:
docs/labwc-config.5.scd
docs/rc.xml.all
include/button/button-png.h [deleted file]
include/button/button-svg.h [deleted file]
include/button/button-xbm.h [deleted file]
include/button/common.h [deleted file]
include/config/default-bindings.h
include/config/rcxml.h
include/icon-loader.h [new file with mode: 0644]
include/img/img-png.h [new file with mode: 0644]
include/img/img-svg.h [new file with mode: 0644]
include/img/img-xbm.h [new file with mode: 0644]
include/labwc.h
include/ssd-internal.h
include/ssd.h
meson.build
meson_options.txt
src/button/common.c [deleted file]
src/config/mousebind.c
src/config/rcxml.c
src/icon-loader.c [new file with mode: 0644]
src/img/img-png.c [moved from src/button/button-png.c with 78% similarity]
src/img/img-svg.c [moved from src/button/button-svg.c with 85% similarity]
src/img/img-xbm.c [moved from src/button/button-xbm.c with 94% similarity]
src/img/meson.build [moved from src/button/meson.build with 52% similarity]
src/meson.build
src/server.c
src/ssd/ssd-part.c
src/ssd/ssd-titlebar.c
src/theme.c
src/view.c
subprojects/libsfdo.wrap [new file with mode: 0644]

index 18a3efea6247e60d8fc485b2d746ef8ad034defd..2506e43cee8677b9dc3d96dc6c6e4b7e17cd53dd 100644 (file)
@@ -437,9 +437,13 @@ extending outward from the snapped edge.
 *<theme><name>*
        The name of the Openbox theme to use. It is not set by default.
 
+*<theme><icon>*
+       The name of the icon theme to use. It is not set by default.
+
 *<theme><titlebar><layout>*
        Selection and order of buttons in a window's titlebar.
        The following identifiers can be used, each only once:
+       - 'icon': window icon
        - 'menu': window menu
        - 'iconify': iconify
        - 'max': maximize toggle
@@ -624,6 +628,7 @@ extending outward from the snapped edge.
          buttons and the window title are shown.
        - Title: The area of the titlebar (including blank space) between
          the window buttons, where the window title is displayed.
+       - Icon: A window icon that, by default, displays a window menu.
        - WindowMenu: A button that, by default, displays a window menu.
        - Iconify: A button that, by default, iconifies a window.
        - Maximize: A button that, by default, toggles maximization of a window.
index b14ab6b00c04dfba0a5b96f56ec6a55ec16b79c6..10e0bd80d6693003fc3c20f51d27d6517f84bd9c 100644 (file)
@@ -28,8 +28,9 @@
   <!-- <font><theme> can be defined without an attribute to set all places -->
   <theme>
     <name></name>
+    <icon></icon>
     <titlebar>
-      <layout>menu:iconify,max,close</layout>
+      <layout>icon:iconify,max,close</layout>
       <showTitle>yes</showTitle>
     </titlebar>
     <cornerRadius>8</cornerRadius>
       </mousebind>
     </context>
 
+    <context name="Icon">
+      <mousebind button="Left" action="Click">
+        <action name="ShowMenu" menu="client-menu" atCursor="no" />
+      </mousebind>
+      <mousebind button="Right" action="Click">
+        <action name="ShowMenu" menu="client-menu" atCursor="no" />
+      </mousebind>
+    </context>
+
     <context name="Shade">
       <mousebind button="Left" action="Click">
         <action name="ToggleShade" />
diff --git a/include/button/button-png.h b/include/button/button-png.h
deleted file mode 100644 (file)
index 31d01ff..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-#ifndef LABWC_BUTTON_PNG_H
-#define LABWC_BUTTON_PNG_H
-
-struct lab_data_buffer;
-
-void button_png_load(const char *button_name, struct lab_data_buffer **buffer);
-
-#endif /* LABWC_BUTTON_PNG_H */
diff --git a/include/button/button-svg.h b/include/button/button-svg.h
deleted file mode 100644 (file)
index 884a884..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-#ifndef LABWC_BUTTON_SVG_H
-#define LABWC_BUTTON_SVG_H
-
-struct lab_data_buffer;
-
-void button_svg_load(const char *button_name, struct lab_data_buffer **buffer,
-       int size);
-
-#endif /* LABWC_BUTTON_SVG_H */
diff --git a/include/button/button-xbm.h b/include/button/button-xbm.h
deleted file mode 100644 (file)
index dd0e316..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-#ifndef LABWC_BUTTON_XBM_H
-#define LABWC_BUTTON_XBM_H
-
-struct lab_data_buffer;
-
-/**
- * button_xbm_from_bitmap() - create button from monochrome bitmap
- * @bitmap: bitmap data array in hexadecimal xbm format
- * @buffer: cairo-surface-buffer to create
- * @rgba: color
- *
- * Example bitmap: char button[6] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
- */
-void button_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer,
-       float *rgba);
-
-/* button_xbm_load - Convert xbm file to buffer with cairo surface */
-void button_xbm_load(const char *button_name, struct lab_data_buffer **buffer,
-       float *rgba);
-
-#endif /* LABWC_BUTTON_XBM_H */
diff --git a/include/button/common.h b/include/button/common.h
deleted file mode 100644 (file)
index b3b5039..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-#ifndef LABWC_BUTTON_COMMON_H
-#define LABWC_BUTTON_COMMON_H
-
-#include <stddef.h>
-
-/**
- * button_filename() - Get full filename for button.
- * @name: The name of the button (for example 'iconify.xbm').
- * @buf: Buffer to fill with the full filename
- * @len: Length of buffer
- *
- * Example return value: /usr/share/themes/Numix/openbox-3/iconify.xbm
- */
-void button_filename(const char *name, char *buf, size_t len);
-
-#endif /* LABWC_BUTTON_COMMON_H */
index 46a3819e8965f378c4321779279ca2945aa206fa..d901d9fa79d5f97647f80af92bb0214e1a9d2aec 100644 (file)
@@ -376,6 +376,32 @@ static struct mouse_combos {
                        .name = "atCursor",
                        .value = "no",
                },
+       }, {
+               .context = "Icon",
+               .button = "Left",
+               .event = "Click",
+               .action = "ShowMenu",
+               .attributes[0] = {
+                       .name = "menu",
+                       .value = "client-menu",
+               },
+               .attributes[1] = {
+                       .name = "atCursor",
+                       .value = "no",
+               },
+       }, {
+               .context = "Icon",
+               .button = "Right",
+               .event = "Click",
+               .action = "ShowMenu",
+               .attributes[0] = {
+                       .name = "menu",
+                       .value = "client-menu",
+               },
+               .attributes[1] = {
+                       .name = "atCursor",
+                       .value = "no",
+               },
        }, {
                .context = "Root",
                .button = "Left",
index 80ce4df74b29ea081bcd512c9abbf8ba9a6ce75f..dd237fa4ef827ca7d5861d11566d1d018a59da74 100644 (file)
@@ -81,6 +81,7 @@ struct rcxml {
 
        /* theme */
        char *theme_name;
+       char *icon_theme_name;
        struct wl_list title_buttons_left;
        struct wl_list title_buttons_right;
        int corner_radius;
diff --git a/include/icon-loader.h b/include/icon-loader.h
new file mode 100644 (file)
index 0000000..e6741dd
--- /dev/null
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_ICON_LOADER_H
+#define LABWC_ICON_LOADER_H
+
+struct server;
+
+void icon_loader_init(struct server *server);
+void icon_loader_finish(struct server *server);
+struct lab_data_buffer *icon_loader_lookup(struct server *server,
+       const char *app_id, int size, int scale);
+
+#endif /* LABWC_ICON_LOADER_H */
diff --git a/include/img/img-png.h b/include/img/img-png.h
new file mode 100644 (file)
index 0000000..1347a53
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_IMG_PNG_H
+#define LABWC_IMG_PNG_H
+
+struct lab_data_buffer;
+
+void img_png_load(const char *filename, struct lab_data_buffer **buffer);
+
+#endif /* LABWC_IMG_PNG_H */
diff --git a/include/img/img-svg.h b/include/img/img-svg.h
new file mode 100644 (file)
index 0000000..6843774
--- /dev/null
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_IMG_SVG_H
+#define LABWC_IMG_SVG_H
+
+struct lab_data_buffer;
+
+void img_svg_load(const char *filename, struct lab_data_buffer **buffer,
+       int size);
+
+#endif /* LABWC_IMG_SVG_H */
diff --git a/include/img/img-xbm.h b/include/img/img-xbm.h
new file mode 100644 (file)
index 0000000..24ad24d
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_IMG_XBM_H
+#define LABWC_IMG_XBM_H
+
+struct lab_data_buffer;
+
+/**
+ * img_xbm_from_bitmap() - create button from monochrome bitmap
+ * @bitmap: bitmap data array in hexadecimal xbm format
+ * @buffer: cairo-surface-buffer to create
+ * @rgba: color
+ *
+ * Example bitmap: char button[6] = { 0x3f, 0x3f, 0x21, 0x21, 0x21, 0x3f };
+ */
+void img_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer,
+       float *rgba);
+
+/* img_xbm_load - Convert xbm file to buffer with cairo surface */
+void img_xbm_load(const char *filename, struct lab_data_buffer **buffer,
+       float *rgba);
+
+#endif /* LABWC_IMG_XBM_H */
index 93eaf0c263bc47db3bf03352e41dc4c73dd82da9..af6ef3990cc4b3ed683a8264ca5cb29b74dd7c83 100644 (file)
@@ -365,6 +365,8 @@ struct server {
        struct menu *menu_current;
        struct wl_list menus;
 
+       struct icon_loader *icon_loader;
+
        pid_t primary_client_pid;
 };
 
index 3c0fe38b4d469addf1ed19deb7d80dc64ff03e43..14bd3265142069bfeb27853717a7d31517a8422d 100644 (file)
@@ -73,6 +73,8 @@ struct ssd {
                        struct ssd_state_title_width active;
                        struct ssd_state_title_width inactive;
                } title;
+
+               char *app_id;
        } state;
 
        /* An invisible area around the view which allows resizing */
@@ -145,6 +147,8 @@ struct ssd_part *add_scene_button(
 void add_toggled_icon(struct ssd_button *button, struct wl_list *part_list,
        enum ssd_part_type type, struct wlr_buffer *icon_buffer,
        struct wlr_buffer *hover_buffer);
+void update_window_icon_buffer(struct wlr_scene_node *button_node,
+       struct wlr_buffer *buffer);
 
 /* SSD internal helpers */
 struct ssd_part *ssd_get_part(
index 4a62d06aea8c8f29f2543aea01d2db54e056ed05..daad81a56a297ecb3b1a70b5b8f45b937dbf6382 100644 (file)
@@ -24,6 +24,7 @@ enum ssd_part_type {
        LAB_SSD_BUTTON_CLOSE,
        LAB_SSD_BUTTON_MAXIMIZE,
        LAB_SSD_BUTTON_ICONIFY,
+       LAB_SSD_BUTTON_WINDOW_ICON,
        LAB_SSD_BUTTON_WINDOW_MENU,
        LAB_SSD_BUTTON_SHADE,
        LAB_SSD_BUTTON_OMNIPRESENT,
@@ -87,6 +88,7 @@ void ssd_update_title(struct ssd *ssd);
 void ssd_update_geometry(struct ssd *ssd);
 void ssd_destroy(struct ssd *ssd);
 void ssd_set_titlebar(struct ssd *ssd, bool enabled);
+void ssd_update_window_icon(struct ssd *ssd);
 
 void ssd_enable_keybind_inhibit_indicator(struct ssd *ssd, bool enable);
 void ssd_enable_shade(struct ssd *ssd, bool enable);
index 3c63bda131a7d307dff8393305bafb0b730aa899..e5ccae58311642b8491bc7523839d9d464c70a0b 100644 (file)
@@ -73,6 +73,24 @@ pixman = dependency('pixman-1')
 math = cc.find_library('m')
 png = dependency('libpng')
 svg = dependency('librsvg-2.0', version: '>=2.46', required: false)
+sfdo_basedir = dependency(
+  'libsfdo-basedir',
+  default_options: ['default_library=static', 'examples=false', 'tests=false'],
+  version: '>=0.1.0',
+  required: not get_option('icon').disabled(),
+)
+sfdo_desktop = dependency(
+  'libsfdo-desktop',
+  default_options: ['default_library=static', 'examples=false', 'tests=false'],
+  version: '>=0.1.0',
+  required: not get_option('icon').disabled(),
+)
+sfdo_icon = dependency(
+  'libsfdo-icon',
+  default_options: ['default_library=static', 'examples=false', 'tests=false'],
+  version: '>=0.1.0',
+  required: not get_option('icon').disabled(),
+)
 
 if get_option('xwayland').enabled() and not wlroots_has_xwayland
        error('no wlroots Xwayland support')
@@ -88,6 +106,9 @@ else
 endif
 conf_data.set10('HAVE_RSVG', have_rsvg)
 
+have_libsfdo = sfdo_basedir.found() and sfdo_desktop.found() and sfdo_icon.found()
+conf_data.set10('HAVE_LIBSFDO', have_libsfdo)
+
 if get_option('static_analyzer').enabled()
   add_project_arguments(['-fanalyzer'], language: 'c')
 endif
@@ -126,6 +147,13 @@ if have_rsvg
     svg,
   ]
 endif
+if have_libsfdo
+  labwc_deps += [
+    sfdo_basedir,
+    sfdo_desktop,
+    sfdo_icon,
+  ]
+endif
 
 subdir('include')
 subdir('src')
index 4d6e8cd5564dd0076a081743f6095cca90e398aa..ec3fe85d9805c608628097aaa78ef07b50087f0c 100644 (file)
@@ -1,6 +1,7 @@
 option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
 option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
 option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons')
+option('icon', type: 'feature', value: 'enabled', description: 'Enable window icons')
 option('nls', type: 'feature', value: 'auto', description: 'Enable native language support')
 option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer')
 option('test', type: 'feature', value: 'disabled', description: 'Run tests')
diff --git a/src/button/common.c b/src/button/common.c
deleted file mode 100644 (file)
index 3940099..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include <stdio.h>
-#include <unistd.h>
-#include "button/common.h"
-#include "common/dir.h"
-#include "config/rcxml.h"
-#include "labwc.h"
-
-void
-button_filename(const char *name, char *buf, size_t len)
-{
-       struct wl_list paths;
-       paths_theme_create(&paths, rc.theme_name, name);
-
-       /*
-        * You can't really merge buttons, so let's just iterate forwards
-        * and stop on the first hit
-        */
-       struct path *path;
-       wl_list_for_each(path, &paths, link) {
-               if (access(path->string, R_OK) == 0) {
-                       snprintf(buf, len, "%s", path->string);
-                       break;
-               }
-       }
-       paths_destroy(&paths);
-}
index dc3dfb65092035479f467a6e57570032d90a812f..19169d555c511d6b7218b7c449d4909aebe4bebc 100644 (file)
@@ -114,6 +114,8 @@ context_from_str(const char *str)
                return LAB_SSD_BUTTON_ICONIFY;
        } else if (!strcasecmp(str, "WindowMenu")) {
                return LAB_SSD_BUTTON_WINDOW_MENU;
+       } else if (!strcasecmp(str, "Icon")) {
+               return LAB_SSD_BUTTON_WINDOW_ICON;
        } else if (!strcasecmp(str, "Shade")) {
                return LAB_SSD_BUTTON_SHADE;
        } else if (!strcasecmp(str, "AllDesktops")) {
index 88909f55ceb765bfcf8e04b774c922553013ce4b..ab221c1be51f585e187c0dcbed5471dee6f7e8f9 100644 (file)
@@ -151,7 +151,9 @@ fill_section(const char *content, struct wl_list *list)
                        continue;
                }
                enum ssd_part_type type = LAB_SSD_NONE;
-               if (!strcmp(identifier, "menu")) {
+               if (!strcmp(identifier, "icon")) {
+                       type = LAB_SSD_BUTTON_WINDOW_ICON;
+               } else if (!strcmp(identifier, "menu")) {
                        type = LAB_SSD_BUTTON_WINDOW_MENU;
                } else if (!strcmp(identifier, "iconify")) {
                        type = LAB_SSD_BUTTON_ICONIFY;
@@ -1067,6 +1069,8 @@ entry(xmlNode *node, char *nodename, char *content)
                rc.placement_cascade_offset_y = atoi(content);
        } else if (!strcmp(nodename, "name.theme")) {
                rc.theme_name = xstrdup(content);
+       } else if (!strcmp(nodename, "icon.theme")) {
+               rc.icon_theme_name = xstrdup(content);
        } else if (!strcasecmp(nodename, "layout.titlebar.theme")) {
                fill_title_layout(content);
        } else if (!strcasecmp(nodename, "showTitle.titlebar.theme")) {
@@ -1658,7 +1662,7 @@ post_processing(void)
        }
 
        if (!rc.title_layout_loaded) {
-               fill_title_layout("menu:iconify,max,close");
+               fill_title_layout("icon:iconify,max,close");
        }
 
        /*
@@ -1898,6 +1902,7 @@ rcxml_finish(void)
        zfree(rc.font_menuitem.name);
        zfree(rc.font_osd.name);
        zfree(rc.theme_name);
+       zfree(rc.icon_theme_name);
        zfree(rc.workspace_config.prefix);
 
        struct title_button *p, *p_tmp;
diff --git a/src/icon-loader.c b/src/icon-loader.c
new file mode 100644 (file)
index 0000000..969c35a
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sfdo-desktop.h>
+#include <sfdo-icon.h>
+#include <sfdo-basedir.h>
+#include <wlr/util/log.h>
+#include "common/mem.h"
+#include "config.h"
+#include "icon-loader.h"
+#include "img/img-png.h"
+
+#if HAVE_RSVG
+#include "img/img-svg.h"
+#endif
+
+#include "labwc.h"
+
+struct icon_loader {
+       struct sfdo_desktop_ctx *desktop_ctx;
+       struct sfdo_icon_ctx *icon_ctx;
+       struct sfdo_desktop_db *desktop_db;
+       struct sfdo_icon_theme *icon_theme;
+};
+
+void
+icon_loader_init(struct server *server)
+{
+       struct icon_loader *loader = znew(*loader);
+
+       struct sfdo_basedir_ctx *basedir_ctx = sfdo_basedir_ctx_create();
+       if (!basedir_ctx) {
+               goto err_basedir_ctx;
+       }
+       loader->desktop_ctx = sfdo_desktop_ctx_create(basedir_ctx);
+       if (!loader->desktop_ctx) {
+               goto err_desktop_ctx;
+       }
+       loader->icon_ctx = sfdo_icon_ctx_create(basedir_ctx);
+       if (!loader->icon_ctx) {
+               goto err_icon_ctx;
+       }
+       loader->desktop_db = sfdo_desktop_db_load(loader->desktop_ctx, NULL);
+       if (!loader->desktop_db) {
+               goto err_desktop_db;
+       }
+       loader->icon_theme = sfdo_icon_theme_load(loader->icon_ctx,
+               rc.icon_theme_name, SFDO_ICON_THEME_LOAD_OPTIONS_DEFAULT);
+       if (!loader->icon_theme) {
+               goto err_icon_theme;
+       }
+
+       /* basedir_ctx is not referenced by other objects */
+       sfdo_basedir_ctx_destroy(basedir_ctx);
+
+       server->icon_loader = loader;
+       return;
+
+err_icon_theme:
+       sfdo_desktop_db_destroy(loader->desktop_db);
+err_desktop_db:
+       sfdo_icon_ctx_destroy(loader->icon_ctx);
+err_icon_ctx:
+       sfdo_desktop_ctx_destroy(loader->desktop_ctx);
+err_desktop_ctx:
+       sfdo_basedir_ctx_destroy(basedir_ctx);
+err_basedir_ctx:
+       free(loader);
+       wlr_log(WLR_ERROR, "Failed to initialize icon loader");
+}
+
+void
+icon_loader_finish(struct server *server)
+{
+       struct icon_loader *loader = server->icon_loader;
+       if (!loader) {
+               return;
+       }
+
+       sfdo_desktop_db_destroy(loader->desktop_db);
+       sfdo_icon_ctx_destroy(loader->icon_ctx);
+       sfdo_desktop_ctx_destroy(loader->desktop_ctx);
+       free(loader);
+       server->icon_loader = NULL;
+}
+
+struct lab_data_buffer *
+icon_loader_lookup(struct server *server, const char *app_id, int size, int scale)
+{
+       struct icon_loader *loader = server->icon_loader;
+       if (!loader) {
+               return NULL;
+       }
+
+       const char *icon_name = NULL;
+       struct sfdo_desktop_entry *entry = sfdo_desktop_db_get_entry_by_id(
+               loader->desktop_db, app_id, SFDO_NT);
+       if (entry) {
+               icon_name = sfdo_desktop_entry_get_icon(entry, NULL);
+       }
+       if (!icon_name) {
+               /* fall back to app id */
+               icon_name = app_id;
+       }
+
+       int lookup_options = SFDO_ICON_THEME_LOOKUP_OPTIONS_DEFAULT;
+#if !HAVE_RSVG
+       lookup_options |= SFDO_ICON_THEME_LOOKUP_OPTION_NO_SVG;
+#endif
+       struct sfdo_icon_file *icon_file = sfdo_icon_theme_lookup(
+               loader->icon_theme, icon_name, SFDO_NT, size, scale,
+               lookup_options);
+       if (!icon_file || icon_file == SFDO_ICON_FILE_INVALID) {
+               return NULL;
+       }
+
+       struct lab_data_buffer *icon_buffer = NULL;
+       const char *icon_path = sfdo_icon_file_get_path(icon_file, NULL);
+
+       wlr_log(WLR_DEBUG, "loading icon file %s", icon_path);
+
+       switch (sfdo_icon_file_get_format(icon_file)) {
+       case SFDO_ICON_FILE_FORMAT_PNG:
+               img_png_load(icon_path, &icon_buffer);
+               break;
+       case SFDO_ICON_FILE_FORMAT_SVG:
+#if HAVE_RSVG
+               img_svg_load(icon_path, &icon_buffer, size * scale);
+#endif
+               break;
+       case SFDO_ICON_FILE_FORMAT_XPM:
+               /* XPM is not supported */
+               break;
+       }
+
+       sfdo_icon_file_destroy(icon_file);
+
+       return icon_buffer;
+}
similarity index 78%
rename from src/button/button-png.c
rename to src/img/img-png.c
index 2c700a06c5f35d8ed0973c8730120f713dc79d88..07e6a8a758657024eb7022b9270460ea6f419582 100644 (file)
@@ -10,8 +10,7 @@
 #include <stdlib.h>
 #include <wlr/util/log.h>
 #include "buffer.h"
-#include "button/button-png.h"
-#include "button/common.h"
+#include "img/img-png.h"
 #include "common/string-helpers.h"
 #include "labwc.h"
 
@@ -44,25 +43,22 @@ ispng(const char *filename)
 #undef PNG_BYTES_TO_CHECK
 
 void
-button_png_load(const char *button_name, struct lab_data_buffer **buffer)
+img_png_load(const char *filename, struct lab_data_buffer **buffer)
 {
        if (*buffer) {
                wlr_buffer_drop(&(*buffer)->base);
                *buffer = NULL;
        }
-       if (string_null_or_empty(button_name)) {
+       if (string_null_or_empty(filename)) {
                return;
        }
-
-       char path[4096] = { 0 };
-       button_filename(button_name, path, sizeof(path));
-       if (!ispng(path)) {
+       if (!ispng(filename)) {
                return;
        }
 
-       cairo_surface_t *image = cairo_image_surface_create_from_png(path);
+       cairo_surface_t *image = cairo_image_surface_create_from_png(filename);
        if (cairo_surface_status(image)) {
-               wlr_log(WLR_ERROR, "error reading png button '%s'", path);
+               wlr_log(WLR_ERROR, "error reading png button '%s'", filename);
                cairo_surface_destroy(image);
                return;
        }
similarity index 85%
rename from src/button/button-svg.c
rename to src/img/img-svg.c
index 2f27ea47400d61f8e255acf1b6ece1ce34f62a38..12c91102cfd49895841f0fca4366afdc0ff1fc7a 100644 (file)
 #include <stdlib.h>
 #include <wlr/util/log.h>
 #include "buffer.h"
-#include "button/button-svg.h"
-#include "button/common.h"
+#include "img/img-svg.h"
 #include "common/string-helpers.h"
 #include "labwc.h"
 
 void
-button_svg_load(const char *button_name, struct lab_data_buffer **buffer,
+img_svg_load(const char *filename, struct lab_data_buffer **buffer,
                int size)
 {
        if (*buffer) {
                wlr_buffer_drop(&(*buffer)->base);
                *buffer = NULL;
        }
-       if (string_null_or_empty(button_name)) {
-               return;
-       }
-
-       char filename[4096] = { 0 };
-       button_filename(button_name, filename, sizeof(filename));
        if (string_null_or_empty(filename)) {
                return;
        }
similarity index 94%
rename from src/button/button-xbm.c
rename to src/img/img-xbm.c
index 94ad238a3fe5cea24d09ee689d5359ed1fc4754c..98d8f618f8a4ccdee170b93e1d6b7c48e716995a 100644 (file)
@@ -13,8 +13,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <drm_fourcc.h>
-#include "button/button-xbm.h"
-#include "button/common.h"
+#include "img/img-xbm.h"
 #include "common/grab-file.h"
 #include "common/mem.h"
 #include "common/string-helpers.h"
@@ -257,7 +256,7 @@ parse_xbm_builtin(const char *button, int size)
 }
 
 void
-button_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer,
+img_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer,
                float *rgba)
 {
        struct pixmap pixmap = {0};
@@ -272,7 +271,7 @@ button_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer,
 }
 
 void
-button_xbm_load(const char *button_name, struct lab_data_buffer **buffer,
+img_xbm_load(const char *filename, struct lab_data_buffer **buffer,
                float *rgba)
 {
        struct pixmap pixmap = {0};
@@ -280,14 +279,12 @@ button_xbm_load(const char *button_name, struct lab_data_buffer **buffer,
                wlr_buffer_drop(&(*buffer)->base);
                *buffer = NULL;
        }
-       if (string_null_or_empty(button_name)) {
+       if (string_null_or_empty(filename)) {
                return;
        }
        color = argb32(rgba);
 
        /* Read file into memory as it's easier to tokenize that way */
-       char filename[4096] = { 0 };
-       button_filename(button_name, filename, sizeof(filename));
        struct buf token_buf = grab_file(filename);
        if (token_buf.len) {
                struct token *tokens = tokenize_xbm(token_buf.data);
similarity index 52%
rename from src/button/meson.build
rename to src/img/meson.build
index 2e587eafc5ae67fe4b3d23fe1aafea6524363815..854898d0467dc3aa51b34a8afdb82ff9b4b77b1f 100644 (file)
@@ -1,12 +1,11 @@
 labwc_sources += files(
-  'button-png.c',
-  'button-xbm.c',
-  'common.c',
+  'img-png.c',
+  'img-xbm.c',
 )
 
 if have_rsvg
   labwc_sources += files(
-    'button-svg.c',
+    'img-svg.c',
   )
 endif
 
index 9676bec1e53cf1ac9496ba75ea78ef477e745152..37ed893ad6dcfe69420f127ff89f9143ba4525d6 100644 (file)
@@ -44,8 +44,13 @@ if have_xwayland
   )
 endif
 
+if have_libsfdo
+  labwc_sources += files(
+    'icon-loader.c',
+  )
+endif
 
-subdir('button')
+subdir('img')
 subdir('common')
 subdir('config')
 subdir('decorations')
index 7f1caa304afbe4fe155c57d9a462c96dccaf5e19..bfe33c3ca95419d26a38c069b0362ef19ae2bcdb 100644 (file)
@@ -25,6 +25,9 @@
 #include "config/rcxml.h"
 #include "config/session.h"
 #include "decorations.h"
+#if HAVE_LIBSFDO
+#include "icon-loader.h"
+#endif
 #include "idle.h"
 #include "labwc.h"
 #include "layers.h"
@@ -56,6 +59,11 @@ reload_config_and_theme(struct server *server)
        theme_finish(server->theme);
        theme_init(server->theme, server, rc.theme_name);
 
+#if HAVE_LIBSFDO
+       icon_loader_finish(server);
+       icon_loader_init(server);
+#endif
+
        struct view *view;
        wl_list_for_each(view, &server->views, link) {
                view_reload_ssd(view);
@@ -547,6 +555,10 @@ server_init(struct server *server)
 
        layers_init(server);
 
+#if HAVE_LIBSFDO
+       icon_loader_init(server);
+#endif
+
 #if HAVE_XWAYLAND
        xwayland_server_init(server, compositor);
 #endif
@@ -597,4 +609,8 @@ server_finish(struct server *server)
 
        /* TODO: clean up various scene_tree nodes */
        workspaces_destroy(server);
+
+#if HAVE_LIBSFDO
+       icon_loader_finish(server);
+#endif
 }
index 4a5eda72776fc76cfca0a8699e715b4fa0ad828b..d647a272a8e756b5d37b504c96ef66452326f440 100644 (file)
@@ -104,6 +104,23 @@ get_scale_box(struct wlr_buffer *buffer, double container_width,
        return icon_geo;
 }
 
+void
+update_window_icon_buffer(struct wlr_scene_node *button_node,
+               struct wlr_buffer *buffer)
+{
+       struct wlr_scene_buffer *scene_buffer =
+               wlr_scene_buffer_from_node(button_node);
+
+       struct wlr_box icon_geo = get_scale_box(buffer,
+               rc.theme->window_button_width,
+               rc.theme->title_height);
+
+       wlr_scene_buffer_set_buffer(scene_buffer, buffer);
+       wlr_scene_buffer_set_dest_size(scene_buffer,
+               icon_geo.width, icon_geo.height);
+       wlr_scene_node_set_position(button_node, icon_geo.x, icon_geo.y);
+}
+
 struct ssd_part *
 add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
                struct wlr_scene_tree *parent, struct wlr_buffer *icon_buffer,
index 1b2f56ac1051c817f9440f21942326d0bbbcfdef..923ae91e1812a321b9a9de84ce24c669f3288daf 100644 (file)
@@ -4,10 +4,14 @@
 #include <assert.h>
 #include <string.h>
 #include "buffer.h"
+#include "config.h"
 #include "common/mem.h"
 #include "common/scaled-font-buffer.h"
 #include "common/scene-helpers.h"
 #include "common/string-helpers.h"
+#if HAVE_LIBSFDO
+#include "icon-loader.h"
+#endif
 #include "labwc.h"
 #include "node.h"
 #include "ssd-internal.h"
@@ -34,6 +38,7 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ
        struct ssd_button *btn;
 
        switch (type) {
+       case LAB_SSD_BUTTON_WINDOW_ICON: /* fallthrough */
        case LAB_SSD_BUTTON_WINDOW_MENU:
                add_scene_button(&subtree->parts, type, parent,
                        active ? &theme->button_menu_active_unpressed->base
@@ -171,6 +176,7 @@ ssd_titlebar_create(struct ssd *ssd)
        update_visible_buttons(ssd);
 
        ssd_update_title(ssd);
+       ssd_update_window_icon(ssd);
 
        bool maximized = view->maximized == VIEW_AXIS_BOTH;
        if (maximized) {
@@ -368,7 +374,9 @@ ssd_titlebar_update(struct ssd *ssd)
                        wlr_scene_node_set_position(part->node, x, 0);
                }
        } FOR_EACH_END
+
        ssd_update_title(ssd);
+       ssd_update_window_icon(ssd);
 }
 
 void
@@ -386,8 +394,10 @@ ssd_titlebar_destroy(struct ssd *ssd)
        } FOR_EACH_END
 
        if (ssd->state.title.text) {
-               free(ssd->state.title.text);
-               ssd->state.title.text = NULL;
+               zfree(ssd->state.title.text);
+       }
+       if (ssd->state.app_id) {
+               zfree(ssd->state.app_id);
        }
 
        wlr_scene_node_destroy(&ssd->titlebar.tree->node);
@@ -624,4 +634,50 @@ ssd_should_be_squared(struct ssd *ssd)
                && view->maximized != VIEW_AXIS_BOTH;
 }
 
+void
+ssd_update_window_icon(struct ssd *ssd)
+{
+#if HAVE_LIBSFDO
+       const char *app_id = view_get_string_prop(ssd->view, "app_id");
+       if (string_null_or_empty(app_id)) {
+               return;
+       }
+       if (ssd->state.app_id && !strcmp(ssd->state.app_id, app_id)) {
+               return;
+       }
+
+       free(ssd->state.app_id);
+       ssd->state.app_id = xstrdup(app_id);
+
+       struct theme *theme = ssd->view->server->theme;
+
+       int icon_size = MIN(theme->window_button_width,
+               theme->title_height - 2 * theme->padding_height);
+       /* TODO: take into account output scales */
+       int icon_scale = 1;
+
+       struct lab_data_buffer *icon_buffer = icon_loader_lookup(
+               ssd->view->server, app_id, icon_size, icon_scale);
+       if (!icon_buffer) {
+               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(&subtree->parts, LAB_SSD_BUTTON_WINDOW_ICON);
+               if (!part) {
+                       break;
+               }
+
+               struct ssd_button *button = node_ssd_button_from_node(part->node);
+               update_window_icon_buffer(button->normal, &icon_buffer->base);
+               update_window_icon_buffer(button->hover, &icon_buffer->base);
+       } FOR_EACH_END
+
+       wlr_buffer_drop(&icon_buffer->base);
+#endif
+}
+
 #undef FOR_EACH_STATE
index 6965d39e7856ee9f5ad9392452699209b4d9141b..50980af192885790de94a8f1785636b6a0de4063 100644 (file)
 #include "common/parse-double.h"
 #include "common/string-helpers.h"
 #include "config/rcxml.h"
-#include "button/button-png.h"
+#include "img/img-png.h"
 #include "labwc.h"
 
 #if HAVE_RSVG
-#include "button/button-svg.h"
+#include "img/img-svg.h"
 #endif
 
-#include "button/button-xbm.h"
+#include "img/img-xbm.h"
 #include "theme.h"
 #include "buffer.h"
 #include "ssd.h"
@@ -222,6 +222,36 @@ create_hover_fallback(struct theme *theme, const char *icon_name,
        }
 }
 
+/*
+ * Scan theme directories with button names (name + postfix) and write the full
+ * path of the found button file to @buf. An empty string is set if a button
+ * file is not found.
+ */
+static void
+get_button_filename(char *buf, size_t len, const char *name, const char *postfix)
+{
+       buf[0] = '\0';
+
+       char filename[4096];
+       snprintf(filename, sizeof(filename), "%s%s", name, postfix);
+
+       struct wl_list paths;
+       paths_theme_create(&paths, rc.theme_name, filename);
+
+       /*
+        * You can't really merge buttons, so let's just iterate forwards
+        * and stop on the first hit
+        */
+       struct path *path;
+       wl_list_for_each(path, &paths, link) {
+               if (access(path->string, R_OK) == 0) {
+                       snprintf(buf, len, "%s", path->string);
+                       break;
+               }
+       }
+       paths_destroy(&paths);
+}
+
 /*
  * We use the following button filename schema: "BUTTON [TOGGLED] [STATE]"
  * with the words separated by underscore, and the following meaning:
@@ -394,31 +424,31 @@ load_buttons(struct theme *theme)
                zdrop(b->inactive.buffer);
 
                /* PNG */
-               snprintf(filename, sizeof(filename), "%s-active.png", b->name);
-               button_png_load(filename, b->active.buffer);
-               snprintf(filename, sizeof(filename), "%s-inactive.png", b->name);
-               button_png_load(filename, b->inactive.buffer);
+               get_button_filename(filename, sizeof(filename), b->name, "-active.png");
+               img_png_load(filename, b->active.buffer);
+               get_button_filename(filename, sizeof(filename), b->name, "-inactive.png");
+               img_png_load(filename, b->inactive.buffer);
 
 #if HAVE_RSVG
                /* SVG */
                int size = theme->title_height - 2 * theme->padding_height;
                if (!*b->active.buffer) {
-                       snprintf(filename, sizeof(filename), "%s-active.svg", b->name);
-                       button_svg_load(filename, b->active.buffer, size);
+                       get_button_filename(filename, sizeof(filename), b->name, "-active.svg");
+                       img_svg_load(filename, b->active.buffer, size);
                }
                if (!*b->inactive.buffer) {
-                       snprintf(filename, sizeof(filename), "%s-inactive.svg", b->name);
-                       button_svg_load(filename, b->inactive.buffer, size);
+                       get_button_filename(filename, sizeof(filename), b->name, "-inactive.svg");
+                       img_svg_load(filename, b->inactive.buffer, size);
                }
 #endif
 
                /* XBM */
-               snprintf(filename, sizeof(filename), "%s.xbm", b->name);
+               get_button_filename(filename, sizeof(filename), b->name, ".xbm");
                if (!*b->active.buffer) {
-                       button_xbm_load(filename, b->active.buffer, b->active.rgba);
+                       img_xbm_load(filename, b->active.buffer, b->active.rgba);
                }
                if (!*b->inactive.buffer) {
-                       button_xbm_load(filename, b->inactive.buffer, b->inactive.rgba);
+                       img_xbm_load(filename, b->inactive.buffer, b->inactive.rgba);
                }
 
                /*
@@ -426,15 +456,15 @@ load_buttons(struct theme *theme)
                 * For example max_hover_toggled instead of max_toggled_hover
                 */
                if (b->alt_name) {
-                       snprintf(filename, sizeof(filename), "%s.xbm", b->alt_name);
+                       get_button_filename(filename, sizeof(filename), b->alt_name, ".xbm");
                }  else {
                        filename[0] = '\0';
                }
                if (!*b->active.buffer) {
-                       button_xbm_load(filename, b->active.buffer, b->active.rgba);
+                       img_xbm_load(filename, b->active.buffer, b->active.rgba);
                }
                if (!*b->inactive.buffer) {
-                       button_xbm_load(filename, b->inactive.buffer, b->inactive.rgba);
+                       img_xbm_load(filename, b->inactive.buffer, b->inactive.rgba);
                }
 
                /*
@@ -447,11 +477,11 @@ load_buttons(struct theme *theme)
                        continue;
                }
                if (!*b->active.buffer) {
-                       button_xbm_from_bitmap(b->fallback_button,
+                       img_xbm_from_bitmap(b->fallback_button,
                                b->active.buffer, b->active.rgba);
                }
                if (!*b->inactive.buffer) {
-                       button_xbm_from_bitmap(b->fallback_button,
+                       img_xbm_from_bitmap(b->fallback_button,
                                b->inactive.buffer, b->inactive.rgba);
                }
        }
index 973b079ff70c17152d01d28d51729591d2612e5e..00618a18250229327d03ce3b297cf13ff7bc0596 100644 (file)
@@ -2266,6 +2266,10 @@ view_update_app_id(struct view *view)
        }
        wlr_foreign_toplevel_handle_v1_set_app_id(
                view->toplevel.handle, app_id);
+
+       if (view->ssd_enabled) {
+               ssd_update_window_icon(view->ssd);
+       }
 }
 
 void
diff --git a/subprojects/libsfdo.wrap b/subprojects/libsfdo.wrap
new file mode 100644 (file)
index 0000000..22df1d6
--- /dev/null
@@ -0,0 +1,6 @@
+[wrap-git]
+url = https://gitlab.freedesktop.org/vyivel/libsfdo.git
+revision = v0.1.3
+
+[provide]
+dependency_names = libsfdo-basedir, libsfdo-desktop, libsfdo-icon