Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement #392, take 2 #674

Merged
merged 10 commits into from
Dec 18, 2019
25 changes: 23 additions & 2 deletions .valgrind.suppressions
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,30 @@
fun:g_error_new_valist
fun:g_set_error
obj:*/librsvg-2.so*
fun:rsvg_handle_close
obj:*/loaders/libpixbufloader-svg.so
fun:gdk_pixbuf_loader_close
fun:gdk_pixbuf_get_file_info
fun:get_pixbuf_from_file
...
}

# same as above, but as occurs in CI environment
{
rsvg_error_handle_close2
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:g_malloc
fun:g_slice_alloc
fun:g_error_new_valist
fun:g_set_error
obj:*/librsvg-2.so*
obj:*/librsvg-2.so*
obj:*/loaders/libpixbufloader-svg.so
obj:*/libgdk_pixbuf-2.0.so*
fun:gdk_pixbuf_new_from_file
fun:gdk_pixbuf_loader_close
fun:gdk_pixbuf_get_file_info
fun:get_pixbuf_from_file
...
}
Expand All @@ -45,7 +65,8 @@
fun:rsvg_handle_write
obj:*/loaders/libpixbufloader-svg.so
obj:*/libgdk_pixbuf-2.0.so*
fun:gdk_pixbuf_new_from_file
fun:gdk_pixbuf_loader_close
fun:gdk_pixbuf_get_file_info
fun:get_pixbuf_from_file
...
}
1 change: 1 addition & 0 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ struct settings defaults = {

.browser = "/usr/bin/firefox",

.min_icon_size = 0,
.max_icon_size = 0,

/* paths to default icons */
Expand Down
18 changes: 16 additions & 2 deletions docs/dunst.pod
Original file line number Diff line number Diff line change
Expand Up @@ -384,14 +384,28 @@ ACTIONS below for further details.
Defines the position of the icon in the notification window. Setting it to off
disables icons.

=item B<min_icon_size> (default: 0)

Defines the minimum size in pixels for the icons.
If the icon is larger than or equal to the specified value it won't be affected.
If it's smaller then it will be scaled up so that the smaller axis is equivalent
to the specified size.

Set to 0 to disable icon upscaling. (default)

If B<icon_position> is set to off, this setting is ignored.

=item B<max_icon_size> (default: 0)

Defines the maximum size in pixels for the icons.
If the icon is smaller than the specified value it won't be affected.
If the icon is smaller than or equal to the specified value it won't be affected.
If it's larger then it will be scaled down so that the larger axis is equivalent
to the specified size.

Set to 0 to disable icon scaling. (default)
Set to 0 to disable icon downscaling. (default)

If both B<min_icon_size> and B<max_icon_size> are enabled, the latter
gets the last say.

If B<icon_position> is set to off, this setting is ignored.

Expand Down
5 changes: 5 additions & 0 deletions dunstrc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@
# Align icons left/right/off
icon_position = off

# Scale small icons up to this size, set to 0 to disable. Helpful
# for e.g. small files or high-dpi screens. In case of conflict,
# max_icon_size takes precedence over this.
min_icon_size = 0

# Scale larger icons down to this size, set to 0 to disable
max_icon_size = 32

Expand Down
68 changes: 56 additions & 12 deletions src/icon.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,25 +110,56 @@ cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf)
return icon_surface;
}

GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
/**
* Scales the given image dimensions if necessary according to the settings.
*
* @param w a pointer to the image width, to be modified in-place
* @param h a pointer to the image height, to be modified in-place
* @return TRUE if the dimensions were updated, FALSE if they were left unchanged
*/
static bool icon_size_clamp(int *w, int *h) {
int _w = *w, _h = *h;
int landscape = _w > _h;
int orig_larger = landscape ? _w : _h;
double larger = orig_larger;
double smaller = landscape ? _h : _w;
if (settings.min_icon_size && smaller < settings.min_icon_size) {
larger = larger / smaller * settings.min_icon_size;
smaller = settings.min_icon_size;
}
if (settings.max_icon_size && larger > settings.max_icon_size) {
smaller = smaller / larger * settings.max_icon_size;
larger = settings.max_icon_size;
}
if ((int) larger != orig_larger) {
*w = (int) (landscape ? larger : smaller);
*h = (int) (landscape ? smaller : larger);
return TRUE;
}
return FALSE;
}

