mirror of
https://github.com/labwc/labwc.git
synced 2026-02-08 10:06:59 -05:00
ssd: rework titlebar button rendering
- fix that icons for normal/hovered/rounded buttons are not placed exactly the same position - fix blurry window button icons in scaled outputs This commit introduces lab_img and scaled_img_buffer and uses them for rendering icons in the window titlebar. Now the process of rendering button icons are split into 2 phases: loading with lab_img_load() and creating scene-nodes for them with scaled_img_buffer_create(). This might incur some additional overhead since we no longer preload icon textures, but the rendering of icon only happens for the first window as backing buffers are shared and the overhead won't be noticeable. This commit also simplifies the process of centering icon buffer in the button, by creating icon buffers in a fixed geometry via lab_img_render().
This commit is contained in:
parent
9a3412324d
commit
16dbdc64e5
25 changed files with 647 additions and 391 deletions
54
src/buffer.c
54
src/buffer.c
|
|
@ -29,7 +29,6 @@
|
|||
#include <drm_fourcc.h>
|
||||
#include <wlr/interfaces/wlr_buffer.h>
|
||||
#include "buffer.h"
|
||||
#include "common/box.h"
|
||||
#include "common/mem.h"
|
||||
|
||||
static const struct wlr_buffer_impl data_buffer_impl;
|
||||
|
|
@ -130,59 +129,6 @@ buffer_create_cairo(uint32_t logical_width, uint32_t logical_height, float scale
|
|||
return buffer;
|
||||
}
|
||||
|
||||
struct lab_data_buffer *
|
||||
buffer_convert_cairo_surface_for_icon(cairo_surface_t *surface,
|
||||
uint32_t icon_size, float scale)
|
||||
{
|
||||
assert(cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE);
|
||||
|
||||
/*
|
||||
* Compute logical size for display and decide whether we can
|
||||
* use the image data directly (fast path). Requirements are:
|
||||
*
|
||||
* - The pixel format must be ARGB32.
|
||||
* - The image must not be so large as to need downsampling by
|
||||
* more than 2x when displayed at the target scale. wlr_scene
|
||||
* uses linear interpolation without pixel averaging, which
|
||||
* starts to skip samples if downsampling more than 2x,
|
||||
* resulting in a grainy look.
|
||||
*/
|
||||
int width = cairo_image_surface_get_width(surface);
|
||||
int height = cairo_image_surface_get_height(surface);
|
||||
struct wlr_box logical =
|
||||
box_fit_within(width, height, icon_size, icon_size);
|
||||
struct lab_data_buffer *buffer;
|
||||
|
||||
if (cairo_image_surface_get_format(surface) == CAIRO_FORMAT_ARGB32
|
||||
&& width <= 2 * logical.width * scale
|
||||
&& height <= 2 * logical.height * scale) {
|
||||
buffer = buffer_adopt_cairo_surface(surface);
|
||||
/* set logical size for display */
|
||||
buffer->logical_width = logical.width;
|
||||
buffer->logical_height = logical.height;
|
||||
} else {
|
||||
/* convert to ARGB32 and scale for display (slow path) */
|
||||
buffer = buffer_create_cairo(logical.width,
|
||||
logical.height, scale);
|
||||
|
||||
cairo_t *cairo = cairo_create(buffer->surface);
|
||||
cairo_scale(cairo, (double)logical.width / width,
|
||||
(double)logical.height / height);
|
||||
cairo_set_source_surface(cairo, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cairo),
|
||||
CAIRO_FILTER_GOOD);
|
||||
cairo_paint(cairo);
|
||||
|
||||
/* ensure pixel data is updated */
|
||||
cairo_surface_flush(buffer->surface);
|
||||
/* destroy original cairo surface & context */
|
||||
cairo_surface_destroy(surface);
|
||||
cairo_destroy(cairo);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct lab_data_buffer *
|
||||
buffer_create_from_data(void *pixel_data, uint32_t width, uint32_t height,
|
||||
uint32_t stride)
|
||||
|
|
|
|||
|
|
@ -49,27 +49,27 @@ box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b
|
|||
}
|
||||
|
||||
struct wlr_box
|
||||
box_fit_within(int width, int height, int max_width, int max_height)
|
||||
box_fit_within(int width, int height, struct wlr_box *bound)
|
||||
{
|
||||
struct wlr_box box;
|
||||
|
||||
if (width <= max_width && height <= max_height) {
|
||||
if (width <= bound->width && height <= bound->height) {
|
||||
/* No downscaling needed */
|
||||
box.width = width;
|
||||
box.height = height;
|
||||
} else if (width * max_height > height * max_width) {
|
||||
} else if (width * bound->height > height * bound->width) {
|
||||
/* Wider content, fit width */
|
||||
box.width = max_width;
|
||||
box.height = (height * max_width + (width / 2)) / width;
|
||||
box.width = bound->width;
|
||||
box.height = (height * bound->width + (width / 2)) / width;
|
||||
} else {
|
||||
/* Taller content, fit height */
|
||||
box.width = (width * max_height + (height / 2)) / height;
|
||||
box.height = max_height;
|
||||
box.width = (width * bound->height + (height / 2)) / height;
|
||||
box.height = bound->height;
|
||||
}
|
||||
|
||||
/* Compute centered position */
|
||||
box.x = (max_width - box.width) / 2;
|
||||
box.y = (max_height - box.height) / 2;
|
||||
box.x = bound->x + (bound->width - box.width) / 2;
|
||||
box.y = bound->y + (bound->height - box.height) / 2;
|
||||
|
||||
return box;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ labwc_sources += files(
|
|||
'parse-bool.c',
|
||||
'parse-double.c',
|
||||
'scaled-font-buffer.c',
|
||||
'scaled-img-buffer.c',
|
||||
'scaled-rect-buffer.c',
|
||||
'scaled-scene-buffer.c',
|
||||
'scene-helpers.c',
|
||||
|
|
|
|||
90
src/common/scaled-img-buffer.c
Normal file
90
src/common/scaled-img-buffer.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <wayland-server-core.h>
|
||||
#include <wlr/types/wlr_scene.h>
|
||||
#include "buffer.h"
|
||||
#include "common/list.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/scaled-img-buffer.h"
|
||||
#include "common/scaled-scene-buffer.h"
|
||||
#include "img/img.h"
|
||||
#include "node.h"
|
||||
|
||||
static struct wl_list cached_buffers = WL_LIST_INIT(&cached_buffers);
|
||||
|
||||
static struct lab_data_buffer *
|
||||
_create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale)
|
||||
{
|
||||
struct scaled_img_buffer *self = scaled_buffer->data;
|
||||
struct lab_data_buffer *buffer = lab_img_render(self->img,
|
||||
self->width, self->height, self->padding, scale);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
_destroy(struct scaled_scene_buffer *scaled_buffer)
|
||||
{
|
||||
struct scaled_img_buffer *self = scaled_buffer->data;
|
||||
free(self);
|
||||
}
|
||||
|
||||
static bool
|
||||
_equal(struct scaled_scene_buffer *scaled_buffer_a,
|
||||
struct scaled_scene_buffer *scaled_buffer_b)
|
||||
{
|
||||
struct scaled_img_buffer *a = scaled_buffer_a->data;
|
||||
struct scaled_img_buffer *b = scaled_buffer_b->data;
|
||||
|
||||
return a->img == b->img
|
||||
&& a->width == b->width
|
||||
&& a->height == b->height
|
||||
&& a->padding == b->padding;
|
||||
}
|
||||
|
||||
static struct scaled_scene_buffer_impl impl = {
|
||||
.create_buffer = _create_buffer,
|
||||
.destroy = _destroy,
|
||||
.equal = _equal,
|
||||
};
|
||||
|
||||
struct scaled_img_buffer *
|
||||
scaled_img_buffer_create(struct wlr_scene_tree *parent, struct lab_img *img,
|
||||
int width, int height, int padding)
|
||||
{
|
||||
struct scaled_scene_buffer *scaled_buffer = scaled_scene_buffer_create(
|
||||
parent, &impl, &cached_buffers, /* drop_buffer */ true);
|
||||
struct scaled_img_buffer *self = znew(*self);
|
||||
self->scaled_buffer = scaled_buffer;
|
||||
self->scene_buffer = scaled_buffer->scene_buffer;
|
||||
self->img = img;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->padding = padding;
|
||||
|
||||
scaled_buffer->data = self;
|
||||
|
||||
scaled_scene_buffer_request_update(scaled_buffer, width, height);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
scaled_img_buffer_update(struct scaled_img_buffer *self, struct lab_img *img,
|
||||
int width, int height, int padding)
|
||||
{
|
||||
self->img = img;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->padding = padding;
|
||||
scaled_scene_buffer_request_update(self->scaled_buffer, width, height);
|
||||
}
|
||||
|
||||
struct scaled_img_buffer *
|
||||
scaled_img_buffer_from_node(struct wlr_scene_node *node)
|
||||
{
|
||||
struct scaled_scene_buffer *scaled_buffer =
|
||||
node_scaled_scene_buffer_from_node(node);
|
||||
assert(scaled_buffer->impl == &impl);
|
||||
return scaled_buffer->data;
|
||||
}
|
||||
|
|
@ -8,14 +8,8 @@
|
|||
#include "common/macros.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "config.h"
|
||||
#include "desktop-entry.h"
|
||||
#include "img/img-png.h"
|
||||
#include "img/img-xpm.h"
|
||||
|
||||
#if HAVE_RSVG
|
||||
#include "img/img-svg.h"
|
||||
#endif
|
||||
#include "img/img.h"
|
||||
|
||||
#include "labwc.h"
|
||||
|
||||
|
|
@ -267,7 +261,22 @@ get_desktop_entry(struct sfdo *sfdo, const char *app_id)
|
|||
return entry;
|
||||
}
|
||||
|
||||
struct lab_data_buffer *
|
||||
static enum lab_img_type
|
||||
convert_img_type(enum sfdo_icon_file_format fmt)
|
||||
{
|
||||
switch (fmt) {
|
||||
case SFDO_ICON_FILE_FORMAT_PNG:
|
||||
return LAB_IMG_PNG;
|
||||
case SFDO_ICON_FILE_FORMAT_SVG:
|
||||
return LAB_IMG_SVG;
|
||||
case SFDO_ICON_FILE_FORMAT_XPM:
|
||||
return LAB_IMG_XPM;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
struct lab_img *
|
||||
desktop_entry_icon_lookup(struct server *server, const char *app_id, int size,
|
||||
float scale)
|
||||
{
|
||||
|
|
@ -308,26 +317,12 @@ desktop_entry_icon_lookup(struct server *server, const char *app_id, int size,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct lab_data_buffer *icon_buffer = NULL;
|
||||
|
||||
wlr_log(WLR_DEBUG, "loading icon file %s", ctx.path);
|
||||
|
||||
switch (ctx.format) {
|
||||
case SFDO_ICON_FILE_FORMAT_PNG:
|
||||
img_png_load(ctx.path, &icon_buffer, size, scale);
|
||||
break;
|
||||
case SFDO_ICON_FILE_FORMAT_SVG:
|
||||
#if HAVE_RSVG
|
||||
img_svg_load(ctx.path, &icon_buffer, size, scale);
|
||||
#endif
|
||||
break;
|
||||
case SFDO_ICON_FILE_FORMAT_XPM:
|
||||
img_xpm_load(ctx.path, &icon_buffer, size, scale);
|
||||
break;
|
||||
}
|
||||
struct lab_img *img = lab_img_load(convert_img_type(ctx.format), ctx.path, NULL);
|
||||
|
||||
free(ctx.path);
|
||||
return icon_buffer;
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
const char *
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright (C) Johan Malm 2023
|
||||
*/
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <assert.h>
|
||||
#include <cairo.h>
|
||||
#include <png.h>
|
||||
#include <stdbool.h>
|
||||
|
|
@ -10,9 +11,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "buffer.h"
|
||||
#include "img/img-png.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "labwc.h"
|
||||
#include "img/img-png.h"
|
||||
|
||||
/*
|
||||
* cairo_image_surface_create_from_png() does not gracefully handle non-png
|
||||
|
|
@ -42,27 +42,22 @@ ispng(const char *filename)
|
|||
|
||||
#undef PNG_BYTES_TO_CHECK
|
||||
|
||||
void
|
||||
img_png_load(const char *filename, struct lab_data_buffer **buffer, int size,
|
||||
float scale)
|
||||
struct lab_data_buffer *
|
||||
img_png_load(const char *filename)
|
||||
{
|
||||
if (*buffer) {
|
||||
wlr_buffer_drop(&(*buffer)->base);
|
||||
*buffer = NULL;
|
||||
}
|
||||
if (string_null_or_empty(filename)) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
if (!ispng(filename)) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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'", filename);
|
||||
wlr_log(WLR_ERROR, "error reading png file '%s'", filename);
|
||||
cairo_surface_destroy(image);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*buffer = buffer_convert_cairo_surface_for_icon(image, size, scale);
|
||||
return buffer_adopt_cairo_surface(image);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,24 +10,18 @@
|
|||
#include <stdlib.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "buffer.h"
|
||||
#include "img/img-svg.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "img/img-svg.h"
|
||||
#include "labwc.h"
|
||||
|
||||
void
|
||||
img_svg_load(const char *filename, struct lab_data_buffer **buffer, int size,
|
||||
float scale)
|
||||
RsvgHandle *
|
||||
img_svg_load(const char *filename)
|
||||
{
|
||||
if (*buffer) {
|
||||
wlr_buffer_drop(&(*buffer)->base);
|
||||
*buffer = NULL;
|
||||
}
|
||||
if (string_null_or_empty(filename)) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GError *err = NULL;
|
||||
RsvgRectangle viewport = { .width = size, .height = size };
|
||||
RsvgHandle *svg = rsvg_handle_new_from_file(filename, &err);
|
||||
if (err) {
|
||||
wlr_log(WLR_DEBUG, "error reading svg %s-%s", filename, err->message);
|
||||
|
|
@ -36,33 +30,43 @@ img_svg_load(const char *filename, struct lab_data_buffer **buffer, int size,
|
|||
* rsvg_handle_new_from_file() returns NULL if an error occurs,
|
||||
* so there is no need to free svg here.
|
||||
*/
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
return svg;
|
||||
}
|
||||
|
||||
*buffer = buffer_create_cairo(size, size, scale);
|
||||
cairo_surface_t *image = (*buffer)->surface;
|
||||
struct lab_data_buffer *
|
||||
img_svg_render(RsvgHandle *svg, int w, int h, int padding, double scale)
|
||||
{
|
||||
struct lab_data_buffer *buffer = buffer_create_cairo(w, h, scale);
|
||||
cairo_surface_t *image = buffer->surface;
|
||||
cairo_t *cr = cairo_create(image);
|
||||
GError *err = NULL;
|
||||
|
||||
RsvgRectangle viewport = {
|
||||
.x = padding,
|
||||
.y = padding,
|
||||
.width = w - 2 * padding,
|
||||
.height = h - 2 * padding,
|
||||
};
|
||||
rsvg_handle_render_document(svg, cr, &viewport, &err);
|
||||
if (err) {
|
||||
wlr_log(WLR_ERROR, "error rendering svg %s-%s", filename, err->message);
|
||||
wlr_log(WLR_ERROR, "error rendering svg: %s", err->message);
|
||||
g_error_free(err);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cairo_surface_status(image)) {
|
||||
wlr_log(WLR_ERROR, "error reading svg button '%s'", filename);
|
||||
wlr_log(WLR_ERROR, "error reading svg file");
|
||||
goto error;
|
||||
}
|
||||
cairo_surface_flush(image);
|
||||
cairo_surface_flush(buffer->surface);
|
||||
cairo_destroy(cr);
|
||||
|
||||
g_object_unref(svg);
|
||||
return;
|
||||
return buffer;
|
||||
|
||||
error:
|
||||
wlr_buffer_drop(&(*buffer)->base);
|
||||
*buffer = NULL;
|
||||
wlr_buffer_drop(&buffer->base);
|
||||
cairo_destroy(cr);
|
||||
g_object_unref(svg);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include "img/img.h"
|
||||
#include "img/img-xbm.h"
|
||||
#include "common/grab-file.h"
|
||||
#include "common/mem.h"
|
||||
|
|
@ -255,32 +256,23 @@ parse_xbm_builtin(const char *button, int size)
|
|||
return pixmap;
|
||||
}
|
||||
|
||||
void
|
||||
img_xbm_from_bitmap(const char *bitmap, struct lab_data_buffer **buffer,
|
||||
float *rgba)
|
||||
struct lab_data_buffer *
|
||||
img_xbm_load_from_bitmap(const char *bitmap, float *rgba)
|
||||
{
|
||||
struct pixmap pixmap = {0};
|
||||
if (*buffer) {
|
||||
wlr_buffer_drop(&(*buffer)->base);
|
||||
*buffer = NULL;
|
||||
}
|
||||
color = argb32(rgba);
|
||||
pixmap = parse_xbm_builtin(bitmap, 6);
|
||||
*buffer = buffer_create_from_data(pixmap.data, pixmap.width, pixmap.height,
|
||||
|
||||
return buffer_create_from_data(pixmap.data, pixmap.width, pixmap.height,
|
||||
pixmap.width * 4);
|
||||
}
|
||||
|
||||
void
|
||||
img_xbm_load(const char *filename, struct lab_data_buffer **buffer,
|
||||
float *rgba)
|
||||
struct lab_data_buffer *
|
||||
img_xbm_load(const char *filename, float *rgba)
|
||||
{
|
||||
struct pixmap pixmap = {0};
|
||||
if (*buffer) {
|
||||
wlr_buffer_drop(&(*buffer)->base);
|
||||
*buffer = NULL;
|
||||
}
|
||||
if (string_null_or_empty(filename)) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
color = argb32(rgba);
|
||||
|
||||
|
|
@ -295,12 +287,9 @@ img_xbm_load(const char *filename, struct lab_data_buffer **buffer,
|
|||
}
|
||||
buf_reset(&token_buf);
|
||||
if (!pixmap.data) {
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create buffer */
|
||||
if (pixmap.data) {
|
||||
*buffer = buffer_create_from_data(pixmap.data, pixmap.width,
|
||||
pixmap.height, pixmap.width * 4);
|
||||
}
|
||||
return buffer_create_from_data(pixmap.data, pixmap.width,
|
||||
pixmap.height, pixmap.width * 4);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -397,30 +397,26 @@ out:
|
|||
return surface;
|
||||
}
|
||||
|
||||
void
|
||||
img_xpm_load(const char *filename, struct lab_data_buffer **buffer, int size,
|
||||
float scale)
|
||||
struct lab_data_buffer *
|
||||
img_xpm_load(const char *filename)
|
||||
{
|
||||
if (*buffer) {
|
||||
wlr_buffer_drop(&(*buffer)->base);
|
||||
*buffer = NULL;
|
||||
}
|
||||
|
||||
struct file_handle h = {0};
|
||||
h.infile = fopen(filename, "rb");
|
||||
if (!h.infile) {
|
||||
wlr_log(WLR_ERROR, "error opening '%s'", filename);
|
||||
return;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cairo_surface_t *surface = xpm_load_to_surface(&h);
|
||||
struct lab_data_buffer *buffer = NULL;
|
||||
if (surface) {
|
||||
*buffer = buffer_convert_cairo_surface_for_icon(surface, size,
|
||||
scale);
|
||||
buffer = buffer_adopt_cairo_surface(surface);
|
||||
} else {
|
||||
wlr_log(WLR_ERROR, "error loading '%s'", filename);
|
||||
}
|
||||
|
||||
fclose(h.infile);
|
||||
buf_reset(&h.buf);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
|||
222
src/img/img.c
Normal file
222
src/img/img.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <assert.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include "buffer.h"
|
||||
#include "config.h"
|
||||
#include "common/box.h"
|
||||
#include "common/graphic-helpers.h"
|
||||
#include "common/macros.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "img/img.h"
|
||||
#include "img/img-png.h"
|
||||
#if HAVE_RSVG
|
||||
#include "img/img-svg.h"
|
||||
#endif
|
||||
#include "img/img-xbm.h"
|
||||
#include "img/img-xpm.h"
|
||||
#include "labwc.h"
|
||||
#include "theme.h"
|
||||
|
||||
struct lab_img_cache {
|
||||
enum lab_img_type type;
|
||||
/* lab_img_cache is refcounted to be shared by multiple lab_imgs */
|
||||
int refcount;
|
||||
|
||||
/* Handler for the loaded image file */
|
||||
struct lab_data_buffer *buffer; /* for PNG/XBM/XPM image */
|
||||
#if HAVE_RSVG
|
||||
RsvgHandle *svg; /* for SVG image */
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct lab_img *
|
||||
create_img(struct lab_img_cache *cache)
|
||||
{
|
||||
struct lab_img *img = znew(*img);
|
||||
img->cache = cache;
|
||||
cache->refcount++;
|
||||
wl_array_init(&img->modifiers);
|
||||
return img;
|
||||
}
|
||||
|
||||
struct lab_img *
|
||||
lab_img_load(enum lab_img_type type, const char *path, float *xbm_color)
|
||||
{
|
||||
if (string_null_or_empty(path)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lab_img_cache *cache = znew(*cache);
|
||||
cache->type = type;
|
||||
|
||||
switch (type) {
|
||||
case LAB_IMG_PNG:
|
||||
cache->buffer = img_png_load(path);
|
||||
break;
|
||||
case LAB_IMG_XBM:
|
||||
assert(xbm_color);
|
||||
cache->buffer = img_xbm_load(path, xbm_color);
|
||||
break;
|
||||
case LAB_IMG_XPM:
|
||||
cache->buffer = img_xpm_load(path);
|
||||
break;
|
||||
case LAB_IMG_SVG:
|
||||
#if HAVE_RSVG
|
||||
cache->svg = img_svg_load(path);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
bool img_is_loaded = (bool)cache->buffer;
|
||||
#if HAVE_RSVG
|
||||
img_is_loaded |= (bool)cache->svg;
|
||||
#endif
|
||||
|
||||
if (img_is_loaded) {
|
||||
return create_img(cache);
|
||||
} else {
|
||||
free(cache);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct lab_img *
|
||||
lab_img_load_from_bitmap(const char *bitmap, float *rgba)
|
||||
{
|
||||
struct lab_data_buffer *buffer = img_xbm_load_from_bitmap(bitmap, rgba);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lab_img_cache *cache = znew(*cache);
|
||||
cache->type = LAB_IMG_XBM;
|
||||
cache->buffer = buffer;
|
||||
|
||||
return create_img(cache);
|
||||
}
|
||||
|
||||
struct lab_img *
|
||||
lab_img_copy(struct lab_img *img)
|
||||
{
|
||||
struct lab_img *new_img = create_img(img->cache);
|
||||
wl_array_copy(&new_img->modifiers, &img->modifiers);
|
||||
return new_img;
|
||||
}
|
||||
|
||||
void
|
||||
lab_img_add_modifier(struct lab_img *img, lab_img_modifier_func_t modifier,
|
||||
struct theme *theme)
|
||||
{
|
||||
img->theme = theme;
|
||||
lab_img_modifier_func_t *mod = wl_array_add(&img->modifiers, sizeof(*mod));
|
||||
*mod = modifier;
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes a source surface from PNG/XBM/XPM file and output a buffer for the
|
||||
* given size. The source surface is placed at the center of the output buffer
|
||||
* and shrunk if it overflows from the output buffer.
|
||||
*/
|
||||
static struct lab_data_buffer *
|
||||
render_cairo_surface(cairo_surface_t *surface, int width, int height,
|
||||
int padding, double scale)
|
||||
{
|
||||
assert(surface);
|
||||
int src_w = cairo_image_surface_get_width(surface);
|
||||
int src_h = cairo_image_surface_get_height(surface);
|
||||
|
||||
struct lab_data_buffer *buffer =
|
||||
buffer_create_cairo(width, height, scale);
|
||||
cairo_t *cairo = cairo_create(buffer->surface);
|
||||
|
||||
struct wlr_box container = {
|
||||
.x = padding,
|
||||
.y = padding,
|
||||
.width = width - 2 * padding,
|
||||
.height = height - 2 * padding,
|
||||
};
|
||||
|
||||
struct wlr_box src_box = box_fit_within(src_w, src_h, &container);
|
||||
double scene_scale = MIN(1.0, (double_t)container.width / (double)src_w);
|
||||
cairo_translate(cairo, src_box.x, src_box.y);
|
||||
cairo_scale(cairo, scene_scale, scene_scale);
|
||||
cairo_set_source_surface(cairo, surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cairo), CAIRO_FILTER_GOOD);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(cairo);
|
||||
|
||||
cairo_destroy(cairo);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
struct lab_data_buffer *
|
||||
lab_img_render(struct lab_img *img, int width, int height, int padding,
|
||||
double scale)
|
||||
{
|
||||
struct lab_data_buffer *buffer = NULL;
|
||||
|
||||
/* Render the image into the buffer for the given size */
|
||||
switch (img->cache->type) {
|
||||
case LAB_IMG_PNG:
|
||||
case LAB_IMG_XBM:
|
||||
case LAB_IMG_XPM:
|
||||
buffer = render_cairo_surface(img->cache->buffer->surface,
|
||||
width, height, padding, scale);
|
||||
break;
|
||||
#if HAVE_RSVG
|
||||
case LAB_IMG_SVG:
|
||||
buffer = img_svg_render(img->cache->svg, width, height,
|
||||
padding, scale);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Apply modifiers to the buffer (e.g. draw hover overlay) */
|
||||
cairo_t *cairo = cairo_create(buffer->surface);
|
||||
lab_img_modifier_func_t *modifier;
|
||||
wl_array_for_each(modifier, &img->modifiers) {
|
||||
cairo_save(cairo);
|
||||
(*modifier)(img->theme, cairo, width, height);
|
||||
cairo_restore(cairo);
|
||||
}
|
||||
|
||||
cairo_surface_flush(buffer->surface);
|
||||
cairo_destroy(cairo);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void
|
||||
lab_img_destroy(struct lab_img *img)
|
||||
{
|
||||
if (!img) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct lab_img_cache *cache = img->cache;
|
||||
cache->refcount--;
|
||||
|
||||
if (cache->refcount == 0) {
|
||||
if (cache->buffer) {
|
||||
wlr_buffer_drop(&cache->buffer->base);
|
||||
}
|
||||
#if HAVE_RSVG
|
||||
if (cache->svg) {
|
||||
g_object_unref(cache->svg);
|
||||
}
|
||||
#endif
|
||||
free(cache);
|
||||
}
|
||||
|
||||
wl_array_release(&img->modifiers);
|
||||
free(img);
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
labwc_sources += files(
|
||||
'img.c',
|
||||
'img-png.c',
|
||||
'img-xbm.c',
|
||||
'img-xpm.c'
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "common/box.h"
|
||||
#include "common/list.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/scaled-img-buffer.h"
|
||||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
#include "ssd-internal.h"
|
||||
|
|
@ -80,35 +81,10 @@ add_scene_buffer(struct wl_list *list, enum ssd_part_type type,
|
|||
return part;
|
||||
}
|
||||
|
||||
static struct wlr_box
|
||||
get_scale_box(struct lab_data_buffer *buffer, int container_width,
|
||||
int container_height)
|
||||
{
|
||||
return box_fit_within(buffer->logical_width, buffer->logical_height,
|
||||
container_width, container_height);
|
||||
}
|
||||
|
||||
void
|
||||
update_window_icon_buffer(struct wlr_scene_node *button_node,
|
||||
struct lab_data_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->window_button_height);
|
||||
|
||||
wlr_scene_buffer_set_buffer(scene_buffer, &buffer->base);
|
||||
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 lab_data_buffer *buffers[LAB_BS_ALL + 1],
|
||||
struct lab_img *imgs[LAB_BS_ALL + 1],
|
||||
int x, int y, struct view *view)
|
||||
{
|
||||
struct ssd_part *button_root = add_scene_part(part_list, type);
|
||||
|
|
@ -125,18 +101,15 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type,
|
|||
/* Icons */
|
||||
struct wlr_scene_node *nodes[LAB_BS_ALL + 1] = {0};
|
||||
for (uint8_t state_set = 0; state_set <= LAB_BS_ALL; state_set++) {
|
||||
if (!buffers[state_set]) {
|
||||
if (!imgs[state_set]) {
|
||||
continue;
|
||||
}
|
||||
struct lab_data_buffer *icon_buffer = buffers[state_set];
|
||||
struct wlr_box icon_geo = get_scale_box(icon_buffer,
|
||||
rc.theme->window_button_width, rc.theme->window_button_height);
|
||||
struct ssd_part *icon_part = add_scene_buffer(part_list, type,
|
||||
parent, &icon_buffer->base, icon_geo.x, icon_geo.y);
|
||||
/* Make sure big icons are scaled down if necessary */
|
||||
wlr_scene_buffer_set_dest_size(
|
||||
wlr_scene_buffer_from_node(icon_part->node),
|
||||
icon_geo.width, icon_geo.height);
|
||||
struct ssd_part *icon_part = add_scene_part(part_list, type);
|
||||
struct scaled_img_buffer *img_buffer = scaled_img_buffer_create(
|
||||
parent, imgs[state_set], rc.theme->window_button_width,
|
||||
rc.theme->window_button_height, /* padding */ 0);
|
||||
assert(img_buffer);
|
||||
icon_part->node = &img_buffer->scene_buffer->node;
|
||||
wlr_scene_node_set_enabled(icon_part->node, false);
|
||||
nodes[state_set] = icon_part->node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@
|
|||
#include "config.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/scaled-font-buffer.h"
|
||||
#include "common/scaled-img-buffer.h"
|
||||
#include "common/scene-helpers.h"
|
||||
#include "common/string-helpers.h"
|
||||
#if HAVE_LIBSFDO
|
||||
#include "desktop-entry.h"
|
||||
#endif
|
||||
#include "img/img.h"
|
||||
#include "labwc.h"
|
||||
#include "node.h"
|
||||
#include "ssd-internal.h"
|
||||
|
|
@ -73,20 +75,20 @@ ssd_titlebar_create(struct ssd *ssd)
|
|||
int y = (theme->titlebar_height - theme->window_button_height) / 2;
|
||||
|
||||
wl_list_for_each(b, &rc.title_buttons_left, link) {
|
||||
struct lab_data_buffer **buffers =
|
||||
theme->window[active].buttons[b->type];
|
||||
struct lab_img **imgs =
|
||||
theme->window[active].button_imgs[b->type];
|
||||
add_scene_button(&subtree->parts, b->type, parent,
|
||||
buffers, x, y, view);
|
||||
imgs, x, y, view);
|
||||
x += theme->window_button_width + theme->window_button_spacing;
|
||||
}
|
||||
|
||||
x = width - theme->window_titlebar_padding_width + theme->window_button_spacing;
|
||||
wl_list_for_each_reverse(b, &rc.title_buttons_right, link) {
|
||||
x -= theme->window_button_width + theme->window_button_spacing;
|
||||
struct lab_data_buffer **buffers =
|
||||
theme->window[active].buttons[b->type];
|
||||
struct lab_img **imgs =
|
||||
theme->window[active].button_imgs[b->type];
|
||||
add_scene_button(&subtree->parts, b->type, parent,
|
||||
buffers, x, y, view);
|
||||
imgs, x, y, view);
|
||||
}
|
||||
} FOR_EACH_END
|
||||
|
||||
|
|
@ -343,6 +345,9 @@ ssd_titlebar_destroy(struct ssd *ssd)
|
|||
if (ssd->state.app_id) {
|
||||
zfree(ssd->state.app_id);
|
||||
}
|
||||
if (ssd->state.icon_img) {
|
||||
lab_img_destroy(ssd->state.icon_img);
|
||||
}
|
||||
|
||||
wlr_scene_node_destroy(&ssd->titlebar.tree->node);
|
||||
ssd->titlebar.tree = NULL;
|
||||
|
|
@ -593,9 +598,9 @@ ssd_update_window_icon(struct ssd *ssd)
|
|||
* was considered, but these settings have distinct purposes
|
||||
* already and are zero by default.
|
||||
*/
|
||||
int hpad = theme->window_button_width / 10;
|
||||
int icon_size = MIN(theme->window_button_width - 2 * hpad,
|
||||
theme->window_button_height);
|
||||
int icon_padding = theme->window_button_width / 10;
|
||||
int icon_size = MIN(theme->window_button_width - 2 * icon_padding,
|
||||
theme->window_button_height - 2 * icon_padding);
|
||||
|
||||
/*
|
||||
* Load/render icons at the max scale of any usable output (at
|
||||
|
|
@ -607,9 +612,9 @@ ssd_update_window_icon(struct ssd *ssd)
|
|||
*/
|
||||
float icon_scale = output_max_scale(ssd->view->server);
|
||||
|
||||
struct lab_data_buffer *icon_buffer = desktop_entry_icon_lookup(
|
||||
struct lab_img *icon_img = desktop_entry_icon_lookup(
|
||||
ssd->view->server, app_id, icon_size, icon_scale);
|
||||
if (!icon_buffer) {
|
||||
if (!icon_img) {
|
||||
wlr_log(WLR_DEBUG, "icon could not be loaded for %s", app_id);
|
||||
return;
|
||||
}
|
||||
|
|
@ -625,14 +630,22 @@ ssd_update_window_icon(struct ssd *ssd)
|
|||
/* Replace all the buffers in the button with the window icon */
|
||||
struct ssd_button *button = node_ssd_button_from_node(part->node);
|
||||
for (uint8_t state_set = 0; state_set <= LAB_BS_ALL; state_set++) {
|
||||
if (button->nodes[state_set]) {
|
||||
update_window_icon_buffer(button->nodes[state_set],
|
||||
icon_buffer);
|
||||
struct wlr_scene_node *node = button->nodes[state_set];
|
||||
if (node) {
|
||||
struct scaled_img_buffer *img_buffer =
|
||||
scaled_img_buffer_from_node(node);
|
||||
scaled_img_buffer_update(img_buffer, icon_img,
|
||||
theme->window_button_width,
|
||||
theme->window_button_height,
|
||||
icon_padding);
|
||||
}
|
||||
}
|
||||
} FOR_EACH_END
|
||||
|
||||
wlr_buffer_drop(&icon_buffer->base);
|
||||
if (ssd->state.icon_img) {
|
||||
lab_img_destroy(ssd->state.icon_img);
|
||||
}
|
||||
ssd->state.icon_img = icon_img;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
227
src/theme.c
227
src/theme.c
|
|
@ -29,14 +29,8 @@
|
|||
#include "common/parse-double.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "config/rcxml.h"
|
||||
#include "img/img-png.h"
|
||||
#include "img/img.h"
|
||||
#include "labwc.h"
|
||||
|
||||
#if HAVE_RSVG
|
||||
#include "img/img-svg.h"
|
||||
#endif
|
||||
|
||||
#include "img/img-xbm.h"
|
||||
#include "theme.h"
|
||||
#include "buffer.h"
|
||||
#include "ssd.h"
|
||||
|
|
@ -80,134 +74,63 @@ zdrop(struct lab_data_buffer **buffer)
|
|||
}
|
||||
}
|
||||
|
||||
struct icon_drawing_context {
|
||||
struct lab_data_buffer *buffer;
|
||||
cairo_t *cairo;
|
||||
};
|
||||
|
||||
static struct icon_drawing_context
|
||||
copy_icon_buffer(struct theme *theme, struct lab_data_buffer *icon_buffer)
|
||||
{
|
||||
assert(icon_buffer);
|
||||
|
||||
cairo_surface_t *surface = icon_buffer->surface;
|
||||
int icon_width = cairo_image_surface_get_width(surface);
|
||||
int icon_height = cairo_image_surface_get_height(surface);
|
||||
|
||||
int width = theme->window_button_width;
|
||||
int height = theme->window_button_height;
|
||||
|
||||
/*
|
||||
* Proportionately increase size of hover_buffer if the non-hover
|
||||
* 'donor' buffer is larger than the allocated space. It will get
|
||||
* scaled down again by wlroots when rendered and as required by the
|
||||
* current output scale.
|
||||
*
|
||||
* This ensures that icons > width or > height keep their aspect ratio
|
||||
* and are rendered the same as without the hover overlay.
|
||||
*/
|
||||
double scale = (width && height) ?
|
||||
MAX((double)icon_width / width, (double)icon_height / height) : 1.0;
|
||||
if (scale < 1.0) {
|
||||
scale = 1.0;
|
||||
}
|
||||
int buffer_width = (double)width * scale;
|
||||
int buffer_height = (double)height * scale;
|
||||
struct lab_data_buffer *buffer = buffer_create_cairo(
|
||||
buffer_width, buffer_height, 1.0);
|
||||
cairo_t *cairo = cairo_create(buffer->surface);
|
||||
|
||||
cairo_set_source_surface(cairo, surface,
|
||||
(buffer_width - icon_width) / 2, (buffer_height - icon_height) / 2);
|
||||
cairo_paint(cairo);
|
||||
|
||||
/*
|
||||
* Scale cairo context so that we can draw hover overlay or rounded
|
||||
* corner on this buffer in the scene coordinates.
|
||||
*/
|
||||
cairo_scale(cairo, scale, scale);
|
||||
|
||||
return (struct icon_drawing_context){
|
||||
.buffer = buffer,
|
||||
.cairo = cairo,
|
||||
};
|
||||
}
|
||||
|
||||
/* Draw rounded-rectangular hover overlay on the button buffer */
|
||||
static void
|
||||
create_hover_fallback(struct theme *theme,
|
||||
struct lab_data_buffer **hover_buffer,
|
||||
struct lab_data_buffer *icon_buffer)
|
||||
draw_hover_overlay_on_button(struct theme *theme, cairo_t *cairo, int w, int h)
|
||||
{
|
||||
assert(icon_buffer);
|
||||
assert(!*hover_buffer);
|
||||
|
||||
int width = theme->window_button_width;
|
||||
int height = theme->window_button_height;
|
||||
|
||||
struct icon_drawing_context ctx = copy_icon_buffer(theme, icon_buffer);
|
||||
*hover_buffer = ctx.buffer;
|
||||
cairo_t *cairo = ctx.cairo;
|
||||
|
||||
/* Overlay (pre-multiplied alpha) */
|
||||
float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f};
|
||||
set_cairo_color(cairo, overlay_color);
|
||||
int radius = theme->window_button_hover_bg_corner_radius;
|
||||
int r = theme->window_button_hover_bg_corner_radius;
|
||||
|
||||
cairo_new_sub_path(cairo);
|
||||
cairo_arc(cairo, radius, radius, radius, 180 * deg, 270 * deg);
|
||||
cairo_line_to(cairo, width - radius, 0);
|
||||
cairo_arc(cairo, width - radius, radius, radius, -90 * deg, 0 * deg);
|
||||
cairo_line_to(cairo, width, height - radius);
|
||||
cairo_arc(cairo, width - radius, height - radius, radius, 0 * deg, 90 * deg);
|
||||
cairo_line_to(cairo, radius, height);
|
||||
cairo_arc(cairo, radius, height - radius, radius, 90 * deg, 180 * deg);
|
||||
cairo_arc(cairo, r, r, r, 180 * deg, 270 * deg);
|
||||
cairo_line_to(cairo, w - r, 0);
|
||||
cairo_arc(cairo, w - r, r, r, -90 * deg, 0 * deg);
|
||||
cairo_line_to(cairo, w, h - r);
|
||||
cairo_arc(cairo, w - r, h - r, r, 0 * deg, 90 * deg);
|
||||
cairo_line_to(cairo, r, h);
|
||||
cairo_arc(cairo, r, h - r, r, 90 * deg, 180 * deg);
|
||||
cairo_close_path(cairo);
|
||||
cairo_fill(cairo);
|
||||
|
||||
cairo_surface_flush(cairo_get_target(cairo));
|
||||
cairo_destroy(cairo);
|
||||
}
|
||||
|
||||
/* Round the buffer for the leftmost button in the titlebar */
|
||||
static void
|
||||
create_rounded_buffer(struct theme *theme, enum corner corner,
|
||||
struct lab_data_buffer **rounded_buffer,
|
||||
struct lab_data_buffer *icon_buffer)
|
||||
round_left_corner_button(struct theme *theme, cairo_t *cairo, int w, int h)
|
||||
{
|
||||
struct icon_drawing_context ctx = copy_icon_buffer(theme, icon_buffer);
|
||||
*rounded_buffer = ctx.buffer;
|
||||
cairo_t *cairo = ctx.cairo;
|
||||
|
||||
int width = theme->window_button_width;
|
||||
int height = theme->window_button_height;
|
||||
|
||||
/*
|
||||
* Round the corner button by cropping the region within the window
|
||||
* border. See the picture in #2189 for reference.
|
||||
* Position of the topleft corner of the titlebar relative to the
|
||||
* leftmost button
|
||||
*/
|
||||
int margin_x = theme->window_titlebar_padding_width;
|
||||
int margin_y = (theme->titlebar_height - theme->window_button_height) / 2;
|
||||
float white[4] = {1, 1, 1, 1};
|
||||
struct rounded_corner_ctx rounded_ctx = {
|
||||
.box = &(struct wlr_box){
|
||||
.width = margin_x + width,
|
||||
.height = margin_y + height,
|
||||
},
|
||||
.radius = rc.corner_radius,
|
||||
.line_width = theme->border_width,
|
||||
.fill_color = white,
|
||||
.border_color = white,
|
||||
.corner = corner,
|
||||
};
|
||||
struct lab_data_buffer *mask_buffer = rounded_rect(&rounded_ctx);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_IN);
|
||||
cairo_set_source_surface(cairo, mask_buffer->surface,
|
||||
(corner == LAB_CORNER_TOP_LEFT) ? -margin_x : 0,
|
||||
-margin_y);
|
||||
cairo_paint(cairo);
|
||||
double x = -theme->window_titlebar_padding_width;
|
||||
double y = -(theme->titlebar_height - theme->window_button_height) / 2;
|
||||
|
||||
cairo_surface_flush(cairo_get_target(cairo));
|
||||
cairo_destroy(cairo);
|
||||
wlr_buffer_drop(&mask_buffer->base);
|
||||
double r = rc.corner_radius - (double)theme->border_width / 2.0;
|
||||
|
||||
cairo_new_sub_path(cairo);
|
||||
cairo_arc(cairo, x + r, y + r, r, deg * 180, deg * 270);
|
||||
cairo_line_to(cairo, w, y);
|
||||
cairo_line_to(cairo, w, h);
|
||||
cairo_line_to(cairo, x, h);
|
||||
cairo_close_path(cairo);
|
||||
|
||||
cairo_set_source_rgba(cairo, 1, 1, 1, 1);
|
||||
cairo_set_operator(cairo, CAIRO_OPERATOR_DEST_IN);
|
||||
cairo_fill(cairo);
|
||||
}
|
||||
|
||||
/* Round the buffer for the rightmost button in the titlebar */
|
||||
static void
|
||||
round_right_corner_button(struct theme *theme, cairo_t *cairo, int w, int h)
|
||||
{
|
||||
/*
|
||||
* Horizontally flip the cairo context so we can reuse
|
||||
* round_left_corner_button() for rounding the rightmost button.
|
||||
*/
|
||||
cairo_scale(cairo, -1, 1);
|
||||
cairo_translate(cairo, -w, 0);
|
||||
round_left_corner_button(theme, cairo, w, h);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -243,45 +166,42 @@ get_button_filename(char *buf, size_t len, const char *name, const char *postfix
|
|||
static void
|
||||
load_button(struct theme *theme, struct button *b, int active)
|
||||
{
|
||||
struct lab_data_buffer *(*buttons)[LAB_BS_ALL + 1] =
|
||||
theme->window[active].buttons;
|
||||
struct lab_data_buffer **buffer = &buttons[b->type][b->state_set];
|
||||
struct lab_img *(*button_imgs)[LAB_BS_ALL + 1] =
|
||||
theme->window[active].button_imgs;
|
||||
struct lab_img **img = &button_imgs[b->type][b->state_set];
|
||||
float *rgba = theme->window[active].button_colors[b->type];
|
||||
char filename[4096];
|
||||
|
||||
zdrop(buffer);
|
||||
|
||||
int size = theme->window_button_height;
|
||||
float scale = 1; /* TODO: account for output scale */
|
||||
assert(!*img);
|
||||
|
||||
/* PNG */
|
||||
get_button_filename(filename, sizeof(filename), b->name,
|
||||
active ? "-active.png" : "-inactive.png");
|
||||
img_png_load(filename, buffer, size, scale);
|
||||
*img = lab_img_load(LAB_IMG_PNG, filename, rgba);
|
||||
|
||||
#if HAVE_RSVG
|
||||
/* SVG */
|
||||
if (!*buffer) {
|
||||
if (!*img) {
|
||||
get_button_filename(filename, sizeof(filename), b->name,
|
||||
active ? "-active.svg" : "-inactive.svg");
|
||||
img_svg_load(filename, buffer, size, scale);
|
||||
*img = lab_img_load(LAB_IMG_SVG, filename, rgba);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XBM */
|
||||
if (!*buffer) {
|
||||
if (!*img) {
|
||||
get_button_filename(filename, sizeof(filename), b->name, ".xbm");
|
||||
img_xbm_load(filename, buffer, rgba);
|
||||
*img = lab_img_load(LAB_IMG_XBM, filename, rgba);
|
||||
}
|
||||
|
||||
/*
|
||||
* XBM (alternative name)
|
||||
* For example max_hover_toggled instead of max_toggled_hover
|
||||
*/
|
||||
if (!*buffer && b->alt_name) {
|
||||
if (!*img && b->alt_name) {
|
||||
get_button_filename(filename, sizeof(filename),
|
||||
b->alt_name, ".xbm");
|
||||
img_xbm_load(filename, buffer, rgba);
|
||||
*img = lab_img_load(LAB_IMG_XBM, filename, rgba);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -290,32 +210,36 @@ load_button(struct theme *theme, struct button *b, int active)
|
|||
* Applicable to basic buttons such as max, max_toggled and iconify.
|
||||
* There are no bitmap fallbacks for *_hover icons.
|
||||
*/
|
||||
if (!*buffer && b->fallback_button) {
|
||||
img_xbm_from_bitmap(b->fallback_button, buffer, rgba);
|
||||
if (!*img && b->fallback_button) {
|
||||
*img = lab_img_load_from_bitmap(b->fallback_button, rgba);
|
||||
}
|
||||
|
||||
/*
|
||||
* If hover-icons do not exist, add fallbacks by copying the non-hover
|
||||
* variant and then adding an overlay.
|
||||
*/
|
||||
if (!*buffer && (b->state_set & LAB_BS_HOVERD)) {
|
||||
uint8_t non_hover_state_set = b->state_set & ~LAB_BS_HOVERD;
|
||||
create_hover_fallback(theme, buffer,
|
||||
buttons[b->type][non_hover_state_set]);
|
||||
if (!*img && (b->state_set & LAB_BS_HOVERD)) {
|
||||
struct lab_img *non_hover_img =
|
||||
button_imgs[b->type][b->state_set & ~LAB_BS_HOVERD];
|
||||
*img = lab_img_copy(non_hover_img);
|
||||
lab_img_add_modifier(*img,
|
||||
draw_hover_overlay_on_button, theme);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the loaded button is at the corner of the titlebar, also create
|
||||
* rounded variants.
|
||||
*/
|
||||
uint8_t rounded_state_set = b->state_set | LAB_BS_ROUNDED;
|
||||
struct lab_img **rounded_img =
|
||||
&button_imgs[b->type][b->state_set | LAB_BS_ROUNDED];
|
||||
|
||||
struct title_button *leftmost_button;
|
||||
wl_list_for_each(leftmost_button,
|
||||
&rc.title_buttons_left, link) {
|
||||
if (leftmost_button->type == b->type) {
|
||||
create_rounded_buffer(theme, LAB_CORNER_TOP_LEFT,
|
||||
&buttons[b->type][rounded_state_set], *buffer);
|
||||
*rounded_img = lab_img_copy(*img);
|
||||
lab_img_add_modifier(*rounded_img,
|
||||
round_left_corner_button, theme);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -323,8 +247,9 @@ load_button(struct theme *theme, struct button *b, int active)
|
|||
wl_list_for_each_reverse(rightmost_button,
|
||||
&rc.title_buttons_right, link) {
|
||||
if (rightmost_button->type == b->type) {
|
||||
create_rounded_buffer(theme, LAB_CORNER_TOP_RIGHT,
|
||||
&buttons[b->type][rounded_state_set], *buffer);
|
||||
*rounded_img = lab_img_copy(*img);
|
||||
lab_img_add_modifier(*rounded_img,
|
||||
round_right_corner_button, theme);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -1588,6 +1513,12 @@ theme_init(struct theme *theme, struct server *server, const char *theme_name)
|
|||
create_shadows(theme);
|
||||
}
|
||||
|
||||
static void destroy_img(struct lab_img **img)
|
||||
{
|
||||
lab_img_destroy(*img);
|
||||
*img = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
theme_finish(struct theme *theme)
|
||||
{
|
||||
|
|
@ -1595,10 +1526,10 @@ theme_finish(struct theme *theme)
|
|||
type <= LAB_SSD_BUTTON_LAST; type++) {
|
||||
for (uint8_t state_set = 0; state_set <= LAB_BS_ALL;
|
||||
state_set++) {
|
||||
zdrop(&theme->window[THEME_INACTIVE]
|
||||
.buttons[type][state_set]);
|
||||
zdrop(&theme->window[THEME_ACTIVE]
|
||||
.buttons[type][state_set]);
|
||||
destroy_img(&theme->window[THEME_INACTIVE]
|
||||
.button_imgs[type][state_set]);
|
||||
destroy_img(&theme->window[THEME_ACTIVE]
|
||||
.button_imgs[type][state_set]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue