Support png buttons

In the theme directory add close-{active,inactive}.png instead of
close.xbm - and similarly for iconify, menu and max.
This commit is contained in:
Johan Malm 2023-08-04 22:34:07 +01:00 committed by Johan Malm
parent eca98a9b6e
commit a6a03daae3
7 changed files with 138 additions and 11 deletions

View 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 Choosing a theme is done by editing the <name> key in the <theme> section of
the rc.xml configuration file (labwc-config(5)). 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 Theme settings specified in themerc can be overridden by creating a
'themerc-override' file in the configuration directory, which is normally 'themerc-override' file in the configuration directory, which is normally
@ -175,19 +176,26 @@ elements are not listed here, but are supported.
# BUTTONS # BUTTONS
The images used for the titlebar buttons are 1-bit xbm (X Bitmaps). These are The images used for the titlebar icons are referred to as buttons.
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 The image formats listed below are supported. They are listed in order of
files looked for: 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 - max.xbm
- iconify.xbm - iconify.xbm
- close.xbm - close.xbm
- menu.xbm - menu.xbm
More will be supported later. 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
Note: menu.xbm is not part of openbox-3.6 spec with the respective titlebar colors. For example: "close-active.png"
# DEFINITIONS # DEFINITIONS

9
include/button-png.h Normal file
View file

@ -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 */

View file

@ -69,6 +69,7 @@ cairo = dependency('cairo')
pangocairo = dependency('pangocairo') pangocairo = dependency('pangocairo')
input = dependency('libinput', version: '>=1.14') input = dependency('libinput', version: '>=1.14')
math = cc.find_library('m') math = cc.find_library('m')
png = dependency('libpng')
if get_option('xwayland').enabled() and not wlroots_has_xwayland if get_option('xwayland').enabled() and not wlroots_has_xwayland
error('no wlroots Xwayland support') error('no wlroots Xwayland support')
@ -103,6 +104,7 @@ labwc_deps = [
pangocairo, pangocairo,
input, input,
math, math,
png,
] ]
subdir('include') subdir('include')

91
src/button-png.c Normal file
View file

@ -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);
}

View file

@ -15,6 +15,7 @@ labwc_sources = files(
'node.c', 'node.c',
'osd.c', 'osd.c',
'output.c', 'output.c',
'button-png.c',
'regions.c', 'regions.c',
'resistance.c', 'resistance.c',
'seat.c', 'seat.c',

View file

@ -53,7 +53,8 @@ ssd_titlebar_create(struct ssd *ssd)
corner_top_right = &theme->corner_top_right_inactive_normal->base; corner_top_right = &theme->corner_top_right_inactive_normal->base;
menu_button_unpressed = &theme->button_menu_inactive_unpressed->base; menu_button_unpressed = &theme->button_menu_inactive_unpressed->base;
iconify_button_unpressed = &theme->button_iconify_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; close_button_unpressed = &theme->button_close_inactive_unpressed->base;
wlr_scene_node_set_enabled(&parent->node, false); wlr_scene_node_set_enabled(&parent->node, false);
} }

View file

@ -24,6 +24,7 @@
#include "common/match.h" #include "common/match.h"
#include "common/string-helpers.h" #include "common/string-helpers.h"
#include "config/rcxml.h" #include "config/rcxml.h"
#include "button-png.h"
#include "theme.h" #include "theme.h"
#include "xbm/xbm.h" #include "xbm/xbm.h"
#include "buffer.h" #include "buffer.h"
@ -95,9 +96,23 @@ load_buttons(struct theme *theme)
char filename[4096] = {0}; char filename[4096] = {0};
for (size_t i = 0; i < sizeof(buttons) / sizeof(buttons[0]); ++i) { for (size_t i = 0; i < sizeof(buttons) / sizeof(buttons[0]); ++i) {
struct button *b = &buttons[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); snprintf(filename, sizeof(filename), "%s.xbm", b->name);
xbm_load_button(filename, b->active.buffer, b->fallback_button, b->active.rgba); if (!*b->active.buffer) {
xbm_load_button(filename, b->inactive.buffer, b->fallback_button, b->inactive.rgba); 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);
}
} }
} }