Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add color gradients #1370

Merged
merged 14 commits into from
Oct 31, 2024
3 changes: 3 additions & 0 deletions docs/dunst.5.pod
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,9 @@ The foreground color of the notification. See COLORS for possible values.
The highlight color of the notification. This color is used for coloring the
progress bar. See COLORS for possible values.

You can also set additional color values (as a comma-separated list)
to define a linear gradient spanning all the length of the progress bar.

=item C<format>

Equivalent to the C<format> setting.
Expand Down
52 changes: 48 additions & 4 deletions src/dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,25 @@ static void color_entry(const struct color c, GVariantDict *dict, const char *fi
}
}

static void gradient_entry(const struct gradient *grad, GVariantDict *dict, const char *field_name) {
if (GRADIENT_VALID(grad)) {
if (grad->length == 1) {
color_entry(grad->colors[0], dict, field_name);
return;
}

char **strv = g_malloc((grad->length + 1) * sizeof(char *));
for (int i = 0; i < grad->length; i++) {
char buf[10];
if (color_to_string(grad->colors[i], buf))
strv[i] = g_strdup(buf);
}
strv[grad->length] = NULL;

g_variant_dict_insert(dict, field_name, "^as", strv);
}
}

static void dbus_cb_dunst_RuleList(GDBusConnection *connection,
const gchar *sender,
GVariant *parameters,
Expand Down Expand Up @@ -543,7 +562,7 @@ static void dbus_cb_dunst_RuleList(GDBusConnection *connection,
enum_to_string(icon_position_enum_data, r->icon_position));
color_entry(r->fg, &dict, "fg");
color_entry(r->bg, &dict, "bg");
color_entry(r->highlight, &dict, "highlight");
gradient_entry(r->highlight, &dict, "highlight");
color_entry(r->fc, &dict, "fc");
if (r->format)
g_variant_dict_insert(&dict, "format", "s", r->format);
Expand Down Expand Up @@ -838,12 +857,37 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
g_variant_unref(dict_value);
}

if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING_ARRAY))) {
char **cols = (char **)g_variant_get_strv(dict_value, NULL);
size_t length = g_strv_length(cols);
struct gradient *grad = gradient_alloc(length);

for (int i = 0; i < length; i++) {
if (!string_parse_color(cols[i], &grad->colors[i])) {
g_free(grad);
goto end;
}
}


gradient_pattern(grad);

notification_keep_original(n);
if (!GRADIENT_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight;
n->colors.highlight = grad;

end:
g_variant_unref(dict_value);
} else if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
struct color c;
if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) {
struct gradient *grad = gradient_alloc(1);
grad->colors[0] = c;
gradient_pattern(grad);

notification_keep_original(n);
if (!COLOR_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight;
n->colors.highlight = c;
if (!GRADIENT_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight;
n->colors.highlight = grad;
}
g_variant_unref(dict_value);
}
Expand Down
71 changes: 69 additions & 2 deletions src/draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,69 @@ char *color_to_string(struct color c, char buf[10])
return buf;
}

struct gradient *gradient_alloc(size_t length)
{
if (length == 0)
return NULL;

struct gradient *grad = g_malloc(sizeof(struct gradient) + length * sizeof(struct color));

grad->length = length;
grad->pattern = NULL;

return grad;
}

void gradient_free(struct gradient *grad)
{
if (grad == NULL) return;

if (grad->pattern)
cairo_pattern_destroy(grad->pattern);

g_free(grad);
}

void gradient_pattern(struct gradient *grad)
{
if (grad->length == 1) {
grad->pattern = cairo_pattern_create_rgba(grad->colors[0].r,
grad->colors[0].g,
grad->colors[0].b,
grad->colors[0].a);
} else {
grad->pattern = cairo_pattern_create_linear(0, 0, 1, 0);
for (int i = 0; i < grad->length; i++) {
double offset = i / (double)(grad->length - 1);
cairo_pattern_add_color_stop_rgba(grad->pattern,
offset,
grad->colors[i].r,
grad->colors[i].g,
grad->colors[i].b,
grad->colors[i].a);
}
}
}