/**
* Scales the given GdkPixbuf if necessary according to the settings.
*
* @param pixbuf (nullable) The pixbuf, which may be too big.
* Takes ownership of the reference.
* @return the scaled version of the pixbuf. If scaling wasn't
* necessary, it returns the same pixbuf. Transfers full
* ownership of the reference.
*/
static GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf)
{
ASSERT_OR_RET(pixbuf, NULL);

int w = gdk_pixbuf_get_width(pixbuf);
int h = gdk_pixbuf_get_height(pixbuf);
int larger = w > h ? w : h;
if (settings.max_icon_size && larger > settings.max_icon_size) {
int scaled_w = settings.max_icon_size;
int scaled_h = settings.max_icon_size;
if (w >= h)
scaled_h = (settings.max_icon_size * h) / w;
else
scaled_w = (settings.max_icon_size * w) / h;

if (icon_size_clamp(&w, &h)) {
GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
pixbuf,
scaled_w,
scaled_h,
w,
h,
GDK_INTERP_BILINEAR);
g_object_unref(pixbuf);
pixbuf = scaled;
Expand All @@ -141,8 +172,19 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename)
{
char *path = string_to_path(g_strdup(filename));
GError *error = NULL;
gint w, h;

GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error);
if (!gdk_pixbuf_get_file_info (path, &w, &h)) {
LOG_W("Failed to load image info for %s", filename);
g_free(path);
return NULL;
tsipinakis marked this conversation as resolved.
Show resolved Hide resolved
}
icon_size_clamp(&w, &h);
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
w,
h,
TRUE,
&error);

if (error) {
LOG_W("%s", error->message);
Expand Down Expand Up @@ -320,6 +362,8 @@ GdkPixbuf *icon_get_for_data(GVariant *data, char **id)
g_free(data_chk);
g_variant_unref(data_variant);

pixbuf = icon_pixbuf_scale(pixbuf);

return pixbuf;
}

Expand Down
19 changes: 4 additions & 15 deletions src/icon.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,7 @@

cairo_surface_t *gdk_pixbuf_to_cairo_surface(GdkPixbuf *pixbuf);

/**
* Scales the given GdkPixbuf if necessary according to the settings.
*
* @param pixbuf (nullable) The pixbuf, which may be too big.
* Takes ownership of the reference.
* @return the scaled version of the pixbuf. If scaling wasn't
* necessary, it returns the same pixbuf. Transfers full
* ownership of the reference.
*/
GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf);

/** Retrieve an icon by its full filepath.
/** Retrieve an icon by its full filepath, scaled according to settings.
*
* @param filename A string representing a readable file path
*
Expand All @@ -28,7 +17,7 @@ GdkPixbuf *icon_pixbuf_scale(GdkPixbuf *pixbuf);
*/
GdkPixbuf *get_pixbuf_from_file(const char *filename);

/** Retrieve an icon by its name sent via the notification bus
/** Retrieve an icon by its name sent via the notification bus, scaled according to settings
*
* @param iconname A string describing a `file://` URL, an arbitary filename
* or an icon name, which then gets searched for in the
Expand All @@ -39,7 +28,7 @@ GdkPixbuf *get_pixbuf_from_file(const char *filename);
*/
GdkPixbuf *get_pixbuf_from_icon(const char *iconname);

/** Read an icon from disk and convert it to a GdkPixbuf.
/** Read an icon from disk and convert it to a GdkPixbuf, scaled according to settings
*
* The returned id will be a unique identifier. To check if two given
* GdkPixbufs are equal, it's sufficient to just compare the id strings.
Expand All @@ -54,7 +43,7 @@ GdkPixbuf *get_pixbuf_from_icon(const char *iconname);
*/
GdkPixbuf *icon_get_for_name(const char *name, char **id);

