diff --git a/config.def.h b/config.def.h index 9f5c224..4df3438 100644 --- a/config.def.h +++ b/config.def.h @@ -11,6 +11,7 @@ char *lowfgcolor = "#000000"; char *format = "%s %b"; /* default format */ int timeouts[] = { 10, 10, 0 }; /* low, normal, critical */ +char *icons[] = { "info", "info", "emblem-important" }; /* low, normal, critical */ unsigned int transparency = 0; /* transparency */ char *geom = "0x0"; /* geometry */ @@ -50,6 +51,9 @@ char *dmenu = "/usr/bin/dmenu"; char *browser = "/usr/bin/firefox"; +/* paths to default icons */ +char *icon_folders = "/usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/"; + /* follow focus to different monitor and display notifications there? * possible values: * FOLLOW_NONE diff --git a/dunst.h b/dunst.h index 97d67a7..ad17b19 100644 --- a/dunst.h +++ b/dunst.h @@ -16,6 +16,7 @@ #define ColBG 0 enum alignment { left, center, right }; +enum icon_position_t { icons_left, icons_right, icons_off }; enum separator_color { FOREGROUND, AUTO, FRAME, CUSTOM }; enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD }; diff --git a/dunstrc b/dunstrc index bc72e46..03688f3 100644 --- a/dunstrc +++ b/dunstrc @@ -138,6 +138,12 @@ # Browser for opening urls in context menu. browser = /usr/bin/firefox -new-tab + # Align icons left/right/off + icon_position = off + + # Paths to default icons. + icon_folders = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/ + [frame] width = 3 color = "#aaaaaa" @@ -185,7 +191,7 @@ # override settings for certain messages. # Messages can be matched by "appname", "summary", "body", "icon", # "msg_urgency" and you can override the "timeout", "urgency", "foreground", -# "background" and "format". +# "background", "new_icon" and "format". # Shell-like globbing will get expanded. # # SCRIPTING diff --git a/notification.c b/notification.c index 42242d6..3510851 100644 --- a/notification.c +++ b/notification.c @@ -383,6 +383,14 @@ int notification_init(notification * n, int id) n->timeout == -1 ? settings.timeouts[n->urgency] : n->timeout; n->start = 0; + if (n->icon == NULL) { + n->icon = settings.icons[n->urgency]; + } + else if (strlen(n->icon) <= 0) { + free(n->icon); + n->icon = settings.icons[n->urgency]; + } + n->timestamp = time(NULL); n->redisplayed = false; diff --git a/rules.c b/rules.c index 8cc5186..4762c9f 100644 --- a/rules.c +++ b/rules.c @@ -15,6 +15,8 @@ void rule_apply(rule_t * r, notification * n) n->timeout = r->timeout; if (r->urgency != -1) n->urgency = r->urgency; + if (r->new_icon) + n->icon = r->new_icon; if (r->fg) n->color_strings[ColFG] = r->fg; if (r->bg) @@ -51,6 +53,7 @@ void rule_init(rule_t * r) r->msg_urgency = -1; r->timeout = -1; r->urgency = -1; + r->new_icon = NULL; r->fg = NULL; r->bg = NULL; r->format = NULL; diff --git a/rules.h b/rules.h index 2862f75..397dd64 100644 --- a/rules.h +++ b/rules.h @@ -18,6 +18,7 @@ typedef struct _rule_t { /* actions */ int timeout; int urgency; + char *new_icon; char *fg; char *bg; const char *format; diff --git a/settings.c b/settings.c index ba97ec0..869c6a5 100644 --- a/settings.c +++ b/settings.c @@ -213,6 +213,28 @@ void load_settings(char *cmdline_config_path) option_get_string("global", "browser", "-browser", browser, "path to browser"); + { + char *c = option_get_string("global", "icon_position", + "-icon_position", "off", + "Align icons left/right/off"); + if (strlen(c) > 0) { + if (strcmp(c, "left") == 0) + settings.icon_position = icons_left; + else if (strcmp(c, "right") == 0) + settings.icon_position = icons_right; + else if (strcmp(c, "off") == 0) + settings.icon_position = icons_off; + else + fprintf(stderr, + "Warning: unknown icon position: %s\n", c); + free(c); + } + } + + settings.icon_folders = + option_get_string("global", "icon_folders", "-icon_folders", icon_folders, + "paths to default icons"); + settings.frame_width = option_get_int("frame", "width", "-frame_width", frame_width, "Width of frame around window"); @@ -230,6 +252,9 @@ void load_settings(char *cmdline_config_path) settings.timeouts[LOW] = option_get_int("urgency_low", "timeout", "-lto", timeouts[LOW], "Timeout for notifications with low urgency"); + settings.icons[LOW] = + option_get_string("urgency_low", "icon", "-li", icons[LOW], + "Icon for notifications with low urgency"); settings.normbgcolor = option_get_string("urgency_normal", "background", "-nb", normbgcolor, @@ -241,6 +266,9 @@ void load_settings(char *cmdline_config_path) settings.timeouts[NORM] = option_get_int("urgency_normal", "timeout", "-nto", timeouts[NORM], "Timeout for notifications with normal urgency"); + settings.icons[NORM] = + option_get_string("urgency_normal", "icon", "-ni", icons[NORM], + "Icon for notifications with normal urgency"); settings.critbgcolor = option_get_string("urgency_critical", "background", "-cb", critbgcolor, @@ -250,9 +278,11 @@ void load_settings(char *cmdline_config_path) critfgcolor, "Foreground color for notifications with ciritical urgency"); settings.timeouts[CRIT] = - option_get_int("urgency_critical", "timeout", "-cto", - timeouts[CRIT], + option_get_int("urgency_critical", "timeout", "-cto", timeouts[CRIT], "Timeout for notifications with critical urgency"); + settings.icons[CRIT] = + option_get_string("urgency_critical", "icon", "-ci", icons[CRIT], + "Icon for notifications with critical urgency"); settings.close_ks.str = option_get_string("shortcuts", "close", "-key", close_ks.str, @@ -317,7 +347,8 @@ void load_settings(char *cmdline_config_path) r->fg = ini_get_string(cur_section, "foreground", r->fg); r->bg = ini_get_string(cur_section, "background", r->bg); r->format = ini_get_string(cur_section, "format", r->format); - r->script = ini_get_string(cur_section, "script", NULL); + r->new_icon = ini_get_string(cur_section, "new_icon", r->new_icon); + r->script = ini_get_string(cur_section, "script", NULL); } #ifndef STATIC_CONFIG diff --git a/settings.h b/settings.h index 9481cce..55d08f4 100644 --- a/settings.h +++ b/settings.h @@ -14,6 +14,7 @@ typedef struct _settings { char *lowfgcolor; char *format; int timeouts[3]; + char *icons[3]; unsigned int transparency; char *geom; int shrink; @@ -42,6 +43,8 @@ typedef struct _settings { char *dmenu; char **dmenu_cmd; char *browser; + enum icon_position_t icon_position; + char *icon_folders; enum follow_mode f_mode; keyboard_shortcut close_ks; keyboard_shortcut close_all_ks; diff --git a/x.c b/x.c index 1b7525e..5aae2e8 100644 --- a/x.c +++ b/x.c @@ -40,6 +40,7 @@ typedef struct _colored_layout { color_t bg; char *text; PangoAttrList *attr; + cairo_surface_t *icon; } colored_layout; cairo_ctx_t cairo_ctx; @@ -177,6 +178,7 @@ static void free_colored_layout(void *data) g_object_unref(cl->l); pango_attr_list_unref(cl->attr); g_free(cl->text); + if (cl->icon) cairo_surface_destroy(cl->icon); g_free(cl); } @@ -219,6 +221,10 @@ static dimension_t calculate_dimensions(GSList *layouts) colored_layout *cl = iter->data; int w=0,h=0; pango_layout_get_pixel_size(cl->l, &w, &h); + if (cl->icon) { + h = MAX(cairo_image_surface_get_height(cl->icon), h); + w += cairo_image_surface_get_width(cl->icon) + settings.h_padding; + } dim.h += h; text_width = MAX(w, text_width); @@ -238,13 +244,18 @@ static dimension_t calculate_dimensions(GSList *layouts) } /* re-setup the layout */ - int width = dim.w; - width -= 2 * settings.h_padding; - width -= 2 * settings.frame_width; - r_setup_pango_layout(cl->l, width); + w = dim.w; + w -= 2 * settings.h_padding; + w -= 2 * settings.frame_width; + if (cl->icon) w -= cairo_image_surface_get_width(cl->icon) + settings.h_padding; + r_setup_pango_layout(cl->l, w); /* re-read information */ pango_layout_get_pixel_size(cl->l, &w, &h); + if (cl->icon) { + h = MAX(cairo_image_surface_get_height(cl->icon), h); + w += cairo_image_surface_get_width(cl->icon) + settings.h_padding; + } dim.h += h; text_width = MAX(w, text_width); } @@ -258,6 +269,49 @@ static dimension_t calculate_dimensions(GSList *layouts) return dim; } +static cairo_t *get_icon_surface(char *icon_path) +{ + cairo_t *icon_surface = NULL; + if (strlen(icon_path) > 0 && settings.icon_position != icons_off) { + /* absolute path? */ + if (icon_path[0] == '/' || icon_path[0] == '~') { + icon_surface = cairo_image_surface_create_from_png(icon_path); + if (cairo_surface_status(icon_surface) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(icon_surface); + icon_surface = NULL; + } + } + /* search in icon_folders */ + if (icon_surface == NULL) { + char *start = settings.icon_folders, + *end, *current_folder, *maybe_icon_path; + do { + end = strchr(start, ':'); + if (end == NULL) end = strchr(settings.icon_folders, '\0'); /* end = end of string */ + + current_folder = strndup(start, end - start); + maybe_icon_path = g_strconcat(current_folder, "/", icon_path, ".png", NULL); + free(current_folder); + + icon_surface = cairo_image_surface_create_from_png(maybe_icon_path); + free(maybe_icon_path); + if (cairo_surface_status(icon_surface) == CAIRO_STATUS_SUCCESS) { + return icon_surface; + } else { + cairo_surface_destroy(icon_surface); + icon_surface = NULL; + } + + start = end + 1; + } while (*(end) != '\0'); + } + if (icon_surface == NULL) + fprintf(stderr, + "Could not load icon: '%s'\n", icon_path); + } + return icon_surface; +} + static colored_layout *r_init_shared(cairo_t *c, notification *n) { colored_layout *cl = malloc(sizeof(colored_layout)); @@ -267,6 +321,7 @@ static colored_layout *r_init_shared(cairo_t *c, notification *n) pango_layout_set_ellipsize(cl->l, PANGO_ELLIPSIZE_MIDDLE); } + cl->icon = get_icon_surface(n->icon); cl->fg = x_string_to_color_t(n->color_strings[ColFG]); cl->bg = x_string_to_color_t(n->color_strings[ColBG]); @@ -279,6 +334,7 @@ static colored_layout *r_init_shared(cairo_t *c, notification *n) } else { width -= 2 * settings.h_padding; width -= 2 * settings.frame_width; + if (cl->icon) width -= cairo_image_surface_get_width(cl->icon) + settings.h_padding; r_setup_pango_layout(cl->l, width); } @@ -320,6 +376,7 @@ static colored_layout *r_create_layout_from_notification(cairo_t *c, notificatio pango_layout_get_pixel_size(cl->l, NULL, &(n->displayed_height)); + if (cl->icon) n->displayed_height = MAX(cairo_image_surface_get_height(cl->icon), n->displayed_height); n->displayed_height += 2 * settings.padding; n->first_render = false; @@ -369,6 +426,7 @@ static dimension_t x_render_layout(cairo_t *c, colored_layout *cl, dimension_t d { int h; pango_layout_get_pixel_size(cl->l, NULL, &h); + if (cl->icon) h = MAX(cairo_image_surface_get_height(cl->icon), h); int bg_x = 0; int bg_y = dim.y; @@ -390,7 +448,9 @@ static dimension_t x_render_layout(cairo_t *c, colored_layout *cl, dimension_t d cairo_fill(c); dim.y += settings.padding; - cairo_move_to(c, settings.h_padding, dim.y); + if (cl->icon && settings.icon_position == icons_left) + cairo_move_to(c, cairo_image_surface_get_width(cl->icon) + 2 * settings.h_padding, dim.y); + else cairo_move_to(c, settings.h_padding, dim.y); cairo_set_source_rgb(c, cl->fg.r, cl->fg.g, cl->fg.b); pango_cairo_update_layout(c, cl->l); pango_cairo_show_layout(c, cl->l); @@ -408,6 +468,20 @@ static dimension_t x_render_layout(cairo_t *c, colored_layout *cl, dimension_t d } cairo_move_to(c, settings.h_padding, dim.y); + if (cl->icon) { + unsigned int image_width = cairo_image_surface_get_width(cl->icon), + image_height = cairo_image_surface_get_height(cl->icon), + image_x, + image_y = bg_y + settings.padding; + + if (settings.icon_position == icons_left) image_x = settings.h_padding; + else image_x = bg_width - settings.h_padding - image_width; + + cairo_set_source_surface (c, cl->icon, image_x, image_y); + cairo_rectangle (c, image_x, image_y, image_width, image_height); + cairo_fill (c); + } + return dim; }