diff --git a/dbus.c b/dbus.c index 27072e4..4d52933 100644 --- a/dbus.c +++ b/dbus.c @@ -74,6 +74,7 @@ static void onGetServerInformation(GDBusConnection * connection, const gchar * sender, const GVariant * parameters, GDBusMethodInvocation * invocation); +static RawImage * get_raw_image_from_data_hint(GVariant *icon_data); void handle_method_call(GDBusConnection * connection, const gchar * sender, @@ -140,6 +141,7 @@ static void onNotify(GDBusConnection * connection, gchar *fgcolor = NULL; gchar *bgcolor = NULL; gchar *category = NULL; + RawImage *raw_icon = NULL; actions->actions = NULL; actions->count = 0; @@ -232,6 +234,23 @@ static void onNotify(GDBusConnection * connection, dict_value, NULL); } + dict_value = + g_variant_lookup_value(content, + "image-data", + G_VARIANT_TYPE("(iiibiiay)")); + if (!dict_value) { + dict_value = + g_variant_lookup_value(content, + "icon_data", + G_VARIANT_TYPE("(iiibiiay)")); + } + + if (dict_value) { + raw_icon = + get_raw_image_from_data_hint( + dict_value); + } + dict_value = g_variant_lookup_value(content, "value", @@ -283,6 +302,7 @@ static void onNotify(GDBusConnection * connection, n->summary = summary; n->body = body; n->icon = icon; + n->raw_icon = raw_icon; n->timeout = timeout; n->allow_markup = settings.allow_markup; n->plain_text = settings.plain_text; @@ -412,6 +432,40 @@ static void on_name_lost(GDBusConnection * connection, exit(1); } +static RawImage * get_raw_image_from_data_hint(GVariant *icon_data) +{ + RawImage *image = malloc(sizeof(RawImage)); + GVariant *data_variant; + gsize expected_len; + + g_variant_get (icon_data, + "(iiibii@ay)", + &image->width, + &image->height, + &image->rowstride, + &image->has_alpha, + &image->bits_per_sample, + &image->n_channels, + &data_variant); + + expected_len = (image->height - 1) * image->rowstride + image->width + * ((image->n_channels * image->bits_per_sample + 7) / 8); + + if (expected_len != g_variant_get_size (data_variant)) { + fprintf(stderr, "Expected image data to be of length %" G_GSIZE_FORMAT + " but got a " "length of %" G_GSIZE_FORMAT, + expected_len, + g_variant_get_size (data_variant)); + free(image); + return NULL; + } + + image->data = (guchar *) g_memdup (g_variant_get_data (data_variant), + g_variant_get_size (data_variant)); + + return image; +} + int initdbus(void) { guint owner_id; diff --git a/notification.c b/notification.c index 21ccfee..0ae12af 100644 --- a/notification.c +++ b/notification.c @@ -161,6 +161,12 @@ void notification_free(notification * n) free(n->actions->dmenu_str); } + if (n->raw_icon) { + if (n->raw_icon->data) + free(n->raw_icon->data); + free(n->raw_icon); + } + free(n); } @@ -442,11 +448,12 @@ 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 = strdup(settings.icons[n->urgency]); - } - else if (strlen(n->icon) <= 0) { + if (strlen(n->icon) <= 0) { free(n->icon); + n->icon = NULL; + } + + if (n->raw_icon == NULL && n->icon == NULL) { n->icon = strdup(settings.icons[n->urgency]); } diff --git a/notification.h b/notification.h index 71422ea..c6b0b6a 100644 --- a/notification.h +++ b/notification.h @@ -7,6 +7,16 @@ #define NORM 1 #define CRIT 2 +typedef struct _raw_image { + int width; + int height; + int rowstride; + int has_alpha; + int bits_per_sample; + int n_channels; + unsigned char *data; +} RawImage; + typedef struct _actions { char **actions; char *dmenu_str; @@ -18,6 +28,7 @@ typedef struct _notification { char *summary; char *body; char *icon; + RawImage *raw_icon; char *msg; /* formatted message */ char *category; char *text_to_render; diff --git a/x.c b/x.c index 5951f8e..844c130 100644 --- a/x.c +++ b/x.c @@ -286,6 +286,24 @@ static dimension_t calculate_dimensions(GSList *layouts) return dim; } +static cairo_surface_t *gdk_pixbuf_to_cairo_surface(const GdkPixbuf *pixbuf) +{ + cairo_surface_t *icon_surface = NULL; + cairo_t *cr; + cairo_format_t format; + double width, height; + + format = gdk_pixbuf_get_has_alpha(pixbuf) ? CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24; + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + icon_surface = cairo_image_surface_create(format, width, height); + cr = cairo_create(icon_surface); + gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); + cairo_paint(cr); + free(cr); + return icon_surface; +} + static cairo_surface_t *get_icon_surface_from_file(const char *icon_path) { cairo_surface_t *icon_surface = NULL; @@ -297,23 +315,9 @@ static cairo_surface_t *get_icon_surface_from_file(const char *icon_path) } else { GdkPixbuf *pixbuf; GError *error = NULL; - cairo_t *cr; - cairo_format_t format; - double width, height; pixbuf = gdk_pixbuf_new_from_file(icon_path, &error); if (pixbuf != NULL) { - if (gdk_pixbuf_get_has_alpha(pixbuf)) { - format = CAIRO_FORMAT_ARGB32; - } else { - format = CAIRO_FORMAT_RGB24; - } - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); - icon_surface = cairo_image_surface_create(format, width, height); - cr = cairo_create(icon_surface); - gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0); - cairo_paint(cr); - free(cr); + icon_surface = gdk_pixbuf_to_cairo_surface(pixbuf); g_object_unref(pixbuf); } else { g_free(error); @@ -327,7 +331,7 @@ static cairo_surface_t *get_icon_surface_from_file(const char *icon_path) return icon_surface; } -static cairo_surface_t *get_icon_surface(char *icon_path) +static cairo_surface_t *get_icon_surface_from_path(char *icon_path) { cairo_surface_t *icon_surface = NULL; gchar *uri_path = NULL; @@ -374,6 +378,28 @@ static cairo_surface_t *get_icon_surface(char *icon_path) return icon_surface; } +static cairo_surface_t *get_icon_surface_from_raw_image(const RawImage *raw_image) +{ + cairo_surface_t *icon_surface = NULL; + GdkPixbuf *pixbuf; + + pixbuf = gdk_pixbuf_new_from_data(raw_image->data, + GDK_COLORSPACE_RGB, + raw_image->has_alpha, + raw_image->bits_per_sample, + raw_image->width, + raw_image->height, + raw_image->rowstride, + NULL, + NULL); + + if (pixbuf != NULL) { + icon_surface = gdk_pixbuf_to_cairo_surface(pixbuf); + g_object_unref(pixbuf); + } + return icon_surface; +} + static colored_layout *r_init_shared(cairo_t *c, notification *n) { colored_layout *cl = malloc(sizeof(colored_layout)); @@ -386,7 +412,11 @@ 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); + if (n->icon) { + cl->icon = get_icon_surface_from_path(n->icon); + } else if (n->raw_icon) { + cl->icon = get_icon_surface_from_raw_image(n->raw_icon); + } cl->fg = x_string_to_color_t(n->color_strings[ColFG]); cl->bg = x_string_to_color_t(n->color_strings[ColBG]);