/** Convert a GVariant like described in GdkPixbuf
/** Convert a GVariant like described in GdkPixbuf, scaled according to settings
*
* The returned id will be a unique identifier. To check if two given
* GdkPixbufs are equal, it's sufficient to just compare the id strings.
Expand Down
2 changes: 0 additions & 2 deletions src/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,6 @@ void notification_icon_replace_path(struct notification *n, const char *new_icon
g_clear_pointer(&n->icon_id, g_free);

n->icon = icon_get_for_name(new_icon, &n->icon_id);
n->icon = icon_pixbuf_scale(n->icon);
}

void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
Expand All @@ -264,7 +263,6 @@ void notification_icon_replace_data(struct notification *n, GVariant *new_icon)
g_clear_pointer(&n->icon_id, g_free);

n->icon = icon_get_for_data(new_icon, &n->icon_id);
n->icon = icon_pixbuf_scale(n->icon);
}

/* see notification.h */
Expand Down
6 changes: 6 additions & 0 deletions src/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,12 @@ void load_settings(char *cmdline_config_path)
g_free(c);
}

settings.min_icon_size = option_get_int(
"global",
"min_icon_size", "-min_icon_size", defaults.min_icon_size,
"Scale smaller icons up to this size, set to 0 to disable. If max_icon_size also specified, that has the final say."
);

settings.max_icon_size = option_get_int(
"global",
"max_icon_size", "-max_icon_size", defaults.max_icon_size,
Expand Down
1 change: 1 addition & 0 deletions src/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct settings {
char *browser;
char **browser_cmd;
enum icon_position icon_position;
int min_icon_size;
int max_icon_size;
char *icon_path;
enum follow_mode f_mode;
Expand Down
25 changes: 3 additions & 22 deletions test/dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gio/gio.h>

#include "helpers.h"
#include "queues.h"

extern const char *base;
Expand Down Expand Up @@ -252,33 +253,13 @@ bool dbus_notification_fire(struct dbus_notification *n, uint *id)

void dbus_notification_set_raw_image(struct dbus_notification *n_dbus, const char *path)
{
GdkPixbuf *pb = gdk_pixbuf_new_from_file(path, NULL);

if (!pb)
GVariant *hint = notification_setup_raw_image(path);
if (!hint)
return;

GVariant *hint_data = g_variant_new_from_data(
G_VARIANT_TYPE("ay"),
gdk_pixbuf_read_pixels(pb),
gdk_pixbuf_get_byte_length(pb),
TRUE,
(GDestroyNotify) g_object_unref,
g_object_ref(pb));

GVariant *hint = g_variant_new(
"(iiibii@ay)",
gdk_pixbuf_get_width(pb),
gdk_pixbuf_get_height(pb),
gdk_pixbuf_get_rowstride(pb),
gdk_pixbuf_get_has_alpha(pb),
gdk_pixbuf_get_bits_per_sample(pb),
gdk_pixbuf_get_n_channels(pb),
hint_data);

g_hash_table_insert(n_dbus->hints,
g_strdup("image-data"),
g_variant_ref_sink(hint));
g_object_unref(pb);
}

/////// TESTS
Expand Down
35 changes: 35 additions & 0 deletions test/helpers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <gdk-pixbuf/gdk-pixbuf.h>

#include "helpers.h"

GVariant *notification_setup_raw_image(const char *path)
{
GdkPixbuf *pb = gdk_pixbuf_new_from_file(path, NULL);

if (!pb)
return NULL;

GVariant *hint_data = g_variant_new_from_data(
G_VARIANT_TYPE("ay"),
gdk_pixbuf_read_pixels(pb),
gdk_pixbuf_get_byte_length(pb),
TRUE,
(GDestroyNotify) g_object_unref,
g_object_ref(pb));

GVariant *hint = g_variant_new(
"(iiibii@ay)",
gdk_pixbuf_get_width(pb),
gdk_pixbuf_get_height(pb),
gdk_pixbuf_get_rowstride(pb),
gdk_pixbuf_get_has_alpha(pb),
gdk_pixbuf_get_bits_per_sample(pb),
gdk_pixbuf_get_n_channels(pb),
hint_data);

g_object_unref(pb);

return hint;
}

/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
9 changes: 9 additions & 0 deletions test/helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef DUNST_TEST_HELPERS_H
#define DUNST_TEST_HELPERS_H

#include <glib.h>

GVariant *notification_setup_raw_image(const char *path);

#endif
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
Loading