From ed2ffd516208d17a99e7d2fca969027532dc47a1 Mon Sep 17 00:00:00 2001 From: Oleksiy Yakovenko Date: Fri, 29 Dec 2023 11:11:54 +0100 Subject: [PATCH] notify: sending image data via image-data hint WIP --- configure.ac | 11 +- plugins/gtkui/covermanager/covermanager.c | 33 ++++- plugins/notify/Makefile.am | 4 +- plugins/notify/notify.c | 156 +++++++++++++++++++++- premake5.lua | 2 +- 5 files changed, 192 insertions(+), 14 deletions(-) diff --git a/configure.ac b/configure.ac index 8bf3d9e492..eea40a416f 100644 --- a/configure.ac +++ b/configure.ac @@ -733,7 +733,16 @@ AS_IF([test "${enable_gme}" != "no" -a "${HAVE_ZLIB}" = "yes"], [ ]) AS_IF([test "${HAVE_DBUS}" = "yes" -a "${enable_notify}" != "no"], [ - HAVE_NOTIFY=yes + AS_IF([test "${enable_staticlink}" != "no"], [ + HAVE_NOTIFY=yes + NOTIFY_DEPS_CFLAGS="${GTK3_310_CFLAGS}" + NOTIFY_DEPS_LIBS="${GTK3_310_LIBS}" + AC_SUBST(NOTIFY_DEPS_CFLAGS) + AC_SUBST(NOTIFY_DEPS_LIBS) + ], [ + PKG_CHECK_MODULES(NOTIFY_DEPS, glib-2.0 >= 2.26 gio-2.0 gdk-pixbuf-2.0, HAVE_NOTIFY=yes, HAVE_NOTIFY=no) + ]) + NOTIFY_LIBS="$DBUS_DEPS_LIBS" NOTIFY_CFLAGS="$DBUS_DEPS_CFLAGS" AC_SUBST(NOTIFY_LIBS) diff --git a/plugins/gtkui/covermanager/covermanager.c b/plugins/gtkui/covermanager/covermanager.c index 75c8ac4229..ea6b4f0515 100644 --- a/plugins/gtkui/covermanager/covermanager.c +++ b/plugins/gtkui/covermanager/covermanager.c @@ -157,7 +157,7 @@ static GdkPixbuf * _load_image_from_cover (covermanager_t *impl, ddb_cover_info_t *cover, int want_default) { GdkPixbuf *img = NULL; - if (!img && cover && cover->image_filename) { + if (cover && cover->image_filename) { long size = 0; char *buf = _buffer_from_file (cover->image_filename, &size); if (buf != NULL) { @@ -267,7 +267,8 @@ _cover_loaded_callback (int error, ddb_cover_query_t *query, ddb_cover_info_t *c gtkui_dispatch_on_main (^{ // Prevent spurious loading of the same image. The load is already scheduled, so we should just wait for it. char *key = _cache_key_for_track (impl, query->track); - gboolean should_wait = gobj_cache_get_should_wait (impl->cache, key) || (gobj_cache_get (impl->cache, key) != NULL); + gboolean should_wait = + gobj_cache_get_should_wait (impl->cache, key) || (gobj_cache_get (impl->cache, key) != NULL); if (should_wait) { // append to the end of loader queue @@ -378,7 +379,12 @@ covermanager_free (covermanager_t *impl) { } static GdkPixbuf * -_cover_for_track (covermanager_t *impl, int want_default, DB_playItem_t *track, int64_t source_id, covermanager_completion_block_t completion_block) { +_cover_for_track ( + covermanager_t *impl, + int want_default, + DB_playItem_t *track, + int64_t source_id, + covermanager_completion_block_t completion_block) { if (!impl->plugin) { completion_block (NULL); return NULL; @@ -418,12 +424,20 @@ _cover_for_track (covermanager_t *impl, int want_default, DB_playItem_t *track, } GdkPixbuf * -covermanager_cover_for_track_no_default (covermanager_t *impl, DB_playItem_t *track, int64_t source_id, covermanager_completion_block_t completion_block) { +covermanager_cover_for_track_no_default ( + covermanager_t *impl, + DB_playItem_t *track, + int64_t source_id, + covermanager_completion_block_t completion_block) { return _cover_for_track (impl, 0, track, source_id, completion_block); } GdkPixbuf * -covermanager_cover_for_track (covermanager_t *impl, DB_playItem_t *track, int64_t source_id, covermanager_completion_block_t completion_block) { +covermanager_cover_for_track ( + covermanager_t *impl, + DB_playItem_t *track, + int64_t source_id, + covermanager_completion_block_t completion_block) { return _cover_for_track (impl, 1, track, source_id, completion_block); } @@ -451,8 +465,13 @@ covermanager_create_scaled_image (covermanager_t *manager, GdkPixbuf *image, Gtk } GtkAllocation -covermanager_desired_size_for_image_size (covermanager_t *manager, GtkAllocation image_size, GtkAllocation availableSize) { - double scale = min ((double)availableSize.width / (double)image_size.width, (double)availableSize.height / (double)image_size.height); +covermanager_desired_size_for_image_size ( + covermanager_t *manager, + GtkAllocation image_size, + GtkAllocation availableSize) { + double scale = min ( + (double)availableSize.width / (double)image_size.width, + (double)availableSize.height / (double)image_size.height); GtkAllocation a = { 0 }; a.width = image_size.width * scale; diff --git a/plugins/notify/Makefile.am b/plugins/notify/Makefile.am index 53b692f850..cd73c33185 100644 --- a/plugins/notify/Makefile.am +++ b/plugins/notify/Makefile.am @@ -3,6 +3,6 @@ pkglib_LTLIBRARIES = notify.la notify_la_SOURCES = notify.c notify_la_LDFLAGS = -module -avoid-version -notify_la_LIBADD = $(LDADD) $(NOTIFY_LIBS) $(DISPATCH_LIBS) -notify_la_CFLAGS = -std=c99 $(CFLAGS) $(NOTIFY_CFLAGS) $(DISPATCH_CFLAGS) -I@top_srcdir@/include +notify_la_LIBADD = $(LDADD) $(NOTIFY_LIBS) $(NOTIFY_DEPS_LIBS) $(DISPATCH_LIBS) +notify_la_CFLAGS = -std=c99 $(CFLAGS) $(NOTIFY_CFLAGS) $(NOTIFY_DEPS_CFLAGS) $(DISPATCH_CFLAGS) -I@top_srcdir@/include endif diff --git a/plugins/notify/notify.c b/plugins/notify/notify.c index 9869f5f57a..64efd30e21 100644 --- a/plugins/notify/notify.c +++ b/plugins/notify/notify.c @@ -163,6 +163,90 @@ _cover_loaded_callback (int error, ddb_cover_query_t *query, ddb_cover_info_t *c Block_release (completion_block); } +static char * +_buffer_from_file (const char *fname, long *psize) { + char *buffer = NULL; + FILE *fp = fopen (fname, "rb"); + if (fp == NULL) { + return NULL; + } + if (fseek (fp, 0, SEEK_END) < 0) { + goto error; + } + long size = ftell (fp); + if (size <= 0 || size > MAX_ALBUM_ART_FILE_SIZE) { + goto error; // we don't really want to load ultra-high-res images + } + rewind (fp); + + buffer = malloc (size); + if (buffer == NULL) { + goto error; + } + + if (fread (buffer, 1, size, fp) != size) { + goto error; + } + + fclose (fp); + + *psize = size; + return buffer; + +error: + if (fp != NULL) { + fclose (fp); + } + free (buffer); + return NULL; +} + +static GdkPixbuf * +_load_image_from_cover (const char *image_filename) { + GdkPixbuf *img = NULL; + + long size = 0; + char *buf = _buffer_from_file (cover->image_filename, &size); + if (buf != NULL) { + GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); + gdk_pixbuf_loader_write (loader, (const guchar *)buf, size, NULL); + gdk_pixbuf_loader_close (loader, NULL); + img = gdk_pixbuf_loader_get_pixbuf (loader); + free (buf); + } + + // if (img) { + // const int max_image_size = impl->image_size; + // + // // downscale + // GtkAllocation size = { + // .width = gdk_pixbuf_get_width (img), + // .height = gdk_pixbuf_get_height (img), + // }; + // + // if (size.width > max_image_size || size.height > max_image_size) { + // GtkAllocation new_size = { + // .width = max_image_size, + // .height = max_image_size, + // }; + // new_size = covermanager_desired_size_for_image_size (impl, size, new_size); + // + // GdkPixbuf *scaled_img = covermanager_create_scaled_image (impl, img, new_size); + // gobj_unref (img); + // img = scaled_img; + // } + // } + // + // if (!img && want_default) { + // img = impl->default_cover; + // if (img != NULL) { + // gobj_ref (img); + // } + // } + + return img; +} + static dbus_uint32_t show_notification (DB_playItem_t *track, char *image_filename, dbus_uint32_t replaces_id, int force) { char title[1024]; @@ -208,11 +292,36 @@ show_notification (DB_playItem_t *track, char *image_filename, dbus_uint32_t rep // KDE won't re-display notification via reuse, // so don't show it now and wait for cover callback. if (!(should_wait_for_cover && should_apply_kde_fix)) { - char *v_iconname = image_filename ?: "deadbeef"; + char *v_iconname = ""; // image_filename ?: "deadbeef"; const char *v_summary = title; const char *v_body = esc_content; dbus_int32_t v_timeout = -1; + // GdkPixbuf *img = _load_image_from_cover(image_filename); + + struct { + dbus_int32_t width = 2; + dbus_int32_t height = 2; + dbus_int32_t stride = 8; + dbus_bool_t has_alpha = 0; + dbus_int32_t bits_per_sample = 32; + dbus_int32_t channels = 4; + uint32_t bytes[4]; + } image_data; + + image_data.width = 2; + image_data.height = 2; + image_data.stride = 8; + image_data.has_alpha = 0; + image_data.bits_per_sample = 32; + image_data.channels = 4; + image_data.bytes[0] = 0x00000000; + image_data.bytes[1] = 0xffffffff; + image_data.bytes[2] = 0x00000000; + image_data.bytes[3] = 0xffffffff; + + DBusMessageIter iter, sub; + dbus_message_append_args ( msg, DBUS_TYPE_STRING, @@ -221,19 +330,60 @@ show_notification (DB_playItem_t *track, char *image_filename, dbus_uint32_t rep &replaces_id, DBUS_TYPE_STRING, &v_iconname, - DBUS_TYPE_STRING, + DBUS_TYPE_VARIANT, &v_summary, DBUS_TYPE_STRING, &v_body, DBUS_TYPE_INVALID); - DBusMessageIter iter, sub; // actions dbus_message_iter_init_append (msg, &iter); dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "s", &sub); dbus_message_iter_close_container (&iter, &sub); + // hints dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "{sv}", &sub); + + { + + // image data needs to be inserted here, under image-data key + // NOTE: it's not guaranteed that image-data hint is supported by all notification daemons + + const char v_image_data[] = "image-data"; + dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &v_image_data); + + DBusMessageIter value_sub; + + dbus_message_iter_open_container (&sub, DBUS_TYPE_VARIANT, "(iiibiiay)", &value_sub); + + { + + DBusMessageIter image_sub; + dbus_message_iter_open_container (&value_sub, DBUS_TYPE_STRUCT, NULL, &image_sub); + + { + + DBusMessageIter data_sub; + + dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &image_data.width); + dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &image_data.height); + dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &image_data.stride); + dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_BOOLEAN, &image_data.has_alpha); + dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &image_data.bits_per_sample); + dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &image_data.channels); + + dbus_message_iter_open_container (&image_sub, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &data_sub); + { dbus_message_iter_append_fixed_array (&data_sub, DBUS_TYPE_BYTE, &image_data.bytes, 16); } + + dbus_message_iter_close_container (&image_sub, &data_sub); + } + + dbus_message_iter_close_container (&value_sub, &image_sub); + } + + dbus_message_iter_close_container (&sub, &value_sub); + } + dbus_message_iter_close_container (&iter, &sub); dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &v_timeout); diff --git a/premake5.lua b/premake5.lua index a539391bec..3f2ddacbde 100644 --- a/premake5.lua +++ b/premake5.lua @@ -558,7 +558,7 @@ project "notify_plugin" files { "plugins/notify/notify.c" } - pkgconfig ("dbus-1") + pkgconfig ("dbus-1", "glib-2.0", "gio-2.0", "gdk-pixbuf-2.0") buildoptions {"-fblocks"} links {"dispatch", "BlocksRuntime"} end