char *gradient_to_string(const struct gradient *grad)
{
if (!GRADIENT_VALID(grad)) return NULL;

char *buf = g_malloc(grad->length * 11);
color_to_string(grad->colors[0], buf);
char *ptr = buf + 9;

for (int i = 1; i < grad->length; i++) {
ptr += g_snprintf(ptr, 11, ", #%02x%02x%02x%02x",
(int)(grad->colors[i].r * 255),
(int)(grad->colors[i].g * 255),
(int)(grad->colors[i].b * 255),
(int)(grad->colors[i].a * 255));
}

return buf;
}

void draw_setup(void)
{
const struct output *out = output_create(settings.force_xwayland);
Expand Down Expand Up @@ -320,7 +383,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, struct notification

// Invalid colors should never reach this point!
assert(settings.frame_width == 0 || COLOR_VALID(COLOR(cl, frame)));
assert(!have_progress_bar(cl) || COLOR_VALID(COLOR(cl, highlight)));
assert(!have_progress_bar(cl) || COLOR(cl, highlight) != NULL);
assert(COLOR_VALID(COLOR(cl, fg)));
assert(COLOR_VALID(COLOR(cl, bg)));
return cl;
Expand Down Expand Up @@ -828,7 +891,11 @@ static void render_content(cairo_t *c, struct colored_layout *cl, int width, int
cairo_fill(c);

// top layer (fill)
cairo_set_source_rgba(c, COLOR(cl, highlight.r), COLOR(cl, highlight.g), COLOR(cl, highlight.b), COLOR(cl, highlight.a));
cairo_matrix_t matrix;
cairo_matrix_init_scale(&matrix, 1.0 / width, 1.0);
cairo_pattern_set_matrix(COLOR(cl, highlight->pattern), &matrix);
cairo_set_source(c, COLOR(cl, highlight->pattern));

draw_rounded_rect(c, x_bar_1, frame_y, progress_width_1, progress_height,
settings.progress_bar_corner_radius, scale, settings.progress_bar_corners);
cairo_fill(c);
Expand Down
17 changes: 17 additions & 0 deletions src/draw.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ struct color {
*/
char *color_to_string(struct color c, char buf[10]);

struct gradient {
cairo_pattern_t *pattern;
size_t length;
struct color colors[];
};

#define GRADIENT_VALID(g) ((g) != NULL && (g)->length != 0)

struct gradient *gradient_alloc(size_t length);

void gradient_free(struct gradient *grad);

void gradient_pattern(struct gradient *grad);

char *gradient_to_string(const struct gradient *grad);


void draw_setup(void);

void draw(void);
Expand Down
5 changes: 5 additions & 0 deletions src/dunst.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "draw.h"
#include "log.h"
#include "menu.h"
#include "rules.h"
#include "notification.h"
#include "option_parser.h"
#include "queues.h"
Expand Down Expand Up @@ -208,6 +209,8 @@ static void teardown(void)
draw_deinit();

g_strfreev(config_paths);

g_slist_free_full(rules, (GDestroyNotify)rule_free);
}

void reload(char **const configs)
Expand Down Expand Up @@ -321,6 +324,8 @@ int dunst_main(int argc, char *argv[])

teardown();

settings_free(&settings);

return 0;
}

Expand Down
20 changes: 12 additions & 8 deletions src/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ void notification_print(const struct notification *n)
char buf[10];
printf("\tfg: %s\n", STR_NN(color_to_string(n->colors.fg, buf)));
printf("\tbg: %s\n", STR_NN(color_to_string(n->colors.bg, buf)));
printf("\thighlight: %s\n", STR_NN(color_to_string(n->colors.highlight, buf)));
printf("\tframe: %s\n", STR_NN(color_to_string(n->colors.frame, buf)));

char *grad = gradient_to_string(n->colors.highlight);
printf("\thighlight: %s\n", STR_NN(grad));
g_free(grad);

printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
printf("\tformat: %s\n", STR_NN(n->format));
printf("\tprogress: %d\n", n->progress);
Expand Down Expand Up @@ -295,11 +299,7 @@ void notification_unref(struct notification *n)
return;

if (n->original) {
g_free(n->original->action_name);
g_free(n->original->set_category);
g_free(n->original->default_icon);
g_free(n->original->set_stack_tag);
g_free(n->original->new_icon);
rule_free(n->original);
g_free(n->original);
}

Expand Down Expand Up @@ -457,14 +457,16 @@ struct notification *notification_create(void)
struct color invalid = COLOR_UNINIT;
n->colors.fg = invalid;
n->colors.bg = invalid;
n->colors.highlight = invalid;
n->colors.frame = invalid;
n->colors.highlight = NULL;

n->script_run = false;
n->dbus_valid = false;

n->fullscreen = FS_SHOW;

n->original = NULL;

n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
n->default_action_name = g_strdup("default");

Expand Down Expand Up @@ -508,9 +510,10 @@ void notification_init(struct notification *n)
}
if (!COLOR_VALID(n->colors.fg)) n->colors.fg = defcolors.fg;
if (!COLOR_VALID(n->colors.bg)) n->colors.bg = defcolors.bg;
if (!COLOR_VALID(n->colors.highlight)) n->colors.highlight = defcolors.highlight;
if (!COLOR_VALID(n->colors.frame)) n->colors.frame = defcolors.frame;

if (!GRADIENT_VALID(n->colors.highlight)) n->colors.highlight = defcolors.highlight;

/* Sanitize misc hints */
if (n->progress < 0)
n->progress = -1;
Expand Down Expand Up @@ -783,6 +786,7 @@ void notification_keep_original(struct notification *n)
if (n->original) return;
n->original = g_malloc0(sizeof(struct rule));
*n->original = empty_rule;
n->original->name = g_strdup("original");
}

/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
2 changes: 1 addition & 1 deletion src/notification.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ struct notification_colors {
struct color frame;
struct color bg;
struct color fg;
struct color highlight;
struct gradient *highlight;
};

struct notification {
Expand Down
32 changes: 32 additions & 0 deletions src/option_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,36 @@ int string_parse_color(const char *s, struct color *ret)
return true;
}

int string_parse_gradient(const char *s, struct gradient **ret)
{
struct color colors[10];
size_t length = 0;

gchar **strs = g_strsplit(s, ",", -1);
for (int i = 0; strs[i] != NULL; i++) {
if (i > 10) {
LOG_W("Do you really need so many colors? ;)");
break;
}

if (!string_parse_color(g_strstrip(strs[i]), &colors[length++])) {
g_strfreev(strs);
return false;
}
}

g_strfreev(strs);
if (length == 0) {
DIE("Unreachable");
}

*ret = gradient_alloc(length);
memcpy((*ret)->colors, colors, length * sizeof(struct color));
gradient_pattern(*ret);

return true;
}

int string_parse_bool(const void *data, const char *s, void *ret)
{
// this is needed, since string_parse_enum assumses a
Expand Down Expand Up @@ -404,6 +434,8 @@ bool set_from_string(void *target, struct setting setting, const char *value) {
return string_parse_length(target, value);
case TYPE_COLOR:
return string_parse_color(value, target);
case TYPE_GRADIENT:
return string_parse_gradient(value, target);
default:
LOG_W("Setting type of '%s' is not known (type %i)", setting.name, setting.type);
return false;
Expand Down
1 change: 1 addition & 0 deletions src/option_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
int string_parse_enum(const void* data, const char *s, void * ret);
int string_parse_sepcolor(const void *data, const char *s, void *ret);
int string_parse_color(const char *s, struct color *ret);
int string_parse_gradient(const char *s, struct gradient **ret);
int string_parse_bool(const void *data, const char *s, void *ret);
int string_parse_corners(const void *data, const char *s, void *ret);
int string_parse_maybe_int(const void *data, const char *s, void *ret);
Expand Down
Loading