Skip to content

Commit

Permalink
notify: scaled image via image-data hint
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleksiy-Yakovenko committed Dec 29, 2023
1 parent 401c399 commit 494c032
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 14 deletions.
11 changes: 10 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
33 changes: 26 additions & 7 deletions plugins/gtkui/covermanager/covermanager.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions plugins/notify/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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
197 changes: 194 additions & 3 deletions plugins/notify/notify.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "../../gettext.h"
#include "../artwork/artwork.h"

#define min(x, y) ((x) < (y) ? (x) : (y))
#define MAX_ALBUM_ART_FILE_SIZE (40 * 1024 * 1024)
#define E_NOTIFICATION_BUS_NAME "org.freedesktop.Notifications"
#define E_NOTIFICATION_INTERFACE "org.freedesktop.Notifications"
#define E_NOTIFICATION_PATH "/org/freedesktop/Notifications"
Expand Down Expand Up @@ -163,6 +166,122 @@ _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 *
_create_scaled_image (GdkPixbuf *image, int width, int height) {
int originalWidth = gdk_pixbuf_get_width (image);
int originalHeight = gdk_pixbuf_get_height (image);

if (originalWidth <= width && originalHeight <= height) {
g_object_ref (image);
return image;
}

gboolean has_alpha = gdk_pixbuf_get_has_alpha (image);
int bits_per_sample = gdk_pixbuf_get_bits_per_sample (image);

GdkPixbuf *scaled_image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, bits_per_sample, width, height);

double scale_x = (double)width / (double)originalWidth;
double scale_y = (double)height / (double)originalHeight;

gdk_pixbuf_scale (image, scaled_image, 0, 0, width, height, 0, 0, scale_x, scale_y, GDK_INTERP_BILINEAR);

return scaled_image;
}

static void
_desired_size_for_image_size (
int image_width,
int image_height,
int avail_width,
int avail_height,
int *result_width,
int *result_height) {
double scale = min ((double)avail_width / (double)image_width, (double)avail_height / (double)image_height);

*result_width = image_width * scale;
*result_height = image_height * scale;
}

static GdkPixbuf *
_load_image (const char *image_filename) {
GdkPixbuf *img = NULL;

long size = 0;
char *buf = _buffer_from_file (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 == NULL) {
return NULL;
}

const int max_image_size = conf_get_int ("notify.albumart_size", 64);

// downscale
int orig_width = gdk_pixbuf_get_width (img);
int orig_height = gdk_pixbuf_get_height (img);

if (orig_width > max_image_size || orig_height > max_image_size) {
int new_width = max_image_size;
int new_height = max_image_size;

int result_width;
int result_height;

_desired_size_for_image_size (orig_width, orig_height, new_width, new_height, &result_width, &result_height);

GdkPixbuf *scaled_img = _create_scaled_image (img, result_width, result_height);
g_object_unref (img);
img = scaled_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];
Expand Down Expand Up @@ -205,14 +324,21 @@ show_notification (DB_playItem_t *track, char *image_filename, dbus_uint32_t rep
bool should_wait_for_cover = !image_filename && deadbeef->conf_get_int ("notify.albumart", 0) && artwork_plugin;
bool should_apply_kde_fix = deadbeef->conf_get_int ("notify.fix_kde_5_23_5", 0) ? true : false;

// KDE won't re-display notification via reuse,
// 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 (image_filename);
if (!img) {
v_iconname = "deadbeef";
}

DBusMessageIter iter, sub;

dbus_message_append_args (
msg,
DBUS_TYPE_STRING,
Expand All @@ -227,13 +353,78 @@ show_notification (DB_playItem_t *track, char *image_filename, dbus_uint32_t rep
&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);

if (img != NULL) {
dbus_int32_t width = gdk_pixbuf_get_width (img);
dbus_int32_t height = gdk_pixbuf_get_height (img);
dbus_int32_t stride = gdk_pixbuf_get_rowstride (img);
dbus_bool_t has_alpha = gdk_pixbuf_get_has_alpha (img);
dbus_int32_t bits_per_sample = gdk_pixbuf_get_bits_per_sample (img);
dbus_int32_t channels = gdk_pixbuf_get_n_channels (img);
guchar *image_bytes = gdk_pixbuf_get_pixels (img);

DBusMessageIter dict_entry_sub;
dbus_message_iter_open_container (&sub, DBUS_TYPE_DICT_ENTRY, 0, &dict_entry_sub);

{
char *v_image_data = "image-data";
dbus_message_iter_append_basic (&dict_entry_sub, DBUS_TYPE_STRING, &v_image_data);

DBusMessageIter value_sub;

dbus_message_iter_open_container (&dict_entry_sub, DBUS_TYPE_VARIANT, "(iiibiiay)", &value_sub);

{

DBusMessageIter image_sub;
dbus_message_iter_open_container (&value_sub, DBUS_TYPE_STRUCT, NULL, &image_sub);

{

dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &width);
dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &height);
dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &stride);
dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_BOOLEAN, &has_alpha);
dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &bits_per_sample);
dbus_message_iter_append_basic (&image_sub, DBUS_TYPE_INT32, &channels);

DBusMessageIter data_sub;

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_bytes,
stride * height);
}

dbus_message_iter_close_container (&image_sub, &data_sub);
}

dbus_message_iter_close_container (&value_sub, &image_sub);
}

dbus_message_iter_close_container (&dict_entry_sub, &value_sub);
}

dbus_message_iter_close_container (&sub, &dict_entry_sub);

g_object_unref (img);
}

dbus_message_iter_close_container (&iter, &sub);

dbus_message_iter_append_basic (&iter, DBUS_TYPE_INT32, &v_timeout);
Expand Down
2 changes: 1 addition & 1 deletion premake5.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 494c032

Please sign in to comment.