]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Support png buttons
authorJohan Malm <jgm323@gmail.com>
Fri, 4 Aug 2023 21:34:07 +0000 (22:34 +0100)
committerJohan Malm <johanmalm@users.noreply.github.com>
Wed, 9 Aug 2023 06:38:17 +0000 (07:38 +0100)
In the theme directory add close-{active,inactive}.png instead of
close.xbm - and similarly for iconify, menu and max.

docs/labwc-theme.5.scd
include/button-png.h [new file with mode: 0644]
meson.build
src/button-png.c [new file with mode: 0644]
src/meson.build
src/ssd/ssd_titlebar.c
src/theme.c

index e8cb1326dccbb54026b6f5cd9a56193e69093d24..6de7f1cd3e7cd86f541f3aa36f8cd7cf22a86be4 100644 (file)
@@ -18,7 +18,8 @@ searched for in the following order:
 Choosing a theme is done by editing the <name> key in the <theme> section of
 the rc.xml configuration file (labwc-config(5)).
 
-A theme consists of a themerc file and optionally some xbm icons.
+A theme consists of a themerc file and optionally some titlebar icons (referred
+to as buttons).
 
 Theme settings specified in themerc can be overridden by creating a
 'themerc-override' file in the configuration directory, which is normally
@@ -175,19 +176,26 @@ elements are not listed here, but are supported.
 
 # BUTTONS
 
-The images used for the titlebar buttons are 1-bit xbm (X Bitmaps). These are
-masks where 0=clear and 1=colored. The xbm image files are placed in the same
-directory within your theme as the themerc file. Here are all the possible xbm
-files looked for:
+The images used for the titlebar icons are referred to as buttons.
+
+The image formats listed below are supported. They are listed in order of
+precedence, where the first format in the list is searched for first.
+
+- png
+- xbm
+
+By default, buttons are 1-bit xbm (X Bitmaps). These are masks where 0=clear and
+1=colored. The xbm image files are placed in the same directory as the themerc
+file within a particular theme. The following xbm buttons are supported:
 
 - max.xbm
 - iconify.xbm
 - close.xbm
 - menu.xbm
 
-More will be supported later.
-
-Note: menu.xbm is not part of openbox-3.6 spec
+One advantage of xbm buttons over other formats is that they change color based
+on the theme. Other formats use the suffices "-active" and "-inactive" to align
+with the respective titlebar colors. For example: "close-active.png"
 
 # DEFINITIONS
 
diff --git a/include/button-png.h b/include/button-png.h
new file mode 100644 (file)
index 0000000..408a21f
--- /dev/null
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LABWC_BUTTON_PNG_H
+#define LABWC_BUTTON_PNG_H
+
+struct lab_data_buffer;
+
+void png_load(const char *filename, struct lab_data_buffer **buffer);
+
+#endif /* LABWC_BUTTON_PNG_H */
index 2d2d798b171a53e6b81f0406f89d8c19e4a9bdbd..b26e2b50c8642d41b96506a5e3218a4f3fa6da4f 100644 (file)
@@ -69,6 +69,7 @@ cairo = dependency('cairo')
 pangocairo = dependency('pangocairo')
 input = dependency('libinput', version: '>=1.14')
 math = cc.find_library('m')
+png = dependency('libpng')
 
 if get_option('xwayland').enabled() and not wlroots_has_xwayland
        error('no wlroots Xwayland support')
@@ -103,6 +104,7 @@ labwc_deps = [
   pangocairo,
   input,
   math,
+  png,
 ]
 
 subdir('include')
