From 30f91d7c8769a68e4bf11ed4ff0177bcf1bc9f03 Mon Sep 17 00:00:00 2001 From: Federico Angelilli Date: Wed, 20 Nov 2024 01:08:30 +0100 Subject: Add gradients --- src/block.c | 3 +++ src/block.h | 7 ++--- src/config.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- src/config.h | 1 + src/layout.c | 49 +++++++++++++++++++++-------------- src/util.c | 27 +++++++++++++++++++ src/util.h | 11 ++++++++ 7 files changed, 150 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/block.c b/src/block.c index 44a6241..25f24cd 100644 --- a/src/block.c +++ b/src/block.c @@ -53,6 +53,9 @@ void block_free(block_t *block) if (block->scheme->clean_fn != NULL) block->scheme->clean_fn(block); + gradient_free(&block->bg_color); + gradient_free(&block->line_color); + free(block->label); free(block); } diff --git a/src/block.h b/src/block.h index c12cea6..e461e0d 100644 --- a/src/block.h +++ b/src/block.h @@ -58,11 +58,8 @@ struct block { struct timespec update_interval; struct timespec update_last; block_update_t update_fn; - // TODO - //gradient_t bg_color; - //gradient_t line_color; - color_t color; - color_t line_color; + gradient_t bg_color; + gradient_t line_color; unsigned int line_width; unsigned int x_padding, y_padding; unsigned int min_width, max_width; diff --git a/src/config.c b/src/config.c index 32666a8..e13d9b9 100644 --- a/src/config.c +++ b/src/config.c @@ -31,15 +31,15 @@ static const config_entry_t bar_entries[] = { }; static const config_entry_t block_entries[] = { - { "hidden", CONFIG_BOOL, NULL, offsetof(block_t, hidden) }, - { "color", CONFIG_COLOR, NULL, offsetof(block_t, color) }, - { "line-color", CONFIG_COLOR, NULL, offsetof(block_t, line_color) }, - { "line-width", CONFIG_UINT, NULL, offsetof(block_t, line_width) }, - { "x-padding", CONFIG_UINT, NULL, offsetof(block_t, x_padding) }, - { "y-padding", CONFIG_UINT, NULL, offsetof(block_t, y_padding) }, - { "min-width", CONFIG_UINT, NULL, offsetof(block_t, min_width) }, - { "max-width", CONFIG_UINT, NULL, offsetof(block_t, max_width) }, - { "interval", CONFIG_TIME, NULL, offsetof(block_t, update_interval) }, + { "hidden", CONFIG_BOOL, NULL, offsetof(block_t, hidden) }, + { "color", CONFIG_GRADIENT, NULL, offsetof(block_t, bg_color) }, + { "line-color", CONFIG_GRADIENT, NULL, offsetof(block_t, line_color) }, + { "line-width", CONFIG_UINT, NULL, offsetof(block_t, line_width) }, + { "x-padding", CONFIG_UINT, NULL, offsetof(block_t, x_padding) }, + { "y-padding", CONFIG_UINT, NULL, offsetof(block_t, y_padding) }, + { "min-width", CONFIG_UINT, NULL, offsetof(block_t, min_width) }, + { "max-width", CONFIG_UINT, NULL, offsetof(block_t, max_width) }, + { "interval", CONFIG_TIME, NULL, offsetof(block_t, update_interval) }, { 0 }, }; @@ -173,6 +173,64 @@ static bool config_read_color(const char *value, color_t *result) return false; } +static bool config_read_gradient(const char *value, gradient_t *result) +{ + size_t count = 0; + for (size_t i = 0; value[i] != '\0'; ++i) + count += value[i] == ','; + + color_t *colors = calloc(count + 1, sizeof(color_t)); + size_t n = 0; + + char *state, *copy = strcopy(value); + for (char *string = copy; ; string = NULL) { + char *token = strtok_r(string, ",", &state); + + if (token == NULL) + break; + + while (isspace(*token)) token++; + char *end = token + strlen(token); + while (isspace(*end)) end--; + *end = '\0'; + + if (!config_read_color(token, &colors[n++])) { + free(copy); + free(colors); + return false; + } + } + + free(copy); + result->colors = colors; + result->length = n; + + if (n == 0) { + free(colors); + log_debug("Expected at least one color"); + return false; + } + + if (n == 1) { + result->cached = cairo_pattern_create_rgba(colors->r, colors->g, colors->b, colors->a); + return true; + } + + result->cached = cairo_pattern_create_linear(0, 0, 1, 0); + + for (size_t i = 0; i < n; i++) { + double offset = i * 1.0 / (n - 1); + cairo_pattern_add_color_stop_rgba(result->cached, + offset, + colors[i].r, + colors[i].g, + colors[i].b, + colors[i].a); + } + + return true; +} + static bool config_read_enum(const char *value, config_enum_t *data, int *result) { for (int i = 0; data[i].label != NULL; i++) { @@ -306,6 +364,15 @@ static config_status_t config_read_entry(const config_entry_t *entries, void *re } break; + case CONFIG_GRADIENT: + if (config_read_gradient(value, (gradient_t *)result)) { + char *gradient = gradient_to_string((gradient_t *)result); + log_debug("Set '%s.%s' to '%s'", section, key, gradient); + free(gradient); + return CONFIG_SUCCESS; + } + break; + case CONFIG_ENUM: if (config_read_enum(value, (config_enum_t *)entries[i].data, (int *)result)) { log_debug("Set '%s.%s' to '%d'", section, key, *(int *)result); diff --git a/src/config.h b/src/config.h index e65dbcf..3ed689c 100644 --- a/src/config.h +++ b/src/config.h @@ -15,6 +15,7 @@ typedef enum { CONFIG_DOUBLE, CONFIG_BOOL, CONFIG_COLOR, + CONFIG_GRADIENT, CONFIG_ENUM, CONFIG_TIME, CONFIG_LIST, diff --git a/src/layout.c b/src/layout.c index db707c5..3c4f13b 100644 --- a/src/layout.c +++ b/src/layout.c @@ -93,30 +93,41 @@ void layout_init(layout_t *layout, block_t *block, layout_info_t info) void layout_render(layout_t *layout, cairo_t *cr) { - double degree = M_PI / 180.0; + const double degree = M_PI / 180.0; int radius = layout->height / 2 - layout->y_padding; int line_radius = radius - layout->block->line_width / 2; - // Render background - color_t color = layout->block->color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); + // Update gradients + cairo_matrix_t matrix; + cairo_matrix_init_scale(&matrix, 1.0 / layout->width, 1.0); + cairo_matrix_translate(&matrix, -layout->x, 0.0); - cairo_new_sub_path(cr); - cairo_arc(cr, layout->x + layout->x_padding + radius, layout->y + layout->y_padding + radius, radius, 90 * degree, 270 * degree); - cairo_arc(cr, layout->x + layout->width - layout->x_padding - radius, layout->y + layout->y_padding + radius, radius, 270 * degree, 450 * degree); - cairo_close_path(cr); - cairo_fill(cr); + // Render background + cairo_pattern_t *pattern = layout->block->bg_color.cached; + if (pattern != NULL) { + cairo_new_sub_path(cr); + cairo_arc(cr, layout->x + layout->x_padding + radius, layout->y + layout->y_padding + radius, radius, 90 * degree, 270 * degree); + cairo_arc(cr, layout->x + layout->width - layout->x_padding - radius, layout->y + layout->y_padding + radius, radius, 270 * degree, 450 * degree); + cairo_close_path(cr); + + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + cairo_fill(cr); + } // Render border - color = layout->block->line_color; - cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); - cairo_set_line_width(cr, layout->block->line_width); - - cairo_new_sub_path(cr); - cairo_arc(cr, layout->x + layout->x_padding + radius, layout->y + layout->y_padding + radius, line_radius, 90 * degree, 270 * degree); - cairo_arc(cr, layout->x + layout->width - layout->x_padding - radius, layout->y + layout->y_padding + radius, line_radius, 270 * degree, 450 * degree); - cairo_close_path(cr); - cairo_stroke(cr); + pattern = layout->block->line_color.cached; + if (pattern != NULL) { + cairo_new_sub_path(cr); + cairo_arc(cr, layout->x + layout->x_padding + radius, layout->y + layout->y_padding + radius, line_radius, 90 * degree, 270 * degree); + cairo_arc(cr, layout->x + layout->width - layout->x_padding - radius, layout->y + layout->y_padding + radius, line_radius, 270 * degree, 450 * degree); + cairo_close_path(cr); + + cairo_pattern_set_matrix(pattern, &matrix); + cairo_set_source(cr, pattern); + cairo_set_line_width(cr, layout->block->line_width); + cairo_stroke(cr); + } switch (layout->block->type) { case BLOCK_TEXT: { @@ -126,7 +137,7 @@ void layout_render(layout_t *layout, cairo_t *cr) int text_x = layout->x + (layout->width - layout->text_width) / 2; int text_y = layout->y + (layout->height - layout->text_height) / 2; - color = text->text_color; + color_t color = text->text_color; cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a); cairo_move_to(cr, text_x, text_y); diff --git a/src/util.c b/src/util.c index b077484..2900a2f 100644 --- a/src/util.c +++ b/src/util.c @@ -29,6 +29,33 @@ void color_print(FILE *stream, color_t *color) fprintf(stream, "#%02x%02x%02x%02x", r, g, b, a); } +char *gradient_to_string(gradient_t *gradient) +{ + char buffer[256]; + const size_t max = sizeof(buffer); + + int start = 0; + for (size_t i = 0; i < gradient->length; i++) { + unsigned int r = gradient->colors[i].r * 255, + g = gradient->colors[i].g * 255, + b = gradient->colors[i].b * 255, + a = gradient->colors[i].a * 255; + + start += snprintf(buffer + start, max - start, "#%02x%02x%02x%02x", r, g, b, a); + if (i != gradient->length - 1) + start += snprintf(buffer + start, max - start, ", "); + } + + return strslice(buffer, 0, start); +} + +void gradient_free(gradient_t *gradient) +{ + free(gradient->colors); + if (gradient->cached != NULL) + cairo_pattern_destroy(gradient->cached); +} + const struct timespec timespec_from_ms(long ms) { struct timespec ts = { diff --git a/src/util.h b/src/util.h index 8d60eb2..78dcad7 100644 --- a/src/util.h +++ b/src/util.h @@ -6,6 +6,7 @@ #include #include #include +#include #define unreachable() log_panic("The impossible happened"); @@ -15,6 +16,12 @@ typedef struct { double r, g, b, a; } color_t; +typedef struct { + color_t *colors; + size_t length; + cairo_pattern_t *cached; +} gradient_t; + static inline color_t color_rgba(int r, int g, int b, int a) { color_t color = { r / 255.0, g / 255.0, b / 255.0, a / 255.0 }; @@ -35,6 +42,10 @@ char *color_to_string(color_t *color); void color_print(FILE *stream, color_t *color); +char *gradient_to_string(gradient_t *gradient); + +void gradient_free(gradient_t *gradient); + const struct timespec timespec_from_ms(long ms); const long timespec_to_ms(struct timespec ts); -- cgit v1.2.3