Skip to content

Commit 9d3c91a

Browse files
authored
Merge pull request #1370 from bynect/gradients-new
Add color gradients
2 parents 4c977cc + 8630daf commit 9d3c91a

19 files changed

+330
-39
lines changed

docs/dunst.5.pod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,9 @@ The foreground color of the notification. See COLORS for possible values.
913913
The highlight color of the notification. This color is used for coloring the
914914
progress bar. See COLORS for possible values.
915915

916+
You can also set additional color values (as a comma-separated list)
917+
to define a linear gradient spanning all the length of the progress bar.
918+
916919
=item C<format>
917920

918921
Equivalent to the C<format> setting.

src/dbus.c

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,25 @@ static void color_entry(const struct color c, GVariantDict *dict, const char *fi
442442
}
443443
}
444444

445+
static void gradient_entry(const struct gradient *grad, GVariantDict *dict, const char *field_name) {
446+
if (GRADIENT_VALID(grad)) {
447+
if (grad->length == 1) {
448+
color_entry(grad->colors[0], dict, field_name);
449+
return;
450+
}
451+
452+
char **strv = g_malloc((grad->length + 1) * sizeof(char *));
453+
for (int i = 0; i < grad->length; i++) {
454+
char buf[10];
455+
if (color_to_string(grad->colors[i], buf))
456+
strv[i] = g_strdup(buf);
457+
}
458+
strv[grad->length] = NULL;
459+
460+
g_variant_dict_insert(dict, field_name, "^as", strv);
461+
}
462+
}
463+
445464
static void dbus_cb_dunst_RuleList(GDBusConnection *connection,
446465
const gchar *sender,
447466
GVariant *parameters,
@@ -543,7 +562,7 @@ static void dbus_cb_dunst_RuleList(GDBusConnection *connection,
543562
enum_to_string(icon_position_enum_data, r->icon_position));
544563
color_entry(r->fg, &dict, "fg");
545564
color_entry(r->bg, &dict, "bg");
546-
color_entry(r->highlight, &dict, "highlight");
565+
gradient_entry(r->highlight, &dict, "highlight");
547566
color_entry(r->fc, &dict, "fc");
548567
if (r->format)
549568
g_variant_dict_insert(&dict, "format", "s", r->format);
@@ -838,12 +857,37 @@ static struct notification *dbus_message_to_notification(const gchar *sender, GV
838857
g_variant_unref(dict_value);
839858
}
840859

841-
if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
860+
if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING_ARRAY))) {
861+
char **cols = (char **)g_variant_get_strv(dict_value, NULL);
862+
size_t length = g_strv_length(cols);
863+
struct gradient *grad = gradient_alloc(length);
864+
865+
for (int i = 0; i < length; i++) {
866+
if (!string_parse_color(cols[i], &grad->colors[i])) {
867+
g_free(grad);
868+
goto end;
869+
}
870+
}
871+
872+
873+
gradient_pattern(grad);
874+
875+
notification_keep_original(n);
876+
if (!GRADIENT_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight;
877+
n->colors.highlight = grad;
878+
879+
end:
880+
g_variant_unref(dict_value);
881+
} else if ((dict_value = g_variant_lookup_value(hints, "hlcolor", G_VARIANT_TYPE_STRING))) {
842882
struct color c;
843883
if (string_parse_color(g_variant_get_string(dict_value, NULL), &c)) {
884+
struct gradient *grad = gradient_alloc(1);
885+
grad->colors[0] = c;
886+
gradient_pattern(grad);
887+
844888
notification_keep_original(n);
845-
if (!COLOR_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight;
846-
n->colors.highlight = c;
889+
if (!GRADIENT_VALID(n->original->highlight)) n->original->highlight = n->colors.highlight;
890+
n->colors.highlight = grad;
847891
}
848892
g_variant_unref(dict_value);
849893
}

src/draw.c

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,69 @@ char *color_to_string(struct color c, char buf[10])
7171
return buf;
7272
}
7373

74+
struct gradient *gradient_alloc(size_t length)
75+
{
76+
if (length == 0)
77+
return NULL;
78+
79+
struct gradient *grad = g_malloc(sizeof(struct gradient) + length * sizeof(struct color));
80+
81+
grad->length = length;
82+
grad->pattern = NULL;
83+
84+
return grad;
85+
}
86+
87+
void gradient_free(struct gradient *grad)
88+
{
89+
if (grad == NULL) return;
90+
91+
if (grad->pattern)
92+
cairo_pattern_destroy(grad->pattern);
93+
94+
g_free(grad);
95+
}
96+
97+
void gradient_pattern(struct gradient *grad)
98+
{
99+
if (grad->length == 1) {
100+
grad->pattern = cairo_pattern_create_rgba(grad->colors[0].r,
101+
grad->colors[0].g,
102+
grad->colors[0].b,
103+
grad->colors[0].a);
104+
} else {
105+
grad->pattern = cairo_pattern_create_linear(0, 0, 1, 0);
106+
for (int i = 0; i < grad->length; i++) {
107+
double offset = i / (double)(grad->length - 1);
108+
cairo_pattern_add_color_stop_rgba(grad->pattern,
109+
offset,
110+
grad->colors[i].r,
111+
grad->colors[i].g,
112+
grad->colors[i].b,
113+
grad->colors[i].a);
114+
}
115+
}
116+
}
117+
118+
char *gradient_to_string(const struct gradient *grad)
119+
{
120+
if (!GRADIENT_VALID(grad)) return NULL;
121+
122+
char *buf = g_malloc(grad->length * 11);
123+
color_to_string(grad->colors[0], buf);
124+
char *ptr = buf + 9;
125+
126+
for (int i = 1; i < grad->length; i++) {
127+
ptr += g_snprintf(ptr, 11, ", #%02x%02x%02x%02x",
128+
(int)(grad->colors[i].r * 255),
129+
(int)(grad->colors[i].g * 255),
130+
(int)(grad->colors[i].b * 255),
131+
(int)(grad->colors[i].a * 255));
132+
}
133+
134+
return buf;
135+
}
136+
74137
void draw_setup(void)
75138
{
76139
const struct output *out = output_create(settings.force_xwayland);
@@ -320,7 +383,7 @@ static struct colored_layout *layout_init_shared(cairo_t *c, struct notification
320383

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

830893
// top layer (fill)
831-
cairo_set_source_rgba(c, COLOR(cl, highlight.r), COLOR(cl, highlight.g), COLOR(cl, highlight.b), COLOR(cl, highlight.a));
894+
cairo_matrix_t matrix;
895+
cairo_matrix_init_scale(&matrix, 1.0 / width, 1.0);
896+
cairo_pattern_set_matrix(COLOR(cl, highlight->pattern), &matrix);
897+
cairo_set_source(c, COLOR(cl, highlight->pattern));
898+
832899
draw_rounded_rect(c, x_bar_1, frame_y, progress_width_1, progress_height,
833900
settings.progress_bar_corner_radius, scale, settings.progress_bar_corners);
834901
cairo_fill(c);

src/draw.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,23 @@ struct color {
5353
*/
5454
char *color_to_string(struct color c, char buf[10]);
5555

56+
struct gradient {
57+
cairo_pattern_t *pattern;
58+
size_t length;
59+
struct color colors[];
60+
};
61+
62+
#define GRADIENT_VALID(g) ((g) != NULL && (g)->length != 0)
63+
64+
struct gradient *gradient_alloc(size_t length);
65+
66+
void gradient_free(struct gradient *grad);
67+
68+
void gradient_pattern(struct gradient *grad);
69+
70+
char *gradient_to_string(const struct gradient *grad);
71+
72+
5673
void draw_setup(void);
5774

5875
void draw(void);

src/dunst.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "draw.h"
1515
#include "log.h"
1616
#include "menu.h"
17+
#include "rules.h"
1718
#include "notification.h"
1819
#include "option_parser.h"
1920
#include "queues.h"
@@ -208,6 +209,8 @@ static void teardown(void)
208209
draw_deinit();
209210

210211
g_strfreev(config_paths);
212+
213+
g_slist_free_full(rules, (GDestroyNotify)rule_free);
211214
}
212215

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

322325
teardown();
323326

327+
settings_free(&settings);
328+
324329
return 0;
325330
}
326331

src/notification.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,12 @@ void notification_print(const struct notification *n)
7070
char buf[10];
7171
printf("\tfg: %s\n", STR_NN(color_to_string(n->colors.fg, buf)));
7272
printf("\tbg: %s\n", STR_NN(color_to_string(n->colors.bg, buf)));
73-
printf("\thighlight: %s\n", STR_NN(color_to_string(n->colors.highlight, buf)));
7473
printf("\tframe: %s\n", STR_NN(color_to_string(n->colors.frame, buf)));
74+
75+
char *grad = gradient_to_string(n->colors.highlight);
76+
printf("\thighlight: %s\n", STR_NN(grad));
77+
g_free(grad);
78+
7579
printf("\tfullscreen: %s\n", enum_to_string_fullscreen(n->fullscreen));
7680
printf("\tformat: %s\n", STR_NN(n->format));
7781
printf("\tprogress: %d\n", n->progress);
@@ -295,11 +299,7 @@ void notification_unref(struct notification *n)
295299
return;
296300

297301
if (n->original) {
298-
g_free(n->original->action_name);
299-
g_free(n->original->set_category);
300-
g_free(n->original->default_icon);
301-
g_free(n->original->set_stack_tag);
302-
g_free(n->original->new_icon);
302+
rule_free(n->original);
303303
g_free(n->original);
304304
}
305305

@@ -457,14 +457,16 @@ struct notification *notification_create(void)
457457
struct color invalid = COLOR_UNINIT;
458458
n->colors.fg = invalid;
459459
n->colors.bg = invalid;
460-
n->colors.highlight = invalid;
461460
n->colors.frame = invalid;
461+
n->colors.highlight = NULL;
462462

463463
n->script_run = false;
464464
n->dbus_valid = false;
465465

466466
n->fullscreen = FS_SHOW;
467467

468+
n->original = NULL;
469+
468470
n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
469471
n->default_action_name = g_strdup("default");
470472

@@ -508,9 +510,10 @@ void notification_init(struct notification *n)
508510
}
509511
if (!COLOR_VALID(n->colors.fg)) n->colors.fg = defcolors.fg;
510512
if (!COLOR_VALID(n->colors.bg)) n->colors.bg = defcolors.bg;
511-
if (!COLOR_VALID(n->colors.highlight)) n->colors.highlight = defcolors.highlight;
512513
if (!COLOR_VALID(n->colors.frame)) n->colors.frame = defcolors.frame;
513514

515+
if (!GRADIENT_VALID(n->colors.highlight)) n->colors.highlight = defcolors.highlight;
516+
514517
/* Sanitize misc hints */
515518
if (n->progress < 0)
516519
n->progress = -1;
@@ -783,6 +786,7 @@ void notification_keep_original(struct notification *n)
783786
if (n->original) return;
784787
n->original = g_malloc0(sizeof(struct rule));
785788
*n->original = empty_rule;
789+
n->original->name = g_strdup("original");
786790
}
787791

