From a6a03daae33b3bf0aaca06025e4508cef732b933 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 4 Aug 2023 22:34:07 +0100 Subject: [PATCH] Support png buttons 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 | 24 +++++++---- include/button-png.h | 9 +++++ meson.build | 2 + src/button-png.c | 91 ++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 1 + src/ssd/ssd_titlebar.c | 3 +- src/theme.c | 19 ++++++++- 7 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 include/button-png.h create mode 100644 src/button-png.c diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index e8cb1326..6de7f1cd 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -18,7 +18,8 @@ searched for in the following order: Choosing a theme is done by editing the key in the 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 index 00000000..408a21fc --- /dev/null +++ b/include/button-png.h @@ -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 */ diff --git a/meson.build b/meson.build index 2d2d798b..b26e2b50 100644 --- a/meson.build +++ b/meson.build @@ -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 index 00000000..8602391c --- /dev/null +++ b/src/button-png.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) Johan Malm 2023 + */ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/src/meson.build b/src/meson.build index 391e9cdc..dec3ed7b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,6 +15,7 @@ labwc_sources = files( 'node.c', 'osd.c', 'output.c', + 'button-png.c', 'regions.c', 'resistance.c', 'seat.c', diff --git a/src/ssd/ssd_titlebar.c b/src/ssd/ssd_titlebar.c index c12a48b4..11db0500 100644 --- a/src/ssd/ssd_titlebar.c +++ b/src/ssd/ssd_titlebar.c @@ -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); } diff --git a/src/theme.c b/src/theme.c index 5a3c489c..3c216228 100644 --- a/src/theme.c +++ b/src/theme.c @@ -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); + } } }