diff --git a/include/common/graphic-helpers.h b/include/common/graphic-helpers.h index 7bdd1246..ccf17022 100644 --- a/include/common/graphic-helpers.h +++ b/include/common/graphic-helpers.h @@ -47,4 +47,14 @@ void set_cairo_color(cairo_t *cairo, float *color); /* Draws a border with a specified line width */ void draw_cairo_border(cairo_t *cairo, struct wlr_fbox fbox, double line_width); +struct lab_data_buffer; + +struct surface_context { + bool is_duplicate; + cairo_surface_t *surface; +}; + +struct surface_context get_cairo_surface_from_lab_data_buffer( + struct lab_data_buffer *buffer); + #endif /* LABWC_GRAPHIC_HELPERS_H */ diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h index 731f93fe..20f74d5a 100644 --- a/include/common/string-helpers.h +++ b/include/common/string-helpers.h @@ -2,6 +2,15 @@ #ifndef LABWC_STRING_HELPERS_H #define LABWC_STRING_HELPERS_H +/** + * trim_last_field() - Trim last field of string splitting on provided delim + * @buf: string to trim + * @delim: delimitator + * + * Example: With delim='_' and buf="foo_bar_baz" the return value is "foo_bar" + */ +void trim_last_field(char *buf, char delim); + /** * string_strip - strip white space left and right * Note: this function does a left skip, so the returning pointer cannot be diff --git a/src/buffer.c b/src/buffer.c index e96c4d88..0921acdd 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -129,6 +129,8 @@ buffer_create_wrap(void *pixel_data, uint32_t width, uint32_t height, { struct lab_data_buffer *buffer = znew(*buffer); wlr_buffer_init(&buffer->base, &data_buffer_impl, width, height); + buffer->unscaled_width = width; + buffer->unscaled_height = height; buffer->data = pixel_data; buffer->format = DRM_FORMAT_ARGB8888; buffer->stride = stride; diff --git a/src/common/graphic-helpers.c b/src/common/graphic-helpers.c index 9f8141bd..862decd9 100644 --- a/src/common/graphic-helpers.c +++ b/src/common/graphic-helpers.c @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include "buffer.h" #include "common/graphic-helpers.h" #include "common/mem.h" @@ -85,3 +87,32 @@ set_cairo_color(cairo_t *cairo, float *c) { cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]); } + +struct surface_context +get_cairo_surface_from_lab_data_buffer(struct lab_data_buffer *buffer) +{ + /* Handle CAIRO_FORMAT_ARGB32 buffers */ + if (buffer->cairo) { + return (struct surface_context){ + .is_duplicate = false, + .surface = cairo_get_target(buffer->cairo), + }; + } + + /* Handle DRM_FORMAT_ARGB8888 buffers */ + int w = buffer->unscaled_width; + int h = buffer->unscaled_height; + cairo_surface_t *surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + if (!surface) { + return (struct surface_context){0}; + } + unsigned char *data = cairo_image_surface_get_data(surface); + cairo_surface_flush(surface); + memcpy(data, buffer->data, h * buffer->stride); + cairo_surface_mark_dirty(surface); + return (struct surface_context){ + .is_duplicate = true, + .surface = surface, + }; +} diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c index 01ea09f4..ec2c86bd 100644 --- a/src/common/string-helpers.c +++ b/src/common/string-helpers.c @@ -6,6 +6,15 @@ #include "common/mem.h" #include "common/string-helpers.h" +void +trim_last_field(char *buf, char delim) +{ + char *p = strrchr(buf, delim); + if (p) { + *p = '\0'; + } +} + static void rtrim(char **s) { diff --git a/src/theme.c b/src/theme.c index ce71a4d4..e7e52ccc 100644 --- a/src/theme.c +++ b/src/theme.c @@ -7,6 +7,7 @@ #define _POSIX_C_SOURCE 200809L #include "config.h" +#include #include #include #include @@ -56,6 +57,61 @@ drop(struct lab_data_buffer **buffer) } } +static void +create_hover_fallback(struct theme *theme, struct lab_data_buffer **hover_buffer, + struct lab_data_buffer *icon_buffer) +{ + assert(icon_buffer); + assert(!*hover_buffer); + + struct surface_context icon = + get_cairo_surface_from_lab_data_buffer(icon_buffer); + int icon_width = cairo_image_surface_get_width(icon.surface); + int icon_height = cairo_image_surface_get_height(icon.surface); + + /* TODO: need to somehow respect rounded corners */ + int width = SSD_BUTTON_WIDTH; + int height = theme->title_height; + + if (width && 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 = MAX((double)icon_width / width, + (double)icon_height / height); + if (scale > 1.0f) { + width = (double)width * scale; + height = (double)height * scale; + } + } + + *hover_buffer = buffer_create_cairo(width, height, 1.0, true); + + cairo_t *cairo = (*hover_buffer)->cairo; + cairo_surface_t *surf = cairo_get_target(cairo); + + /* Background */ + cairo_set_source_surface(cairo, icon.surface, + (width - icon_width) / 2, (height - icon_height) / 2); + cairo_paint(cairo); + + /* Overlay (non-multiplied alpha) */ + set_cairo_color(cairo, (float[4]) { 0.5f, 0.5f, 0.5f, 0.3f}); + cairo_rectangle(cairo, 0, 0, width, height); + cairo_fill(cairo); + cairo_surface_flush(surf); + + if (icon.is_duplicate) { + cairo_surface_destroy(icon.surface); + } +} + /* * We use the following button filename schema: "BUTTON [TOGGLED] [STATE]" * with the words separted by underscore, and the following meaning: @@ -204,6 +260,39 @@ load_buttons(struct theme *theme) b->fallback_button, b->inactive.rgba); } } + + /* + * If hover-icons do not exist, add fallbacks by coping the non-hover + * variant (base) and then adding an overlay. + */ + for (size_t i = 0; i < ARRAY_SIZE(buttons); i++) { + struct button *hover_button = &buttons[i]; + + if (!strstr(hover_button->name, "_hover")) { + continue; + } + + /* If name=='foo_hover', basename='foo' */ + char basename[64] = {0}; + snprintf(basename, sizeof(basename), "%s", hover_button->name); + trim_last_field(basename, '_'); + for (size_t j = 0; j < ARRAY_SIZE(buttons); j++) { + struct button *base = &buttons[j]; + if (!strcmp(basename, base->name)) { + if (!*hover_button->active.buffer) { + create_hover_fallback(theme, + hover_button->active.buffer, + *base->active.buffer); + } + if (!*hover_button->inactive.buffer) { + create_hover_fallback(theme, + hover_button->inactive.buffer, + *base->inactive.buffer); + } + break; + } + } + } } static int