788792
/* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */

src/notification.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ struct notification_colors {
4242
struct color frame;
4343
struct color bg;
4444
struct color fg;
45-
struct color highlight;
45+
struct gradient *highlight;
4646
};
4747

4848
struct notification {

src/option_parser.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,36 @@ int string_parse_color(const char *s, struct color *ret)
235235
return true;
236236
}
237237

238+
int string_parse_gradient(const char *s, struct gradient **ret)
239+
{
240+
struct color colors[10];
241+
size_t length = 0;
242+
243+
gchar **strs = g_strsplit(s, ",", -1);
244+
for (int i = 0; strs[i] != NULL; i++) {
245+
if (i > 10) {
246+
LOG_W("Do you really need so many colors? ;)");
247+
break;
248+
}
249+
250+
if (!string_parse_color(g_strstrip(strs[i]), &colors[length++])) {
251+
g_strfreev(strs);
252+
return false;
253+
}
254+
}
255+
256+
g_strfreev(strs);
257+
if (length == 0) {
258+
DIE("Unreachable");
259+
}
260+
261+
*ret = gradient_alloc(length);
262+
memcpy((*ret)->colors, colors, length * sizeof(struct color));
263+
gradient_pattern(*ret);
264+
265+
return true;
266+
}
267+
238268
int string_parse_bool(const void *data, const char *s, void *ret)
239269
{
240270
// this is needed, since string_parse_enum assumses a
@@ -404,6 +434,8 @@ bool set_from_string(void *target, struct setting setting, const char *value) {
404434
return string_parse_length(target, value);
405435
case TYPE_COLOR:
406436
return string_parse_color(value, target);
437+
case TYPE_GRADIENT:
438+
return string_parse_gradient(value, target);
407439
default:
408440
LOG_W("Setting type of '%s' is not known (type %i)", setting.name, setting.type);
409441
return false;

src/option_parser.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
int string_parse_enum(const void* data, const char *s, void * ret);
1414
int string_parse_sepcolor(const void *data, const char *s, void *ret);
1515
int string_parse_color(const char *s, struct color *ret);
16+
int string_parse_gradient(const char *s, struct gradient **ret);
1617
int string_parse_bool(const void *data, const char *s, void *ret);
1718
int string_parse_corners(const void *data, const char *s, void *ret);
1819
int string_parse_maybe_int(const void *data, const char *s, void *ret);

0 commit comments

Comments
 (0)