diff --git a/src/button-png.c b/src/button-png.c
new file mode 100644 (file)
index 0000000..8602391
--- /dev/null
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) Johan Malm 2023
+ */
+#define _POSIX_C_SOURCE 200809L
+#include <cairo.h>
+#include <png.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <wlr/util/log.h>
+#include "buffer.h"
+#include "button-png.h"
+#include "common/dir.h"
+#include "labwc.h"
+#include "theme.h"
+
+/* Share with session.c:isfile() */
+static bool
+file_exists(const char *path)
+{
+       struct stat st;
+       return (!stat(path, &st));
+}
+
+/* Share with xbm.c:xbm_path() */
+static char *
+button_path(const char *filename)
+{
+       static char buffer[4096] = { 0 };
+       snprintf(buffer, sizeof(buffer), "%s/%s", theme_dir(rc.theme_name), filename);
+       return buffer;
+}
+
+/*
+ * cairo_image_surface_create_from_png() does not gracefully handle non-png
+ * files, so we verify the header before trying to read the rest of the file.
+ */
+#define PNG_BYTES_TO_CHECK (4)
+static bool
+ispng(const char *filename)
+{
+       unsigned char header[PNG_BYTES_TO_CHECK];
+       FILE *fp = fopen(filename, "rb");
+       if (!fp) {
+               return false;
+       }
+       if (fread(header, 1, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) {
+               fclose(fp);
+               return false;
+       }
+       if (png_sig_cmp(header, (png_size_t)0, PNG_BYTES_TO_CHECK)) {
+               wlr_log(WLR_ERROR, "file '%s' is not a recognised png file", filename);
+               fclose(fp);
+               return false;
+       }
+       fclose(fp);
+       return true;
+}
+
+#undef PNG_BYTES_TO_CHECK
+
+void
+png_load(const char *filename, struct lab_data_buffer **buffer)
+{
+       if (*buffer) {
+               wlr_buffer_drop(&(*buffer)->base);
+               *buffer = NULL;
+       }
+
+       char *path = button_path(filename);
+       if (!file_exists(path) || !ispng(path)) {
+               return;
+       }
+
+       cairo_surface_t *image = cairo_image_surface_create_from_png(path);
+       if (cairo_surface_status(image)) {
+               wlr_log(WLR_ERROR, "error reading png button '%s'", path);
+               cairo_surface_destroy(image);
+               return;
+       }
+       cairo_surface_flush(image);
+
+       double w = cairo_image_surface_get_width(image);
+       double h = cairo_image_surface_get_height(image);
+       *buffer = buffer_create_cairo((int)w, (int)h, 1.0, true);
+       cairo_t *cairo = (*buffer)->cairo;
+       cairo_set_source_surface(cairo, image, 0, 0);
+       cairo_paint_with_alpha(cairo, 1.0);
+}
index 391e9cdc1f76bd122c38c4007764b8351a69b78c..dec3ed7ba8ce4f5a6a96813a6a1f36e3b3da7440 100644 (file)
@@ -15,6 +15,7 @@ labwc_sources = files(
   'node.c',
   'osd.c',
   'output.c',
+  'button-png.c',
   'regions.c',
   'resistance.c',
   'seat.c',
index c12a48b47bda21e4f83539c13c678a72e8c1ef84..11db050037e14b62a4a770a7452f86e1cba9cc3f 100644 (file)
@@ -53,7 +53,8 @@ ssd_titlebar_create(struct ssd *ssd)
                        corner_top_right = &theme->corner_top_right_inactive_normal->base;
                        menu_button_unpressed = &theme->button_menu_inactive_unpressed->base;
                        iconify_button_unpressed = &theme->button_iconify_inactive_unpressed->base;
-                       maximize_button_unpressed = &theme->button_maximize_inactive_unpressed->base;
+                       maximize_button_unpressed =
+                               &theme->button_maximize_inactive_unpressed->base;
                        close_button_unpressed = &theme->button_close_inactive_unpressed->base;
                        wlr_scene_node_set_enabled(&parent->node, false);
                }
index 5a3c489ce51fa219a9331f79177f6b94f426092d..3c216228bdbba272c3a02b3346070acfea8e9f2e 100644 (file)
@@ -24,6 +24,7 @@
 #include "common/match.h"
 #include "common/string-helpers.h"
 #include "config/rcxml.h"
+#include "button-png.h"
 #include "theme.h"
 #include "xbm/xbm.h"
 #include "buffer.h"
@@ -95,9 +96,23 @@ load_buttons(struct theme *theme)
        char filename[4096] = {0};
        for (size_t i = 0; i < sizeof(buttons) / sizeof(buttons[0]); ++i) {
                struct button *b = &buttons[i];
+
+               /* Try png icon first */
+               snprintf(filename, sizeof(filename), "%s-active.png", b->name);
+               png_load(filename, b->active.buffer);
+               snprintf(filename, sizeof(filename), "%s-inactive.png", b->name);
+               png_load(filename, b->inactive.buffer);
+
+               /* If there were no png buttons, use xbm */
                snprintf(filename, sizeof(filename), "%s.xbm", b->name);
-               xbm_load_button(filename, b->active.buffer, b->fallback_button, b->active.rgba);
-               xbm_load_button(filename, b->inactive.buffer, b->fallback_button, b->inactive.rgba);
+               if (!*b->active.buffer) {
+                       xbm_load_button(filename, b->active.buffer,
+                               b->fallback_button, b->active.rgba);
+               }
+               if (!*b->inactive.buffer) {
+                       xbm_load_button(filename, b->inactive.buffer,
+                               b->fallback_button, b->inactive.rgba);
+               }
        }
 }