Control: tags 1019755 + patch
Control: tags 1019755 + pending

Dear maintainer,

I've prepared an NMU for dunst (versioned as 1.9.0-0.1) and
uploaded it to DELAYED/15. Please feel free to tell me if I
should delay it longer.

Regards,
Boyuan Yang
diff -Nru dunst-1.8.1/CHANGELOG.md dunst-1.9.0/CHANGELOG.md
--- dunst-1.8.1/CHANGELOG.md	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/CHANGELOG.md	2022-06-27 08:43:39.000000000 -0400
@@ -1,5 +1,39 @@
 # Dunst changelog
 
+## 1.9.0 -- 2022-06-27
+
+### Added
+- `override_dbus_timeout` setting to override the notification timeout set via
+  dbus. (#1035)
+- Support notification gaps via the `gap_size` setting. Note that since the
+  notifications are not separate windows, you cannot click in between the
+  notifications. (#1053)
+- Make `min_icon_size` and `max_icon_size` a rule for even more flexibility
+  (#1069)
+
+### Changed
+- The window offset is now scaled according to `scale` as well. This way
+  notification stay visually in the same place on higher DPI screens. (#1039)
+- For the recursive icon lookup, revert to using `min_icon_size` and
+  `max_icon_size` instead of `icon_size`. `min_icon_size` is used as the size to
+  look for in icon themes. This way of defining icon size is more flexible and
+  compatible with the old icon lookup. The new icon lookup should now be
+  superior for all use cases. (#1069)
+- Recursive icon lookup is no longer experimental.
+- Recursive icon lookup is enabled in the default dunstrc. This does not change
+  your settings when you have a custom dunstrc.
+
+### Fixed
+- Added back the `action_name` setting that was accidentally dropped. (#1051)
+- Broken `dunstctl history`. (#1060)
+- Merged a few wayland fixes from mako (https://github.com/emersion/mako)
+  (#1067)
+- `follow=keyboard`: Fix regression where we don't fall back to mouse (#1062)
+- Raw icons not being scaled according to icon size (#1043)
+- Notifications not disappearing. For some people notifications would sometimes
+  stay on screen until a new notification appeared. This should not happen
+  anymore (#1073).
+
 ## 1.8.1 -- 2022-03-02
 
 ### Fixed
diff -Nru dunst-1.8.1/debian/changelog dunst-1.9.0/debian/changelog
--- dunst-1.8.1/debian/changelog	2022-06-11 04:42:31.000000000 -0400
+++ dunst-1.9.0/debian/changelog	2022-11-25 14:04:01.000000000 -0500
@@ -1,3 +1,10 @@
+dunst (1.9.0-0.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * New upstream release. (Closes: #1019755)
+
+ -- Boyuan Yang <by...@debian.org>  Fri, 25 Nov 2022 14:04:01 -0500
+
 dunst (1.8.1-1) unstable; urgency=medium
 
   [ Debian Janitor ]
diff -Nru dunst-1.8.1/docs/dunst.5.pod dunst-1.9.0/docs/dunst.5.pod
--- dunst-1.8.1/docs/dunst.5.pod	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/docs/dunst.5.pod	2022-06-27 08:43:39.000000000 -0400
@@ -206,7 +206,8 @@
 =item B<separator_height> (default: 2)
 
 The height in pixels of the separator between notifications, if set to 0 there
-will be no separating line between notifications.
+will be no separating line between notifications. This setting will be ignored
+if B<gap_size> is greater than 0.
 
 =item B<padding> (default: 8)
 
@@ -240,6 +241,16 @@
 Defines width in pixels of frame around the notification window. Set to 0 to
 disable.
 
+=item B<gap_size> (default: 0)
+
+Size of gap to display between notifications.
+
+If value is greater than 0, B<separator_height> will be ignored and a border
+of size B<frame_width> will be drawn around each notification instead.
+
+This setting requires a compositor and any applications displayed between the
+gaps are not currently clickable.
+
 =item B<separator_color> (values: [auto/foreground/frame/#RRGGBB] default: frame)
 
 Sets the color of the separator line between two notifications.
@@ -328,6 +339,9 @@
 If any of the following strings are present, they will be replaced with the
 equivalent notification attribute.
 
+For a complete markup reference, see
+<https://docs.gtk.org/Pango/pango_markup.html>.
+
 =over 4
 
 =item B<%a>  appname
@@ -385,31 +399,6 @@
 Show an indicator if a notification contains actions and/or open-able URLs. See
 ACTIONS below for further details.
 
-=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: 32)
-
-Defines the maximum size in pixels for the icons.
-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 downscaling.
-
-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.
-
 =item B<icon_path> (default: "/usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/")
 
 Can be set to a colon-separated list of paths to search for icons to use with
@@ -419,7 +408,7 @@
 system, see B<enable_recursive_icon_lookup>. This new system will eventually
 replace this and will need new settings.
 
-=item B<icon_theme> (default: "Adwaita", example: "Adwaita, breeze") I<Experimental>
+=item B<icon_theme> (default: "Adwaita", example: "Adwaita, breeze")
 
 Comma-separated of names of the the themes to use for looking up icons. This has
 to be the name of the directory in which the theme is located, not the
@@ -439,19 +428,17 @@
 This setting is experimental and not enabled by default. See
 B<enable_recursive_icon_lookup> for how to enable it.
 
-=item B<enable_recursive_icon_lookup> (default: false) I<Experimental>
+=item B<enable_recursive_icon_lookup> (default: false)
 
 This setting enables the new icon lookup method. This new system will eventually
 be the old icon lookup.
 
-Currently icons are looked up in the B<icon_path> and scaled according to
-B<min_icon_size> and B<max_icon_size>. Since the B<icon_path> wasn't recursive,
-one had to add a ton of paths to this list.
+Currently icons are looked up in the B<icon_path>. Since the B<icon_path> wasn't
+recursive, one had to add a ton of paths to this list.
 This has been drastically simplified by the new lookup method. Now you only have
-to set B<icon_theme> to the name of the theme and B<icon_size> to the icon size
-you want. To enable this new behaviour, set B<enable_recursive_icon_lookup> to
-true in the I<[experimental]> section. See the respective settings for more
-details.
+to set B<icon_theme> to the name of the theme you want. To enable this new
+behaviour, set B<enable_recursive_icon_lookup> to true in the I<[experimental]>
+section. See the respective settings for more details.
 
 =item B<sticky_history> (values: [true/false], default: true)
 
@@ -768,6 +755,13 @@
 
 See C<set_transient> for more details about this attribute.
 
+=item C<match_dbus_timeout>
+
+Matches the dbus timeout of the notification as set by the client or by some
+other rule.
+
+See C<override_dbus_timeout> for more details about this attribute.
+
 =item C<msg_urgency>
 
 Matches the urgency of the notification as set by the client or by some other
@@ -855,6 +849,33 @@
 
 Default: show
 
+=item B<min_icon_size> (default: 32)
+
+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.
+
+When using recursive icon lookup (see B<enable_recursive_icon_lookup>), all
+icons from a theme will be this size.
+
+If B<icon_position> is set to off, this setting is ignored.
+
+=item B<max_icon_size> (default: 128)
+
+Defines the maximum size in pixels for the icons.
+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 downscaling.
+
+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.
+
+
 =item C<new_icon>
 
 Updates the icon of the notification, it should be a path or a name for a valid
@@ -901,6 +922,11 @@
 
 Equivalent to the C<timeout> setting in the urgency sections.
 
+=item C<override_dbus_timeout>
+
+Overrides the timeout specified in dbus.
+This takes precedence over C<timeout>.
+
 =item C<urgency>
 
 This sets the notification urgency.
@@ -991,17 +1017,6 @@
 
 =back
 
-=item B<icon_size> (default: 32) I<Experimental>
-
-The size of the icon in pixels. This is commonly a multiple of 2, for example:
-16, 32 or 64. This size is used for searching the right icon in B<icon_theme>.
-If no icon of the right size can be found, no icon is displayed. When passing a
-full icon path to dunst the icon will be used even when it's not the right
-size. The icon is then scaled to be of size B<icon_size>.
-
-This setting is experimental and not enabled by default. See
-B<enable_recursive_icon_lookup> for how to enable it.
-
 =back
 
 As with the filtering attributes, each one corresponds to
diff -Nru dunst-1.8.1/dunstctl dunst-1.9.0/dunstctl
--- dunst-1.8.1/dunstctl	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/dunstctl	2022-06-27 08:43:39.000000000 -0400
@@ -138,7 +138,7 @@
 			|| die "Dunst controlling interface not available. Is the version too old?"
 		;;
 	"history")
-		busctl --user --json=pretty -l --no-pager call org.freedesktop.Notifications /org/freedesktop/Notifications org.dunstproject.cmd0 NotificationListHistory 2>/dev/null \
+		busctl --user --json=pretty --no-pager call org.freedesktop.Notifications /org/freedesktop/Notifications org.dunstproject.cmd0 NotificationListHistory 2>/dev/null \
 			|| die "Dunst is not running."
 		;;
 	"")
diff -Nru dunst-1.8.1/dunstrc dunst-1.9.0/dunstrc
--- dunst-1.8.1/dunstrc	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/dunstrc	2022-06-27 08:43:39.000000000 -0400
@@ -73,6 +73,7 @@
     # Draw a line of "separator_height" pixel height between two
     # notifications.
     # Set to 0 to disable.
+    # If gap_size is greater than 0, this setting will be ignored.
     separator_height = 2
 
     # Padding between text and separator.
@@ -91,6 +92,12 @@
     # Defines color of the frame around the notification window.
     frame_color = "#aaaaaa"
 
+    # Size of gap to display between notifications - requires a compositor.
+    # If value is greater than 0, separator_height will be ignored and a border
+    # of size frame_width will be drawn around each notification instead.
+    # Click events on gaps do not currently propagate to applications below.
+    gap_size = 0
+
     # Define a color for the separator.
     # possible values are:
     #  * auto: dunst tries to find a color fitting to the background;
@@ -184,18 +191,27 @@
 
     ### Icons ###
 
+    # Recursive icon lookup. You can set a single theme, instead of having to
+    # define all lookup paths.
+    enable_recursive_icon_lookup = true
+
+    # Set icon theme (only used for recursive icon lookup)
+    icon_theme = Adwaita
+    # You can also set multiple icon themes, with the leftmost one being used first.
+    # icon_theme = "Adwaita, breeze"
+
     # Align icons left/right/top/off
     icon_position = left
 
     # 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
+    min_icon_size = 32
 
     # Scale larger icons down to this size, set to 0 to disable
-    max_icon_size = 32
+    max_icon_size = 128
 
-    # Paths to default icons.
+    # Paths to default icons (only neccesary when not using recursive icon lookup)
     icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
 
     ### History ###
diff -Nru dunst-1.8.1/HACKING.md dunst-1.9.0/HACKING.md
--- dunst-1.8.1/HACKING.md	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/HACKING.md	2022-06-27 08:43:39.000000000 -0400
@@ -2,6 +2,78 @@
 
 **You can generate an internal overview with doxygen. For this, use `make doc-doxygen` and you'll find an internal overview of all functions and symbols in `docs/internal/html`. You will also need `graphviz` for this.**
 
+
+For people wanting to develop new features or fix bugs for dunst, here are the
+steps you should take.
+
+# Running dunst
+
+For building dunst, you should take a look at the README. After dunst is build,
+you can run it with:
+
+        ./dunst
+
+This might not work, however, since dunst will abort when another instance of
+dunst or another notification daemon is running. You will see a message like:
+
+        CRITICAL: [dbus_cb_name_lost:1044] Cannot acquire 'org.freedesktop.Notifications': Name is acquired by 'dunst' with PID '20937'.
+
+So it's best to kill any running instance of dunst before trying to run the
+version you just built. You can do that by making a shell function as follows
+and put it in your bashrc/zshrc/config.fish:
+
+```sh
+run_dunst() {
+        if make -j dunst; then
+                pkill dunst
+        else
+                return 1
+        fi
+        ./dunst $@
+}
+```
+
+If you run this function is the root directory of the repository, it will build
+dunst, kill any running instances and run your freshly built version of dunst.
+
+# Testing dunst
+
+To test dunst, it's good to know the following commands. This way you can test
+dunst on your local system and you don't have to wait for CI to finish.
+
+## Run test suite
+
+This will build dunst if there were any changes and run the test suite. You will
+need `awk` for this to work (to color the output of the tests).
+
+        make test
+
+## Run memory leak tests
+
+This will build dunst if there were any changes and run the test suite with
+valgrind to make sure there aren't any memory leaks. You will have to build your
+tests so that they free all allocated memory after you are done, otherwise this
+test will fail. You will need to have `valgrind` installed for this.
+
+        make test-valgrind
+
+
+## Build the doxygen documentation
+
+The internal documentation can be built with (`doxygen` and `graphviz` required):
+
+        make doc-doxygen
+
+To open them in your browser you can run something like:
+
+        firefox docs/internal/html/index.html
+
+
+# Running the tests with docker
+
+Dunst has a few docker images for running tests on different distributions. The
+documentation for this can be found at https://github.com/dunst-project/docker-images
+
 # Comments
 
 - Comment system is held similar to JavaDoc
diff -Nru dunst-1.8.1/Makefile dunst-1.9.0/Makefile
--- dunst-1.8.1/Makefile	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/Makefile	2022-06-27 08:43:39.000000000 -0400
@@ -3,7 +3,7 @@
 
 include config.mk
 
-VERSION := "1.8.1 (2022-03-02)"
+VERSION := "1.9.0 (2022-06-27)"
 ifneq ($(wildcard ./.git/),)
 VERSION := $(shell ${GIT} describe --tags)
 endif
diff -Nru dunst-1.8.1/RELEASE_NOTES dunst-1.9.0/RELEASE_NOTES
--- dunst-1.8.1/RELEASE_NOTES	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/RELEASE_NOTES	2022-06-27 08:43:39.000000000 -0400
@@ -1,4 +1,17 @@
 ===================================================================================
+Release Notes For v1.9.0
+===================================================================================
+
+This release marks the point for a few big features to be useable. The
+recursive icon lookup is marked stable and is used by default for new users. It
+is now also possible to add gaps between notifications, although it is not done
+with separate windows, so clicks in between notification will not register to
+the below window. You'll also need a compositor for the transparancy to take
+effect.
+
+Take a look at the changelog for a more detailed change description.
+
+===================================================================================
 Release Notes For v1.8.0
 ===================================================================================
 
diff -Nru dunst-1.8.1/src/dbus.c dunst-1.9.0/src/dbus.c
--- dunst-1.8.1/src/dbus.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/dbus.c	2022-06-27 08:43:39.000000000 -0400
@@ -528,6 +528,7 @@
         }
 
         GVariant *dict_value;
+        GVariant *icon_value = NULL;
 
         // First process the items that can be filtered on
         if ((dict_value = g_variant_lookup_value(hints, "urgency", G_VARIANT_TYPE_BYTE))) {
@@ -600,15 +601,30 @@
         if (!dict_value)
                 dict_value = g_variant_lookup_value(hints, "icon_data", G_VARIANT_TYPE("(iiibiiay)"));
         if (dict_value) {
-                notification_icon_replace_data(n, dict_value);
-                g_variant_unref(dict_value);
+                // Signal that the notification is still waiting for a raw
+                // icon. It cannot be set now, because min_icon_size and
+                // max_icon_size aren't known yet. It cannot be set later,
+                // because it has to be overwritten by the new_icon rule.
+                n->receiving_raw_icon = true;
+                icon_value = dict_value;
+                dict_value = NULL;
         }
 
+        // Set the dbus timeout
+        if (timeout >= 0)
+                n->dbus_timeout = ((gint64)timeout) * 1000;
+
         // All attributes that have to be set before initializations are set,
         // so we can initialize the notification. This applies all rules that
         // are defined and applies the formatting to the message.
         notification_init(n);
 
+        if (icon_value) {
+                if (n->receiving_raw_icon)
+                        notification_icon_replace_data(n, icon_value);
+                g_variant_unref(icon_value);
+        }
+
         // Modify these values after the notification is initialized and all rules are applied.
         if ((dict_value = g_variant_lookup_value(hints, "fgcolor", G_VARIANT_TYPE_STRING))) {
                 g_free(n->colors.fg);
@@ -633,9 +649,6 @@
                 g_variant_unref(dict_value);
         }
 
-        if (timeout >= 0)
-                n->timeout = ((gint64)timeout) * 1000;
-
         g_variant_unref(hints);
         g_variant_type_free(required_type);
         g_free(actions); // the strv is only a shallow copy
diff -Nru dunst-1.8.1/src/draw.c dunst-1.9.0/src/draw.c
--- dunst-1.8.1/src/draw.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/draw.c	2022-06-27 08:43:39.000000000 -0400
@@ -293,12 +293,10 @@
 
 static struct dimensions calculate_dimensions(GSList *layouts)
 {
+        int layout_count = g_slist_length(layouts);
         struct dimensions dim = { 0 };
         double scale = output->get_scale();
 
-        dim.h += 2 * settings.frame_width;
-        dim.h += (g_slist_length(layouts) - 1) * settings.separator_height;
-
         dim.corner_radius = settings.corner_radius;
 
         for (GSList *iter = layouts; iter; iter = iter->next) {
@@ -319,6 +317,16 @@
                 dim.w = max_width;
         }
 
+        if (settings.gap_size) {
+                int extra_frame_height = layout_count * (2 * settings.frame_width);
+                int extra_gap_height = (layout_count * settings.gap_size) - settings.gap_size;
+                int total_extra_height = extra_frame_height + extra_gap_height;
+                dim.h += total_extra_height;
+        } else {
+                dim.h += 2 * settings.frame_width;
+                dim.h += (layout_count - 1) * settings.separator_height;
+        }
+
         return dim;
 }
 
@@ -782,15 +790,19 @@
         if (first)
                 dim.y += settings.frame_width;
 
-        if (!last)
-                dim.y += settings.separator_height;
-
+        if (last)
+                dim.y += settings.frame_width;
 
         if ((2 * settings.padding + cl_h) < settings.height)
                 dim.y += cl_h + 2 * settings.padding;
         else
                 dim.y += settings.height;
 
+        if (settings.gap_size)
+                dim.y += settings.gap_size;
+        else
+                dim.y += settings.separator_height;
+
         cairo_destroy(c);
         cairo_surface_destroy(content);
         return dim;
@@ -810,12 +822,12 @@
                 case ORIGIN_TOP_LEFT:
                 case ORIGIN_LEFT_CENTER:
                 case ORIGIN_BOTTOM_LEFT:
-                        *ret_x = scr->x + settings.offset.x;
+                        *ret_x = scr->x + round(settings.offset.x * draw_get_scale());
                         break;
                 case ORIGIN_TOP_RIGHT:
                 case ORIGIN_RIGHT_CENTER:
                 case ORIGIN_BOTTOM_RIGHT:
-                        *ret_x = scr->x + (scr->w - width) - settings.offset.x;
+                        *ret_x = scr->x + (scr->w - width) - round(settings.offset.x * draw_get_scale());
                         break;
                 case ORIGIN_TOP_CENTER:
                 case ORIGIN_CENTER:
@@ -830,12 +842,12 @@
                 case ORIGIN_TOP_LEFT:
                 case ORIGIN_TOP_CENTER:
                 case ORIGIN_TOP_RIGHT:
-                        *ret_y = scr->y + settings.offset.y;
+                        *ret_y = scr->y + round(settings.offset.y * draw_get_scale());
                         break;
                 case ORIGIN_BOTTOM_LEFT:
                 case ORIGIN_BOTTOM_CENTER:
                 case ORIGIN_BOTTOM_RIGHT:
-                        *ret_y = scr->y + (scr->h - height) - settings.offset.y;
+                        *ret_y = scr->y + (scr->h - height) - round(settings.offset.y * draw_get_scale());
                         break;
                 case ORIGIN_LEFT_CENTER:
                 case ORIGIN_CENTER:
@@ -861,12 +873,19 @@
                                                                     round(dim.h * scale));
 
         bool first = true;
+        bool last;
         for (GSList *iter = layouts; iter; iter = iter->next) {
 
                 struct colored_layout *cl_this = iter->data;
                 struct colored_layout *cl_next = iter->next ? iter->next->data : NULL;
+                last = !cl_next;
+
+                if (settings.gap_size) {
+                        first = true;
+                        last = true;
+                }
 
-                dim = layout_render(image_surface, cl_this, cl_next, dim, first, !cl_next);
+                dim = layout_render(image_surface, cl_this, cl_next, dim, first, last);
 
                 first = false;
         }
diff -Nru dunst-1.8.1/src/dunst.c dunst-1.9.0/src/dunst.c
--- dunst-1.8.1/src/dunst.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/dunst.c	2022-06-27 08:43:39.000000000 -0400
@@ -66,19 +66,21 @@
         }
 
         LOG_D("Waking up");
-        run(NULL);
+        run(GINT_TO_POINTER(1));
 }
 
 static gboolean run(void *data)
 {
         static gint64 next_timeout = 0;
+        int reason = GPOINTER_TO_INT(data);
 
-        LOG_D("RUN");
+        LOG_D("RUN, reason %i", reason);
+        gint64 now = time_monotonic_now();
 
         dunst_status(S_FULLSCREEN, output->have_fullscreen_window());
         dunst_status(S_IDLE, output->is_idle());
 
-        queues_update(status);
+        queues_update(status, now);
 
         bool active = queues_length_displayed() > 0;
 
@@ -91,14 +93,13 @@
         }
 
         if (active) {
-                gint64 now = time_monotonic_now();
                 gint64 sleep = queues_get_next_datachange(now);
                 gint64 timeout_at = now + sleep;
 
                 LOG_D("Sleeping for %li ms", sleep/1000);
 
                 if (sleep >= 0) {
-                        if (next_timeout < now || timeout_at < next_timeout) {
+                        if (reason == 0 || next_timeout < now || timeout_at < next_timeout) {
                                 g_timeout_add(sleep/1000, run, NULL);
                                 next_timeout = timeout_at;
                         }
diff -Nru dunst-1.8.1/src/icon.c dunst-1.9.0/src/icon.c
--- dunst-1.8.1/src/icon.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/icon.c	2022-06-27 08:43:39.000000000 -0400
@@ -122,21 +122,23 @@
  *
  * @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
+ * @param min_size the minimum icon size setting for this notification
+ * @param max_size the maximum icon size setting for this notification
  * @return TRUE if the dimensions were updated, FALSE if they were left unchanged
  */
-static bool icon_size_clamp(int *w, int *h) {
+static bool icon_size_clamp(int *w, int *h, int min_size, int max_size) {
         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 (min_size && smaller < min_size) {
+                larger = larger / smaller * min_size;
+                smaller = min_size;
+        }
+        if (max_size && larger > max_size) {
+                smaller = smaller / larger * max_size;
+                larger = max_size;
         }
         if ((int) larger != orig_larger) {
                 *w = (int) (landscape ? larger : smaller);
@@ -146,67 +148,42 @@
         return FALSE;
 }
 
-static bool icon_size_clamp2(int *w, int *h, int desired_size, double scale) {
-        int largest_size = MAX(*w, *h);
-        int new_size = desired_size * scale;
-        if (largest_size != new_size) {
-                double scale = (double) new_size / (double) largest_size;
-                *w = round(*w * scale);
-                *h = round(*h * scale);
-                return true;
-        }
-        return false;
-}
 /**
  * Scales the given GdkPixbuf to a given size.. If the image is not square, the
  * largest size will be scaled up to the given size.
  *
- * The icon is scaled to a size of icon_size * dpi_scale.
- *
  * @param pixbuf (nullable) The pixbuf, which may be too big.
  *                          Takes ownership of the reference.
- * @param icon_size An integer the unscaled icon size.
  * @param dpi_scale A double for the dpi scaling.
+ * @param min_size The minimum allowed icon size.
+ * @param max_size The maximum allowed icon size.
  * @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_to_size(GdkPixbuf *pixbuf, int icon_size, double dpi_scale)
+static GdkPixbuf *icon_pixbuf_scale_to_size(GdkPixbuf *pixbuf, double dpi_scale, int min_size, int max_size)
 {
         ASSERT_OR_RET(pixbuf, NULL);
 
         int w = gdk_pixbuf_get_width(pixbuf);
         int h = gdk_pixbuf_get_height(pixbuf);
-        bool needs_change = false;
 
-        if (settings.enable_recursive_icon_lookup)
-        {
-                needs_change = icon_size_clamp2(&w, &h, icon_size, dpi_scale);
-                if (needs_change) {
-                        LOG_D("Scaling to a size of %ix%i", w, h);
-                        LOG_D("While the icon size and scale are %ix%f", icon_size, dpi_scale);
-                }
-        } else {
-                // TODO immediately rescale icon upon scale changes
-                if(icon_size_clamp(&w, &h)) {
-                        w = round(w * dpi_scale);
-                        h = round(h * dpi_scale);
-                        needs_change = true;
-                }
-        }
-        if (needs_change) {
-                GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
-                                pixbuf,
-                                w,
-                                h,
-                                GDK_INTERP_BILINEAR);
-                g_object_unref(pixbuf);
-                pixbuf = scaled;
-        }
+        // TODO immediately rescale icon upon scale changes
+        if(icon_size_clamp(&w, &h, min_size, max_size)) {
+                w = round(w * dpi_scale);
+                h = round(h * dpi_scale);
+        }
+        GdkPixbuf *scaled = gdk_pixbuf_scale_simple(
+                        pixbuf,
+                        w,
+                        h,
+                        GDK_INTERP_BILINEAR);
+        g_object_unref(pixbuf);
+        pixbuf = scaled;
         return pixbuf;
 }
 
-GdkPixbuf *get_pixbuf_from_file(const char *filename, int icon_size, double scale)
+GdkPixbuf *get_pixbuf_from_file(const char *filename, int min_size, int max_size, double scale)
 {
         char *path = string_to_path(g_strdup(filename));
         GError *error = NULL;
@@ -218,23 +195,13 @@
                 return NULL;
         }
         GdkPixbuf *pixbuf = NULL;
-        if (settings.enable_recursive_icon_lookup)
-        {
-                icon_size_clamp2(&w, &h, icon_size, scale);
-                pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
-                                w,
-                                h,
-                                TRUE,
-                                &error);
-        } else {
-                // TODO immediately rescale icon upon scale changes
-                icon_size_clamp(&w, &h);
-                pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
-                                round(w * scale),
-                                round(h * scale),
-                                TRUE,
-                                &error);
-        }
+        // TODO immediately rescale icon upon scale changes
+        icon_size_clamp(&w, &h, min_size, max_size);
+        pixbuf = gdk_pixbuf_new_from_file_at_scale(path,
+                        round(w * scale),
+                        round(h * scale),
+                        TRUE,
+                        &error);
 
         if (error) {
                 LOG_W("%s", error->message);
@@ -305,7 +272,7 @@
         return new_name;
 }
 
-GdkPixbuf *icon_get_for_data(GVariant *data, char **id, double dpi_scale, int icon_size)
+GdkPixbuf *icon_get_for_data(GVariant *data, char **id, double dpi_scale, int min_size, int max_size)
 {
         ASSERT_OR_RET(data, NULL);
         ASSERT_OR_RET(id, NULL);
@@ -415,7 +382,7 @@
         g_free(data_chk);
         g_variant_unref(data_variant);
 
-        pixbuf = icon_pixbuf_scale_to_size(pixbuf, icon_size, dpi_scale);
+        pixbuf = icon_pixbuf_scale_to_size(pixbuf, dpi_scale, min_size, max_size);
 
         return pixbuf;
 }
diff -Nru dunst-1.8.1/src/icon.h dunst-1.9.0/src/icon.h
--- dunst-1.8.1/src/icon.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/icon.h	2022-06-27 08:43:39.000000000 -0400
@@ -11,13 +11,14 @@
 /** Retrieve an icon by its full filepath, scaled according to settings.
  *
  * @param filename A string representing a readable file path
- * @param icon_size An iteger representing the desired unscaled icon size.
+ * @param min_size An iteger representing the desired minimum unscaled icon size.
+ * @param max_size An iteger representing the desired maximum unscaled icon size.
  * @param scale An integer representing the output dpi scaling.
  *
  * @return an instance of `GdkPixbuf`
  * @retval NULL: file does not exist, not readable, etc..
  */
-GdkPixbuf *get_pixbuf_from_file(const char *filename, int icon_size, double scale);
+GdkPixbuf *get_pixbuf_from_file(const char *filename, int min_size, int max_size, double scale);
 
 
 /**
@@ -55,12 +56,13 @@
  *             like described in the notification spec.
  * @param id   (necessary) A unique identifier of the returned pixbuf.
  *             Only filled, if the return value is non-NULL.
- * @param scale An integer representing the output dpi scaling.
- * @param icon_size An integer representing the desired unscaled icon size.
+ * @param dpi_scale An integer representing the output dpi scaling.
+ * @param min_size An integer representing the desired minimum unscaled icon size.
+ * @param max_size An integer representing the desired maximum unscaled icon size.
  * @return an instance of `GdkPixbuf` derived from the GVariant
  * @retval NULL: GVariant parameter nulled, invalid or in wrong format
  */
-GdkPixbuf *icon_get_for_data(GVariant *data, char **id, double scale, int icon_size);
+GdkPixbuf *icon_get_for_data(GVariant *data, char **id, double dpi_scale, int min_size, int max_size);
 
 #endif
 /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
diff -Nru dunst-1.8.1/src/input.c dunst-1.9.0/src/input.c
--- dunst-1.8.1/src/input.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/input.c	2022-06-27 08:43:39.000000000 -0400
@@ -6,16 +6,43 @@
 #include <stddef.h>
 #include <linux/input-event-codes.h>
 
+int get_notification_clickable_height(struct notification *n, bool first, bool last)
+{
+        int notification_size = n->displayed_height;
+        if (settings.gap_size) {
+                notification_size += settings.frame_width * 2;
+        } else {
+                double half_separator = settings.separator_height / 2.0;
+                notification_size += settings.separator_height;
+                if(first)
+                    notification_size += (settings.frame_width - half_separator);
+                if(last)
+                    notification_size += (settings.frame_width - half_separator);
+        }
+        return notification_size;
+}
+
 struct notification *get_notification_at(const int y) {
-        int curr_y = settings.frame_width;
+        int curr_y = 0;
+        bool first = true;
+        bool last;
         for (const GList *iter = queues_get_displayed(); iter;
                         iter = iter->next) {
                 struct notification *current = iter->data;
-                if (y > curr_y && y < curr_y + current->displayed_height) {
+                struct notification *next = iter->next ? iter->next->data : NULL;
+
+                last = !next;
+                int notification_size = get_notification_clickable_height(current, first, last);
+
+                if (y >= curr_y && y < curr_y + notification_size) {
                         return current;
                 }
 
-                curr_y += current->displayed_height + settings.separator_height;
+                curr_y += notification_size;
+                if (settings.gap_size)
+                        curr_y += settings.gap_size;
+
+                first = false;
         }
         // no matching notification was found
         return NULL;
diff -Nru dunst-1.8.1/src/notification.c dunst-1.9.0/src/notification.c
--- dunst-1.8.1/src/notification.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/notification.c	2022-06-27 08:43:39.000000000 -0400
@@ -337,10 +337,11 @@
         g_clear_pointer(&n->icon_id, g_free);
 
         g_free(n->icon_path);
-        n->icon_path = get_path_from_icon_name(new_icon, n->icon_size);
+        n->icon_path = get_path_from_icon_name(new_icon, n->min_icon_size);
         if (n->icon_path) {
                 GdkPixbuf *pixbuf = get_pixbuf_from_file(n->icon_path,
-                                n->icon_size, draw_get_scale());
+                                n->min_icon_size, n->max_icon_size,
+                                draw_get_scale());
                 if (pixbuf) {
                         n->icon = gdk_pixbuf_to_cairo_surface(pixbuf);
                         g_object_unref(pixbuf);
@@ -360,7 +361,7 @@
         g_clear_pointer(&n->icon_id, g_free);
 
         GdkPixbuf *icon = icon_get_for_data(new_icon, &n->icon_id,
-                        draw_get_scale(), n->icon_size);
+                        draw_get_scale(), n->min_icon_size, n->max_icon_size);
         n->icon = gdk_pixbuf_to_cairo_surface(icon);
         if (icon)
                 g_object_unref(icon);
@@ -414,6 +415,7 @@
 
         n->urgency = URG_NORM;
         n->timeout = -1;
+        n->dbus_timeout = -1;
 
         n->transient = false;
         n->progress = -1;
@@ -423,7 +425,9 @@
         n->progress_bar_alignment = PANGO_ALIGN_CENTER;
         n->hide_text = false;
         n->icon_position = ICON_LEFT;
-        n->icon_size = 32;
+        n->min_icon_size = 32;
+        n->max_icon_size = 32;
+        n->receiving_raw_icon = false;
 
         n->script_run = false;
         n->dbus_valid = false;
@@ -506,6 +510,11 @@
         /* UPDATE derived fields */
         notification_extract_urls(n);
         notification_format_message(n);
+
+        /* Update timeout: dbus_timeout has priority over timeout */
+        if (n->dbus_timeout >= 0)
+                n->timeout = n->dbus_timeout;
+
 }
 
 static void notification_format_message(struct notification *n)
diff -Nru dunst-1.8.1/src/notification.h dunst-1.9.0/src/notification.h
--- dunst-1.8.1/src/notification.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/notification.h	2022-06-27 08:43:39.000000000 -0400
@@ -64,12 +64,15 @@
                                    Use this to compare the icon name with rules. May also be modified by rules.*/
         char *icon_path;         /**< Full path to the notification's icon. */
         char *default_icon_name; /**< The icon that is used when no other icon is available. */
-        int icon_size;           /**< Size of the icon used for searching the right icon. */
+        int min_icon_size;  /**< Minimum icon size. Also used for looking up icon names. */
+        int max_icon_size; /**< Maximum icon size. */
         enum icon_position icon_position;       /**< Icon position (enum left,right,top,off). */
+        bool receiving_raw_icon; /**< Still waiting for raw icon to be received */
 
         gint64 start;      /**< begin of current display (in milliseconds) */
         gint64 timestamp;  /**< arrival time (in milliseconds) */
         gint64 timeout;    /**< time to display (in milliseconds) */
+        gint64 dbus_timeout; /**< time to display (in milliseconds) (set by dbus) */
         int locked;     /**< If non-zero the notification is locked **/
         PangoAlignment progress_bar_alignment; /**< Horizontal alignment of the progress bar **/
 
diff -Nru dunst-1.8.1/src/queues.c dunst-1.9.0/src/queues.c
--- dunst-1.8.1/src/queues.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/queues.c	2022-06-27 08:43:39.000000000 -0400
@@ -134,10 +134,11 @@
  *
  * @param n the notification to check
  * @param status the current status of dunst
+ * @param time the current time
  * @retval true: the notification is timed out
  * @retval false: otherwise
  */
-static bool queues_notification_is_finished(struct notification *n, struct dunst_status status)
+static bool queues_notification_is_finished(struct notification *n, struct dunst_status status, gint64 time)
 {
         assert(n);
 
@@ -156,7 +157,7 @@
         }
 
         /* remove old message */
-        if (time_monotonic_now() - n->start > n->timeout) {
+        if (time - n->start > n->timeout) {
                 return true;
         }
 
@@ -408,7 +409,7 @@
 }
 
 /* see queues.h */
-void queues_update(struct dunst_status status)
+void queues_update(struct dunst_status status, gint64 time)
 {
         GList *iter, *nextiter;
 
@@ -433,7 +434,7 @@
                 }
 
 
-                if (queues_notification_is_finished(n, status)){
+                if (queues_notification_is_finished(n, status, time)){
                         queues_notification_close(n, REASON_TIME);
                         iter = nextiter;
                         continue;
diff -Nru dunst-1.8.1/src/queues.h dunst-1.9.0/src/queues.h
--- dunst-1.8.1/src/queues.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/queues.h	2022-06-27 08:43:39.000000000 -0400
@@ -141,8 +141,9 @@
  *       (which closes old and shows new notifications on screen)
  *
  * @param status the current status of dunst
+ * @param time the current time
  */
-void queues_update(struct dunst_status status);
+void queues_update(struct dunst_status status, gint64 time);
 
 /**
  * Calculate the distance to the next event, when an element in the
diff -Nru dunst-1.8.1/src/rules.c dunst-1.9.0/src/rules.c
--- dunst-1.8.1/src/rules.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/rules.c	2022-06-27 08:43:39.000000000 -0400
@@ -21,6 +21,8 @@
 {
         if (r->timeout != -1)
                 n->timeout = r->timeout;
+        if (r->override_dbus_timeout != -1)
+                n->dbus_timeout = r->override_dbus_timeout;
         if (r->urgency != URG_NONE)
                 n->urgency = r->urgency;
         if (r->fullscreen != FS_NULL)
@@ -41,6 +43,10 @@
                 n->hide_text = r->hide_text;
         if (r->progress_bar_alignment != -1)
                 n->progress_bar_alignment = r->progress_bar_alignment;
+        if (r->min_icon_size != -1)
+                n->min_icon_size = r->min_icon_size;
+        if (r->max_icon_size != -1)
+                n->max_icon_size = r->max_icon_size;
         if (r->action_name) {
                 g_free(n->default_action_name);
                 n->default_action_name = g_strdup(r->action_name);
@@ -53,8 +59,6 @@
                 n->markup = r->markup;
         if (r->icon_position != -1)
                 n->icon_position = r->icon_position;
-        if (r->set_icon_size > 0)
-                n->icon_size = r->set_icon_size;
         if (r->fg) {
                 g_free(n->colors.fg);
                 n->colors.fg = g_strdup(r->fg);
@@ -83,6 +87,7 @@
                 // separate variable is needed to track if the icon is
                 // replaced, like in 86cbc1d34bb0f551461dbd466cd9e4860ae01817.
                 notification_icon_replace_path(n, r->new_icon);
+                n->receiving_raw_icon = false;
         }
         if (r->script){
                 n->scripts = g_renew(const char*,n->scripts,n->script_count + 1);
@@ -190,6 +195,7 @@
 {
         return  r->enabled
                 && (r->msg_urgency == URG_NONE || r->msg_urgency == n->urgency)
+                && (r->match_dbus_timeout < 0 || (r->match_dbus_timeout == n->dbus_timeout))
                 && (r->match_transient == -1 || (r->match_transient == n->transient))
                 && rule_field_matches_string(n->appname,        r->appname)
                 && rule_field_matches_string(n->desktop_entry,  r->desktop_entry)
diff -Nru dunst-1.8.1/src/rules.h dunst-1.9.0/src/rules.h
--- dunst-1.8.1/src/rules.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/rules.h	2022-06-27 08:43:39.000000000 -0400
@@ -26,9 +26,11 @@
         char *stack_tag;
         char *desktop_entry;
         int msg_urgency;
+        gint64 match_dbus_timeout;
 
         /* modifying */
         gint64 timeout; // this has to be the first modifying rule
+        gint64 override_dbus_timeout;
         enum urgency urgency;
         char *action_name;
         enum markup_mode markup;
@@ -41,7 +43,8 @@
         int alignment;
         int hide_text;
         int icon_position;
-        int set_icon_size;
+        int min_icon_size;
+        int max_icon_size;
         char *new_icon;
         char *fg;
         char *bg;
diff -Nru dunst-1.8.1/src/settings.c dunst-1.9.0/src/settings.c
--- dunst-1.8.1/src/settings.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/settings.c	2022-06-27 08:43:39.000000000 -0400
@@ -203,29 +203,31 @@
                 }
         }
 
+        // TODO Implement this with icon sizes as rules
+
         // restrict the icon size to a reasonable limit if we have a fixed width.
         // Otherwise the layout will be broken by too large icons.
         // See https://github.com/dunst-project/dunst/issues/540
-        if (s->width.max > 0) {
-                const int icon_size_limit = s->width.max / 2;
-                if (   s->max_icon_size == 0
-                    || s->max_icon_size > icon_size_limit) {
-                        if (s->max_icon_size != 0) {
-                                LOG_W("Max width was set to %d but got a max_icon_size of %d, too large to use. Setting max_icon_size=%d",
-                                        s->width.max, s->max_icon_size, icon_size_limit);
-                        } else {
-                                LOG_I("Max width was set but max_icon_size is unlimited. Limiting icons to %d pixels", icon_size_limit);
-                        }
+        // if (s->width.max > 0) {
+        //         const int icon_size_limit = s->width.max / 2;
+        //         if (   s->max_icon_size == 0
+        //             || s->max_icon_size > icon_size_limit) {
+        //                 if (s->max_icon_size != 0) {
+        //                         LOG_W("Max width was set to %d but got a max_icon_size of %d, too large to use. Setting max_icon_size=%d",
+        //                                 s->width.max, s->max_icon_size, icon_size_limit);
+        //                 } else {
+        //                         LOG_I("Max width was set but max_icon_size is unlimited. Limiting icons to %d pixels", icon_size_limit);
+        //                 }
 
-                        s->max_icon_size = icon_size_limit;
-                }
-        }
+        //                 s->max_icon_size = icon_size_limit;
+        //         }
+        // }
 
-        int text_icon_padding = settings.text_icon_padding != 0 ? settings.text_icon_padding : settings.h_padding;
-        int max_text_width = settings.width.max - settings.max_icon_size - text_icon_padding - 2 * settings.h_padding;
-        if (max_text_width < 10) {
-                DIE("max_icon_size and horizontal padding are too large for the given width");
-        }
+        // int text_icon_padding = settings.text_icon_padding != 0 ? settings.text_icon_padding : settings.h_padding;
+        // int max_text_width = settings.width.max - settings.max_icon_size - text_icon_padding - 2 * settings.h_padding;
+        // if (max_text_width < 10) {
+        //         DIE("max_icon_size and horizontal padding are too large for the given width");
+        // }
 
 }
 
diff -Nru dunst-1.8.1/src/settings_data.h dunst-1.9.0/src/settings_data.h
--- dunst-1.8.1/src/settings_data.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/settings_data.h	2022-06-27 08:43:39.000000000 -0400
@@ -96,6 +96,16 @@
        *        .rule_offset = offsetof(struct rule, *member*);
        */
       size_t rule_offset;
+
+      /**
+       * True if a setting has a different default in the default dunstrc.
+       * This is useful to transition a default value without breaking exisitng
+       * configs. This value is needed for the test suite to skip testing this
+       * setting against the default dunstrc.
+       *
+       * False by default.
+       */
+      bool different_default;
 };
 
 
@@ -125,19 +135,21 @@
 static const struct rule empty_rule = {
         .name            = "empty",
         .appname         = NULL,
+        .action_name     = NULL,
         .summary         = NULL,
         .body            = NULL,
         .icon            = NULL,
         .category        = NULL,
         .msg_urgency     = URG_NONE,
+        .match_dbus_timeout = -1,
         .timeout         = -1,
+        .override_dbus_timeout       = -1,
         .urgency         = URG_NONE,
         .markup          = MARKUP_NULL,
         .history_ignore  = -1,
         .match_transient = -1,
         .set_transient   = -1,
         .icon_position   = -1,
-        .set_icon_size   = -1,
         .skip_display    = -1,
         .word_wrap       = -1,
         .ellipsize       = -1,
@@ -151,6 +163,8 @@
         .script          = NULL,
         .enabled         = true,
         .progress_bar_alignment   = -1,
+        .min_icon_size   = -1,
+        .max_icon_size   = -1,
 };
 
 
@@ -415,6 +429,17 @@
                 .rule_offset = offsetof(struct rule, msg_urgency),
         },
         {
+                .name = "match_dbus_timeout",
+                .section = "*",
+                .description = "Matches the dbus_timeout of the notification",
+                .type = TYPE_TIME,
+                .default_value = "*",
+                .value = NULL,
+                .parser = NULL,
+                .parser_data = NULL,
+                .rule_offset = offsetof(struct rule, match_dbus_timeout),
+        },
+        {
                 .name = "stack_tag",
                 .section = "*",
                 .description = "Matches the stack tag of the notification as set by the client or by some other rule.",
@@ -461,6 +486,17 @@
                 .rule_offset = offsetof(struct rule, bg),
         },
         {
+                .name = "action_name",
+                .section = "*",
+                .description = "Sets the name of the action to be invoked on do_action.",
+                .type = TYPE_STRING,
+                .default_value = "*",
+                .value = NULL,
+                .parser = NULL,
+                .parser_data = NULL,
+                .rule_offset = offsetof(struct rule, action_name),
+        },
+        {
                 .name = "foreground",
                 .section = "*",
                 .description = "The foreground color of the notification.",
@@ -571,6 +607,17 @@
                 .rule_offset = offsetof(struct rule, timeout),
         },
         {
+                .name = "override_dbus_timeout",
+                .section = "*",
+                .description = "Replace the dbus timeout with this value.",
+                .type = TYPE_TIME,
+                .default_value = "*",
+                .value = NULL,
+                .parser = NULL,
+                .parser_data = NULL,
+                .rule_offset = offsetof(struct rule, override_dbus_timeout),
+        },
+        {
                 .name = "urgency",
                 .section = "*",
                 .description = "This sets the notification urgency.",
@@ -670,17 +717,6 @@
                 .rule_offset = offsetof(struct rule, icon_position),
         },
         {
-                .name = "icon_size",
-                .section = "*",
-                .description = "Set the size of the icon",
-                .type = TYPE_INT,
-                .default_value = "*",
-                .value = NULL,
-                .parser = NULL,
-                .parser_data = NULL,
-                .rule_offset = offsetof(struct rule, set_icon_size),
-        },
-        {
                 .name = "enabled",
                 .section = "*",
                 .description = "Enable or disable a rule",
@@ -702,6 +738,28 @@
                 .parser_data = horizontal_alignment_enum_data,
                 .rule_offset = offsetof(struct rule, progress_bar_alignment),
         },
+        {
+                .name = "min_icon_size",
+                .section = "global",
+                .description = "Scale smaller icons up to this size, set to 0 to disable. If max_icon_size also specified, that has the final say.",
+                .type = TYPE_INT,
+                .default_value = "*",
+                .value = NULL,
+                .parser = NULL,
+                .parser_data = NULL,
+                .rule_offset = offsetof(struct rule, min_icon_size),
+        },
+        {
+                .name = "max_icon_size",
+                .section = "global",
+                .description = "Scale larger icons down to this size, set to 0 to disable",
+                .type = TYPE_INT,
+                .default_value = "*",
+                .value = NULL,
+                .parser = NULL,
+                .parser_data = NULL,
+                .rule_offset = offsetof(struct rule, max_icon_size),
+        },
         // end of modifying rules
 
         // other settings below
@@ -1047,26 +1105,6 @@
                 .parser_data = &settings.browser_cmd,
         },
         {
-                .name = "min_icon_size",
-                .section = "global",
-                .description = "Scale smaller icons up to this size, set to 0 to disable. If max_icon_size also specified, that has the final say.",
-                .type = TYPE_INT,
-                .default_value = "0",
-                .value = &settings.min_icon_size,
-                .parser = NULL,
-                .parser_data = NULL,
-        },
-        {
-                .name = "max_icon_size",
-                .section = "global",
-                .description = "Scale larger icons down to this size, set to 0 to disable",
-                .type = TYPE_INT,
-                .default_value = "32",
-                .value = &settings.max_icon_size,
-                .parser = NULL,
-                .parser_data = NULL,
-        },
-        {
                 .name = "always_run_script",
                 .section = "global",
                 .description = "Always run rule-defined scripts, even if the notification is suppressed with format = \"\".",
@@ -1186,6 +1224,7 @@
                 .value = &settings.enable_recursive_icon_lookup,
                 .parser = string_parse_bool,
                 .parser_data = boolean_enum_data,
+                .different_default = true,
         },
         {
                 .name = "enable_posix_regex",
@@ -1453,6 +1492,16 @@
                 .parser = NULL,
                 .parser_data = NULL,
         },
+        {
+                .name = "gap_size",
+                .section = "global",
+                .description = "Size of gap between notifications",
+                .type = TYPE_INT,
+                .default_value = "0",
+                .value = &settings.gap_size,
+                .parser = NULL,
+                .parser_data = NULL,
+        },
 };
 #endif
 /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */
diff -Nru dunst-1.8.1/src/settings.h dunst-1.9.0/src/settings.h
--- dunst-1.8.1/src/settings.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/settings.h	2022-06-27 08:43:39.000000000 -0400
@@ -127,8 +127,6 @@
         char *browser;
         char **browser_cmd;
         enum vertical_alignment vertical_alignment;
-        int min_icon_size;
-        int max_icon_size;
         char **icon_theme; // experimental
         bool enable_recursive_icon_lookup; // experimental
         bool enable_regex; // experimental
@@ -156,6 +154,7 @@
         int height;
         struct position offset;
         int notification_limit;
+        int gap_size;
 };
 
 extern struct settings settings;
diff -Nru dunst-1.8.1/src/wayland/wl.c dunst-1.9.0/src/wayland/wl.c
--- dunst-1.8.1/src/wayland/wl.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/wayland/wl.c	2022-06-27 08:43:39.000000000 -0400
@@ -135,17 +135,27 @@
                 LOG_E("allocation failed");
                 return;
         }
+
+        bool recreate_surface = false;
         static int number = 0;
         LOG_I("New output found - id %i", number);
         output->global_name = global_name;
         output->wl_output = wl_output;
         output->scale = 1;
         output->fullscreen = false;
+
+        recreate_surface = wl_list_empty(&ctx.outputs);
+
         wl_list_insert(&ctx.outputs, &output->link);
 
         wl_output_set_user_data(wl_output, output);
         wl_output_add_listener(wl_output, &output_listener, output);
         number++;
+
+        if (recreate_surface) {
+                // We had no outputs, force our surface to redraw
+                set_dirty(ctx.surface);
+        }
 }
 
 static void destroy_output(struct dunst_output *output) {
@@ -280,13 +290,19 @@
 static void layer_surface_handle_configure(void *data,
                 struct zwlr_layer_surface_v1 *surface,
                 uint32_t serial, uint32_t width, uint32_t height) {
+        zwlr_layer_surface_v1_ack_configure(surface, serial);
+
+        if (ctx.configured &&
+                        ctx.width == (int32_t) width &&
+                        ctx.height == (int32_t) height) {
+                wl_surface_commit(ctx.surface);
+                return;
+        }
+
         ctx.configured = true;
         ctx.width = width;
         ctx.height = height;
 
-        // not needed as it is set somewhere else
-        /* zwlr_layer_surface_v1_set_size(surface, width, height);  */
-        zwlr_layer_surface_v1_ack_configure(surface, serial);
         send_frame();
 }
 
@@ -589,6 +605,11 @@
 static void send_frame() {
         int scale = wl_get_scale();
 
+        if (wl_list_empty(&ctx.outputs)) {
+                ctx.dirty = false;
+                return;
+        }
+
         struct dunst_output *output = get_configured_output();
         int height = ctx.cur_dim.h;
         int width = ctx.cur_dim.w;
diff -Nru dunst-1.8.1/src/x11/screen.c dunst-1.9.0/src/x11/screen.c
--- dunst-1.8.1/src/x11/screen.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/src/x11/screen.c	2022-06-27 08:43:39.000000000 -0400
@@ -389,12 +389,12 @@
  */
 static Window get_focused_window(void)
 {
-        Window focused;
+        Window focused, root = RootWindow(xctx.dpy, DefaultScreen(xctx.dpy));
         int ignored;
 
         XGetInputFocus(xctx.dpy, &focused, &ignored);
 
-        if (focused == None || focused == PointerRoot)
+        if (focused == None || focused == PointerRoot || focused == root)
                 focused = 0;
         return focused;
 }
diff -Nru dunst-1.8.1/test/data/dunstrc.default dunst-1.9.0/test/data/dunstrc.default
--- dunst-1.8.1/test/data/dunstrc.default	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/data/dunstrc.default	2022-06-27 08:43:39.000000000 -0400
@@ -73,6 +73,7 @@
     # Draw a line of "separator_height" pixel height between two
     # notifications.
     # Set to 0 to disable.
+    # If gap_size is greater than 0, this setting will be ignored.
     separator_height = 2
 
     # Padding between text and separator.
@@ -91,6 +92,12 @@
     # Defines color of the frame around the notification window.
     frame_color = "#aaaaaa"
 
+    # Size of gap to display between notifications - requires a compositor.
+    # If value is greater than 0, separator_height will be ignored and a border
+    # of size frame_width will be drawn around each notification instead.
+    # Click events on gaps do not currently propagate to applications below.
+    gap_size = 0
+
     # Define a color for the separator.
     # possible values are:
     #  * auto: dunst tries to find a color fitting to the background;
@@ -184,18 +191,27 @@
 
     ### Icons ###
 
+    # Recursive icon lookup. You can set a single theme, instead of having to
+    # define all lookup paths.
+    enable_recursive_icon_lookup = true
+
+    # Set icon theme (only used for recursive icon lookup)
+    icon_theme = Adwaita
+    # You can also set multiple icon themes, with the leftmost one being used first.
+    # icon_theme = "Adwaita, breeze"
+
     # Align icons left/right/top/off
     icon_position = left
 
     # 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
+    min_icon_size = 32
 
     # Scale larger icons down to this size, set to 0 to disable
-    max_icon_size = 32
+    max_icon_size = 128
 
-    # Paths to default icons.
+    # Paths to default icons (only neccesary when not using recursive icon lookup)
     icon_path = /usr/share/icons/gnome/16x16/status/:/usr/share/icons/gnome/16x16/devices/
 
     ### History ###
diff -Nru dunst-1.8.1/test/dbus.c dunst-1.9.0/test/dbus.c
--- dunst-1.8.1/test/dbus.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/dbus.c	2022-06-27 08:43:39.000000000 -0400
@@ -815,6 +815,91 @@
         PASS();
 }
 
+TEST test_override_dbus_timeout(void)
+{
+        struct notification *n;
+        struct dbus_notification *n_dbus;
+        struct rule *rule;
+
+        n_dbus = dbus_notification_new();
+        n_dbus->app_name = "dunstteststack";
+        n_dbus->expire_timeout = 2147484;
+
+        rule = rule_new("test_override_dbus_timeout");
+        rule->appname = "dunstteststack";
+        rule->override_dbus_timeout = 100000;
+
+        gint64 expected_timeout = rule->override_dbus_timeout;
+
+        guint id;
+        ASSERT(dbus_notification_fire(n_dbus, &id));
+        ASSERT(id != 0);
+
+        n = queues_debug_find_notification_by_id(id);
+        ASSERT_EQ_FMT(expected_timeout, n->timeout, "%" G_GINT64_FORMAT);
+
+        dbus_notification_free(n_dbus);
+        rule->enabled = false;
+
+        PASS();
+}
+
+TEST test_match_dbus_timeout(void)
+{
+        struct notification *n;
+        struct dbus_notification *n_dbus;
+        struct rule *rule;
+
+        n_dbus = dbus_notification_new();
+        n_dbus->app_name = "dunstteststack";
+        n_dbus->expire_timeout = 2147484;
+
+        rule = rule_new("test_match_dbus_timeout");
+        rule->match_dbus_timeout = 2147484000;
+        rule->override_dbus_timeout = 100000;
+
+        gint64 expected_timeout = rule->override_dbus_timeout;
+
+        guint id;
+        ASSERT(dbus_notification_fire(n_dbus, &id));
+        ASSERT(id != 0);
+
+        n = queues_debug_find_notification_by_id(id);
+        ASSERT_EQ_FMT(expected_timeout, n->timeout, "%" G_GINT64_FORMAT);
+
+        dbus_notification_free(n_dbus);
+        rule->enabled = false;
+
+        PASS();
+}
+
+TEST test_timeout(void)
+{
+        struct notification *n;
+        struct dbus_notification *n_dbus;
+        struct rule *rule;
+
+        n_dbus = dbus_notification_new();
+        n_dbus->app_name = "dunstteststack";
+
+        rule = rule_new("test_timeout");
+        rule->appname = "dunstteststack";
+        rule->timeout = 100001;
+
+        gint64 expected_timeout = rule->timeout;
+
+        guint id;
+        ASSERT(dbus_notification_fire(n_dbus, &id));
+        ASSERT(id != 0);
+
+        n = queues_debug_find_notification_by_id(id);
+        ASSERT_EQ_FMT(expected_timeout, n->timeout, "%" G_GINT64_FORMAT);
+
+        dbus_notification_free(n_dbus);
+        rule->enabled = false;
+
+        PASS();
+}
 
 // TESTS END
 
@@ -844,6 +929,9 @@
         RUN_TEST(test_close_and_signal);
         RUN_TEST(test_signal_actioninvoked);
         RUN_TEST(test_timeout_overflow);
+        RUN_TEST(test_override_dbus_timeout);
+        RUN_TEST(test_match_dbus_timeout);
+        RUN_TEST(test_timeout);
 
         RUN_TEST(assert_methodlists_sorted);
 
diff -Nru dunst-1.8.1/test/draw.c dunst-1.9.0/test/draw.c
--- dunst-1.8.1/test/draw.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/draw.c	2022-06-27 08:43:39.000000000 -0400
@@ -5,6 +5,8 @@
 
 cairo_t *c;
 
+double get_dummy_scale() { return 1; }
+
 const struct screen_info* noop_screen(void) {
         static struct screen_info i;
         return &i;
@@ -28,9 +30,64 @@
         x_is_idle,
         have_fullscreen_window,
 
-        x_get_scale,
+        get_dummy_scale,
 };
 
+GSList *get_dummy_layouts(GSList *notifications)
+{
+        GSList *layouts = NULL;
+
+        for (GSList *iter = notifications; iter; iter = iter->next) {
+                struct colored_layout *cl = layout_from_notification(c, iter->data);
+                layouts = g_slist_append(layouts, cl);
+
+        }
+        return layouts;
+}
+
+int get_small_max_height()
+{
+        // to keep test calculations simpler, set max height small to
+        // only test cases where height is not dynamically determined
+        // by notification content
+        // future tests targeting dynamic sizing logic could be added
+        // to address this limitation
+        int small_max_height = 10;
+        return small_max_height;
+}
+
+int get_expected_dimension_height(int layout_count)
+{
+        // assumes settings.height == notification height, see get_small_max_height
+        int separator_height = (layout_count - 1) * settings.separator_height;
+        int total_gap_size = (layout_count - 1) * settings.gap_size;
+        int height = settings.height * layout_count;
+        int frame_width_total_height;
+        int expected_height;
+        if(settings.gap_size) {
+                frame_width_total_height = layout_count * (2 * settings.frame_width);
+                expected_height = height + frame_width_total_height + total_gap_size;
+        } else {
+                frame_width_total_height = 2 * settings.frame_width;
+                expected_height = separator_height + height + frame_width_total_height;
+        }
+        return expected_height;
+}
+
+int get_expected_dimension_y_offset(int layout_count)
+{
+        // assumes settings.height == notification height, see get_small_max_height
+        int expected_y = layout_count * settings.height;
+        if(settings.gap_size) {
+                expected_y += (layout_count * (2 * settings.frame_width));
+                expected_y += (layout_count * settings.gap_size);
+        } else {
+                expected_y += (2 * settings.frame_width);
+                expected_y += (layout_count * settings.separator_height);
+        }
+        return expected_y;
+}
+
 TEST test_layout_from_notification(void)
 {
         struct notification *n = test_notification_with_icon("test", 10);
@@ -71,6 +128,179 @@
         PASS();
 }
 
+TEST test_calculate_dimensions_height_no_gaps(void)
+{
+        int original_height = settings.height;
+        bool orginal_gap_size = settings.gap_size;
+        settings.height = get_small_max_height();
+        settings.gap_size = 10;
+
+        int layout_count;
+        GSList *notifications;
+        GSList *layouts;
+        struct dimensions dim;
+        int expected_height;
+
+        layout_count = 1;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        expected_height = get_expected_dimension_height(layout_count);
+        ASSERT(dim.h == expected_height);
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+
+        layout_count = 2;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        expected_height = get_expected_dimension_height(layout_count);
+        ASSERT(dim.h == expected_height);
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+
+        layout_count = 3;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        expected_height = get_expected_dimension_height(layout_count);
+        ASSERT(dim.h == expected_height);
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+
+        settings.gap_size = orginal_gap_size;
+        settings.height = original_height;
+
+        PASS();
+}
+
+TEST test_calculate_dimensions_height_gaps(void)
+{
+        int original_height = settings.height;
+        bool orginal_gap_size = settings.gap_size;
+        settings.height = get_small_max_height();
+        settings.gap_size = 10;
+
+        int layout_count;
+        GSList *notifications;
+        GSList *layouts;
+        struct dimensions dim;
+        int expected_height;
+
+        layout_count = 1;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        expected_height = get_expected_dimension_height(layout_count);
+        ASSERT(dim.h == expected_height);
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+
+        layout_count = 2;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        expected_height = get_expected_dimension_height(layout_count);
+        ASSERT(dim.h == expected_height);
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+
+        layout_count = 3;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        expected_height = get_expected_dimension_height(layout_count);
+        ASSERT(dim.h == expected_height);
+
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+        settings.gap_size = orginal_gap_size;
+        settings.height = original_height;
+
+        PASS();
+}
+
+TEST test_layout_render_no_gaps(void)
+{
+        int original_height = settings.height;
+        bool orginal_gap_size = settings.gap_size;
+        settings.height = get_small_max_height();
+        settings.gap_size = 0;
+
+        int layout_count;
+        GSList *notifications;
+        GSList *layouts;
+        struct dimensions dim;
+        cairo_surface_t *image_surface;
+        int expected_y;
+
+        layout_count = 3;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+        bool first = true;
+        for (GSList *iter = layouts; iter; iter = iter->next) {
+                struct colored_layout *cl_this = iter->data;
+                struct colored_layout *cl_next = iter->next ? iter->next->data : NULL;
+
+                dim = layout_render(image_surface, cl_this, cl_next, dim, first, !cl_next);
+
+                first = false;
+        }
+
+        expected_y = get_expected_dimension_y_offset(layout_count);
+        ASSERT(dim.y == expected_y);
+
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+        cairo_surface_destroy(image_surface);
+        settings.gap_size = orginal_gap_size;
+        settings.height = original_height;
+
+        PASS();
+}
+
+TEST test_layout_render_gaps(void)
+{
+        int original_height = settings.height;
+        bool orginal_gap_size = settings.gap_size;
+        settings.height = get_small_max_height();
+        settings.gap_size = 10;
+
+        int layout_count;
+        GSList *notifications;
+        GSList *layouts;
+        struct dimensions dim;
+        cairo_surface_t *image_surface;
+        int expected_y;
+
+        layout_count = 3;
+        notifications = get_dummy_notifications(layout_count);
+        layouts = get_dummy_layouts(notifications);
+        dim = calculate_dimensions(layouts);
+        image_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
+
+        for (GSList *iter = layouts; iter; iter = iter->next) {
+                struct colored_layout *cl_this = iter->data;
+                struct colored_layout *cl_next = iter->next ? iter->next->data : NULL;
+
+                dim = layout_render(image_surface, cl_this, cl_next, dim, true, true);
+        }
+
+        expected_y = get_expected_dimension_y_offset(layout_count);
+        ASSERT(dim.y == expected_y);
+
+        g_slist_free_full(layouts, free_colored_layout);
+        g_slist_free_full(notifications, free_dummy_notification);
+        cairo_surface_destroy(image_surface);
+        settings.gap_size = orginal_gap_size;
+        settings.height = original_height;
+
+        PASS();
+}
+
 SUITE(suite_draw)
 {
         output = &dummy_output;
@@ -81,5 +311,9 @@
                         RUN_TEST(test_layout_from_notification);
                         RUN_TEST(test_layout_from_notification_icon_off);
                         RUN_TEST(test_layout_from_notification_no_icon);
+                        RUN_TEST(test_calculate_dimensions_height_no_gaps);
+                        RUN_TEST(test_calculate_dimensions_height_gaps);
+                        RUN_TEST(test_layout_render_no_gaps);
+                        RUN_TEST(test_layout_render_gaps);
         });
 }
diff -Nru dunst-1.8.1/test/functional-tests/dunstrc.gaps dunst-1.9.0/test/functional-tests/dunstrc.gaps
--- dunst-1.8.1/test/functional-tests/dunstrc.gaps	1969-12-31 19:00:00.000000000 -0500
+++ dunst-1.9.0/test/functional-tests/dunstrc.gaps	2022-06-27 08:43:39.000000000 -0400
@@ -0,0 +1,22 @@
+[urgency_low]
+    background = "#222222"
+    foreground = "#888888"
+    timeout = 10
+
+[urgency_normal]
+    background = "#285577"
+    foreground = "#ffffff"
+    timeout = 10
+
+[urgency_critical]
+    background = "#900000"
+    foreground = "#ffffff"
+    timeout = 0
+
+[global]
+    gap_size = 35
+    frame_width = 15
+
+    mouse_left_click = do_action
+    mouse_middle_click = close_current
+    mouse_right_click = context
diff -Nru dunst-1.8.1/test/functional-tests/dunstrc.separator_click dunst-1.9.0/test/functional-tests/dunstrc.separator_click
--- dunst-1.8.1/test/functional-tests/dunstrc.separator_click	1969-12-31 19:00:00.000000000 -0500
+++ dunst-1.9.0/test/functional-tests/dunstrc.separator_click	2022-06-27 08:43:39.000000000 -0400
@@ -0,0 +1,23 @@
+[urgency_low]
+    background = "#222222"
+    foreground = "#888888"
+    timeout = 10
+
+[urgency_normal]
+    background = "#285577"
+    foreground = "#ffffff"
+    timeout = 10
+
+[urgency_critical]
+    background = "#900000"
+    foreground = "#ffffff"
+    timeout = 0
+
+[global]
+    frame_width = 5
+    separator_height = 50
+    separator_color = "#000000"
+
+    mouse_left_click = do_action
+    mouse_middle_click = close_current
+    mouse_right_click = context
diff -Nru dunst-1.8.1/test/functional-tests/test.sh dunst-1.9.0/test/functional-tests/test.sh
--- dunst-1.8.1/test/functional-tests/test.sh	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/functional-tests/test.sh	2022-06-27 08:43:39.000000000 -0400
@@ -243,6 +243,40 @@
     keypress
 }
 
+function gaps {
+    echo "###################################"
+    echo "gaps"
+    echo "###################################"
+    start_dunst dunstrc.gaps
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #1" -u l) \
+        && echo Clicked $CHOICE for \#1 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #2" -u n) \
+        && echo Clicked $CHOICE for \#2 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #3" -u c) \
+        && echo Clicked $CHOICE for \#3 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #4" -u l) \
+        && echo Clicked $CHOICE for \#4 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #5" -u n) \
+        && echo Clicked $CHOICE for \#5 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #6" -u c) \
+        && echo Clicked $CHOICE for \#6 &
+    keypress
+}
+
+function separator_click {
+    echo "###################################"
+    echo "separator_click"
+    echo "###################################"
+    start_dunst dunstrc.separator_click
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #1" -u l) \
+        && echo Clicked $CHOICE for \#1 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #2" -u c) \
+        && echo Clicked $CHOICE for \#2 &
+    CHOICE=$(../../dunstify -a "dunst tester" -A "default,Default" -A "optional,Optional" "Click #3" -u n) \
+        && echo Clicked $CHOICE for \#3 &
+    keypress
+}
+
 if [ -n "$1" ]; then
     while [ -n "$1" ]; do
         $1
@@ -261,6 +295,8 @@
     progress_bar
     icon_position
     hide_text
+    gaps
+    separator_click
 fi
 
 killall dunst
diff -Nru dunst-1.8.1/test/helpers.c dunst-1.9.0/test/helpers.c
--- dunst-1.8.1/test/helpers.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/helpers.c	2022-06-27 08:43:39.000000000 -0400
@@ -67,4 +67,26 @@
         return n;
 }
 
+GSList *get_dummy_notifications(int count)
+{
+        GSList *notifications = NULL;
+
+        int message_size = 24;
+        for (int i = 0; i < count; i++) {
+                char msg[message_size];
+                snprintf(msg, message_size, "test %d", i);
+                struct notification *n = test_notification(msg, 10);
+                n->icon_position = ICON_LEFT;
+                n->text_to_render = g_strdup("dummy layout");
+                notifications = g_slist_append(notifications, n);
+        }
+        return notifications;
+}
+
+void free_dummy_notification(void *notification)
+{
+        // wrapper function to work with g_slist_free_full
+        notification_unref((struct notification *) notification);
+}
+
 /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
diff -Nru dunst-1.8.1/test/helpers.h dunst-1.9.0/test/helpers.h
--- dunst-1.8.1/test/helpers.h	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/helpers.h	2022-06-27 08:43:39.000000000 -0400
@@ -7,6 +7,8 @@
 struct notification *test_notification_uninitialized(const char *name);
 struct notification *test_notification(const char *name, gint64 timeout);
 struct notification *test_notification_with_icon(const char *name, gint64 timeout);
+GSList *get_dummy_notifications(int count);
+void free_dummy_notification(void *notification);
 
 #endif
 /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
diff -Nru dunst-1.8.1/test/icon.c dunst-1.9.0/test/icon.c
--- dunst-1.8.1/test/icon.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/icon.c	2022-06-27 08:43:39.000000000 -0400
@@ -27,7 +27,7 @@
 {
         const char *iconpath = ICONPATH;
 
-        gchar *path = g_build_filename(base, iconpath, "valid", "icon1.svg", NULL);
+        gchar *path = g_build_filename(base, iconpath, "16x16", "actions", "edit.png", NULL);
 
         char *result = get_path_from_icon_name(path, size);
         ASSERT(result);
@@ -38,10 +38,10 @@
         PASS();
 }
 
-TEST test_icon_size_clamp_too_small(void)
+TEST test_icon_size_clamp_too_small(int min_icon_size, int max_icon_size)
 {
         int w = 12, h = 24;
-        bool resized = icon_size_clamp(&w, &h);
+        bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
         ASSERT(resized);
         ASSERT_EQ(w, 16);
         ASSERT_EQ(h, 32);
@@ -49,10 +49,10 @@
         PASS();
 }
 
-TEST test_icon_size_clamp_not_necessary(void)
+TEST test_icon_size_clamp_not_necessary(int min_icon_size, int max_icon_size)
 {
         int w = 20, h = 30;
-        bool resized = icon_size_clamp(&w, &h);
+        bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
         ASSERT(!resized);
         ASSERT_EQ(w, 20);
         ASSERT_EQ(h, 30);
@@ -60,10 +60,10 @@
         PASS();
 }
 
-TEST test_icon_size_clamp_too_big(void)
+TEST test_icon_size_clamp_too_big(int min_icon_size, int max_icon_size)
 {
         int w = 75, h = 150;
-        bool resized = icon_size_clamp(&w, &h);
+        bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
         ASSERT(resized);
         ASSERT_EQ(w, 50);
         ASSERT_EQ(h, 100);
@@ -71,10 +71,10 @@
         PASS();
 }
 
-TEST test_icon_size_clamp_too_small_then_too_big(void)
+TEST test_icon_size_clamp_too_small_then_too_big(int min_icon_size, int max_icon_size)
 {
         int w = 8, h = 80;
-        bool resized = icon_size_clamp(&w, &h);
+        bool resized = icon_size_clamp(&w, &h, min_icon_size, max_icon_size);
         ASSERT(resized);
         ASSERT_EQ(w, 10);
         ASSERT_EQ(h, 100);
@@ -90,30 +90,19 @@
         printf("Icon path: %s\n", icon_path);
         RUN_TEST(test_get_path_from_icon_null);
         RUN_TEST(test_get_path_from_icon_name_full);
-        RUN_TEST(test_icon_size_clamp_not_necessary);
+        RUN_TESTp(test_icon_size_clamp_not_necessary, 0, 100);
 
-        settings.min_icon_size = 16;
-        settings.max_icon_size = 100;
+        RUN_TESTp(test_icon_size_clamp_too_small, 16, 100);
+        RUN_TESTp(test_icon_size_clamp_not_necessary, 16, 100);
+        RUN_TESTp(test_icon_size_clamp_too_big, 16, 100);
+        RUN_TESTp(test_icon_size_clamp_too_small_then_too_big, 16, 100);
 
-        RUN_TEST(test_icon_size_clamp_too_small);
-        RUN_TEST(test_icon_size_clamp_not_necessary);
-        RUN_TEST(test_icon_size_clamp_too_big);
-        RUN_TEST(test_icon_size_clamp_too_small_then_too_big);
+        RUN_TESTp(test_icon_size_clamp_too_small, 16, 0);
+        RUN_TESTp(test_icon_size_clamp_not_necessary, 16, 0);
 
-        settings.min_icon_size = 16;
-        settings.max_icon_size = 0;
+        RUN_TESTp(test_icon_size_clamp_not_necessary, 0, 100);
+        RUN_TESTp(test_icon_size_clamp_too_big, 0, 100);
 
-        RUN_TEST(test_icon_size_clamp_too_small);
-        RUN_TEST(test_icon_size_clamp_not_necessary);
-
-        settings.min_icon_size = 0;
-        settings.max_icon_size = 100;
-
-        RUN_TEST(test_icon_size_clamp_not_necessary);
-        RUN_TEST(test_icon_size_clamp_too_big);
-
-        settings.min_icon_size = 0;
-        settings.max_icon_size = 0;
         g_clear_pointer(&icon_path, g_free);
 }
 /* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
diff -Nru dunst-1.8.1/test/icon-lookup.c dunst-1.9.0/test/icon-lookup.c
--- dunst-1.8.1/test/icon-lookup.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/icon-lookup.c	2022-06-27 08:43:39.000000000 -0400
@@ -80,18 +80,11 @@
 
         ASSERT(n->icon);
 
-        void *old_icon = (void*) n->icon;
-        ASSERT(old_icon == n->icon);
+        int old_width = cairo_image_surface_get_width(n->icon);
         rule_apply(rule, n);
-        ASSERT(old_icon != n->icon);
-        /* n->icon = malloc(1); // allocate some data to emulate a raw icon */
+        ASSERT(old_width != cairo_image_surface_get_width(n->icon));
 
-        /* printf("%lu\n", sizeof(n->icon)); */
-
-
-        /* printf("%lu\n", sizeof(n->icon)); */
-
-        free(n->icon);
+        cairo_surface_destroy(n->icon);
         n->icon = NULL;
 
         notification_unref(n);
diff -Nru dunst-1.8.1/test/input.c dunst-1.9.0/test/input.c
--- dunst-1.8.1/test/input.c	1969-12-31 19:00:00.000000000 -0500
+++ dunst-1.9.0/test/input.c	2022-06-27 08:43:39.000000000 -0400
@@ -0,0 +1,161 @@
+#include "../src/input.c"
+#include "queues.h"
+#include "greatest.h"
+#include "helpers.h"
+#include "../src/utils.h"
+
+TEST test_get_notification_clickable_height_first(void)
+{
+        bool orginal_gap_size = settings.gap_size;
+        settings.gap_size = 0;
+
+        struct notification *n = test_notification("test", 10);
+        n->displayed_height = 12;
+
+        int expected_size = n->displayed_height + settings.frame_width;
+        expected_size += (settings.separator_height / 2.0);
+        int result = get_notification_clickable_height(n, true, false);
+
+        ASSERT(result == expected_size);
+
+        settings.gap_size = orginal_gap_size;
+        notification_unref(n);
+        PASS();
+}
+
+TEST test_get_notification_clickable_height_middle(void)
+{
+        bool orginal_gap_size = settings.gap_size;
+        settings.gap_size = 0;
+
+        struct notification *n = test_notification("test", 10);
+        n->displayed_height = 12;
+
+        int expected_size = n->displayed_height + settings.separator_height;
+        int result = get_notification_clickable_height(n, false, false);
+
+        ASSERT(result == expected_size);
+
+        settings.gap_size = orginal_gap_size;
+        notification_unref(n);
+        PASS();
+}
+
+TEST test_get_notification_clickable_height_last(void)
+{
+        bool orginal_gap_size = settings.gap_size;
+        settings.gap_size = 0;
+
+        struct notification *n = test_notification("test", 10);
+        n->displayed_height = 12;
+
+        int expected_size = n->displayed_height + settings.frame_width;
+        expected_size += (settings.separator_height / 2.0);
+        int result = get_notification_clickable_height(n, false, true);
+
+        ASSERT(result == expected_size);
+
+        settings.gap_size = orginal_gap_size;
+        notification_unref(n);
+        PASS();
+}
+
+TEST test_get_notification_clickable_height_gaps(void)
+{
+        bool orginal_gap_size = settings.gap_size;
+        settings.gap_size = 7;
+
+        struct notification *n = test_notification("test", 10);
+        n->displayed_height = 12;
+
+        int expected_size = n->displayed_height + (settings.frame_width * 2);
+
+        int result_first = get_notification_clickable_height(n, true, false);
+        ASSERT(result_first == expected_size);
+
+        int result_middle = get_notification_clickable_height(n, false, false);
+        ASSERT(result_middle == expected_size);
+
+        int result_last = get_notification_clickable_height(n, false, true);
+        ASSERT(result_last == expected_size);
+
+        settings.gap_size = orginal_gap_size;
+        notification_unref(n);
+        PASS();
+}
+
+TEST test_notification_at(void)
+{
+        int total_notifications = 3;
+        GSList *notifications = get_dummy_notifications(total_notifications);
+
+        queues_init();
+
+        int display_height = 12;
+        struct notification *n;
+        for (GSList *iter = notifications; iter; iter = iter->next) {
+                n = iter->data;
+                n->displayed_height = display_height;
+                queues_notification_insert(n);
+        }
+
+        queues_update(STATUS_NORMAL, time_monotonic_now());
+
+        struct notification *top_notification = g_slist_nth_data(notifications, 0);
+        int top_notification_height = get_notification_clickable_height(top_notification, true, false);
+
+        struct notification *middle_notification = g_slist_nth_data(notifications, 1);
+        int middle_notification_height = get_notification_clickable_height(middle_notification, false, false);
+
+        struct notification *bottom_notification = g_slist_nth_data(notifications, 2);
+        int bottom_notification_height = get_notification_clickable_height(bottom_notification, false, true);
+
+        struct notification *result;
+
+        int top_y_coord;
+        top_y_coord = 0;
+        result = get_notification_at(top_y_coord);
+        ASSERT(result != NULL);
+        ASSERT(result == top_notification);
+
+        top_y_coord = top_notification_height - 1;
+        result = get_notification_at(top_y_coord);
+        ASSERT(result != NULL);
+        ASSERT(result == top_notification);
+
+        int middle_y_coord;
+        middle_y_coord = top_notification_height;
+        result = get_notification_at(middle_y_coord);
+        ASSERT(result != NULL);
+        ASSERT(result == middle_notification);
+
+        middle_y_coord = top_notification_height;
+        result = get_notification_at(middle_y_coord);
+        ASSERT(result != NULL);
+        ASSERT(result == middle_notification);
+
+        int bottom_y_coord;
+        bottom_y_coord = top_notification_height + middle_notification_height;
+        result = get_notification_at(bottom_y_coord);
+        ASSERT(result != NULL);
+        ASSERT(result == bottom_notification);
+
+        bottom_y_coord = top_notification_height + middle_notification_height + bottom_notification_height - 1;
+        result = get_notification_at(bottom_y_coord);
+        ASSERT(result != NULL);
+        ASSERT(result == bottom_notification);
+
+        g_slist_free_full(notifications, free_dummy_notification);
+        PASS();
+}
+
+SUITE(suite_input)
+{
+        SHUFFLE_TESTS(time(NULL), {
+                        RUN_TEST(test_get_notification_clickable_height_first);
+                        RUN_TEST(test_get_notification_clickable_height_middle);
+                        RUN_TEST(test_get_notification_clickable_height_last);
+                        RUN_TEST(test_get_notification_clickable_height_gaps);
+                        RUN_TEST(test_notification_at);
+        });
+}
diff -Nru dunst-1.8.1/test/notification.c dunst-1.9.0/test/notification.c
--- dunst-1.8.1/test/notification.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/notification.c	2022-06-27 08:43:39.000000000 -0400
@@ -137,11 +137,9 @@
 
         GVariant *rawIcon = notification_setup_raw_image(path);
 
-        settings.min_icon_size = min_icon_size;
-        settings.max_icon_size = max_icon_size;
+        n->min_icon_size = min_icon_size;
+        n->max_icon_size = max_icon_size;
         notification_icon_replace_data(n, rawIcon);
-        settings.min_icon_size = 0;
-        settings.max_icon_size = 0;
 
         g_variant_unref(rawIcon);
         g_free(path);
diff -Nru dunst-1.8.1/test/queues.c dunst-1.9.0/test/queues.c
--- dunst-1.8.1/test/queues.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/queues.c	2022-06-27 08:43:39.000000000 -0400
@@ -36,7 +36,7 @@
 
         n = test_notification("n2", 0);
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         n = test_notification("n3", 0);
         queues_notification_insert(n);
@@ -113,7 +113,7 @@
         ASSERT_EQ(a->id, b->id);
         NOT_LAST(a);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         c = test_notification("c", -1);
         c->id = b->id;
 
@@ -138,7 +138,7 @@
         queues_notification_insert(n);
         QUEUE_LEN_ALL(1, 0, 0);
         queues_notification_close(n, REASON_UNDEF);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 0, 1);
         queues_teardown();
 
@@ -148,10 +148,10 @@
         queues_init();
         queues_notification_insert(n);
         QUEUE_LEN_ALL(1, 0, 0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 1, 0);
         queues_notification_close(n, REASON_UNDEF);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 0, 1);
         queues_teardown();
 
@@ -170,7 +170,7 @@
         queues_notification_insert(n);
         QUEUE_LEN_ALL(1, 0, 0);
         queues_notification_close(n, REASON_UNDEF);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 0, 0);
         queues_teardown();
 
@@ -181,10 +181,10 @@
         queues_init();
         queues_notification_insert(n);
         QUEUE_LEN_ALL(1, 0, 0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 1, 0);
         queues_notification_close(n, REASON_UNDEF);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 0, 0);
         queues_teardown();
 
@@ -202,7 +202,7 @@
         queues_init();
         queues_notification_insert(n);
         QUEUE_LEN_ALL(1, 0, 0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 0, 1);
         queues_teardown();
 
@@ -220,12 +220,12 @@
         queues_init();
         queues_notification_insert(n);
         QUEUE_LEN_ALL(1, 0, 0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0, 0, 1);
 
         queues_history_pop();
         QUEUE_LEN_ALL(1, 0, 0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_CONTAINSm("A skip display notification should stay in displayed "
                         "queue when it got pulled out of history queue",
                         DISP, n);
@@ -264,7 +264,7 @@
         QUEUE_LEN_ALL(notification_buffer_size, 0, 0);
 
         // update notification event
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         QUEUE_LEN_ALL(0, 0, notification_buffer_size);
 
@@ -274,7 +274,7 @@
                 if(is_n_popped[i]) {
                         queues_history_pop_by_id(i+1);
                         popped_notification_count++;
-                        queues_update(STATUS_NORMAL);
+                        queues_update(STATUS_NORMAL, time_monotonic_now());
                 }
         }
 
@@ -282,7 +282,7 @@
         QUEUE_LEN_ALL(0, popped_notification_count,
                         notification_buffer_size-popped_notification_count);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         // check if any of the notifications got moved
         for(size_t i=0; i<notification_buffer_size; i++) {
                 if(is_n_popped[i]) {
@@ -312,7 +312,7 @@
                 char name[] = { 'n', '0'+i, '\0' }; // n<i>
                 n = test_notification(name, -1);
                 queues_notification_insert(n);
-                queues_update(STATUS_NORMAL);
+                queues_update(STATUS_NORMAL, time_monotonic_now());
                 queues_notification_close(n, REASON_UNDEF);
         }
 
@@ -344,7 +344,7 @@
                 n = test_notification(name, -1);
                 queues_notification_insert(n);
         }
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         for (int i = 0; i < 10; i++) {
                 char name[] = { '2', 'n', '0'+i, '\0' }; // 2n<i>
@@ -410,7 +410,7 @@
         struct notification *n = test_notification("n", 0);
 
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         ASSERTm("Age threshold is deactivated and the notification is infinite, there is no wakeup necessary.",
                 queues_get_next_datachange(time_monotonic_now()) < 0);
@@ -428,7 +428,7 @@
         struct notification *n = test_notification("n", 0);
 
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         ASSERT_IN_RANGEm("Age threshold is activated and the next wakeup should be less than a second away",
                 S2US(1)/2, queues_get_next_datachange(time_monotonic_now() + S2US(4)), S2US(1)/2);
@@ -454,7 +454,7 @@
         ASSERTm("The inserted notification is inside the waiting queue, so it should get ignored.",
                queues_get_next_datachange(time_monotonic_now()) < S2US(0));
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         ASSERT_IN_RANGEm("The notification has to get closed in less than its timeout",
                S2US(10)/2, queues_get_next_datachange(time_monotonic_now()), S2US(10)/2);
 
@@ -474,14 +474,14 @@
         n = test_notification("n1", 15);
 
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         ASSERT_IN_RANGEm("The notification has to get closed in less than its timeout.",
                n->timeout/2, queues_get_next_datachange(time_monotonic_now()), n->timeout/2);
 
         n = test_notification("n2", 10);
 
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         ASSERT_IN_RANGEm("The timeout of the second notification has to get used as sleep time now.",
                n->timeout/2, queues_get_next_datachange(time_monotonic_now()), n->timeout/2);
 
@@ -511,7 +511,7 @@
         NOT_LAST(n1);
 
         notification_ref(n2);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         queues_notification_insert(n3);
         QUEUE_LEN_ALL(0, 1, 0);
         NOT_LAST(n2);
@@ -542,7 +542,7 @@
         NOT_LAST(n1);
 
         notification_ref(n2);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         queues_notification_insert(n3);
         QUEUE_LEN_ALL(0, 1, 0);
         NOT_LAST(n2);
@@ -575,7 +575,7 @@
         NOT_LAST(n1);
 
         queues_notification_insert(n3);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         printf("queue %i\n",g_queue_get_length(QUEUE(HIST)));
         QUEUE_LEN_ALL(0, 2, 0);
 
@@ -603,7 +603,7 @@
         queues_notification_insert(n2);
 
         notification_ref(n2);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         queues_notification_insert(n3);
         QUEUE_LEN_ALL(0, 2, 0);
         NOT_LAST(n2);
@@ -628,13 +628,13 @@
         queues_notification_insert(n2);
         queues_notification_insert(n3);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         // hacky way to shift time
         n1->start -= S2US(11);
         n2->start -= S2US(11);
         n3->start -= S2US(11);
-        queues_update(STATUS_IDLE);
+        queues_update(STATUS_IDLE, time_monotonic_now());
 
         QUEUE_LEN_ALL(0,2,1);
         QUEUE_CONTAINS(HIST, n3);
@@ -642,7 +642,7 @@
         // hacky way to shift time
         n1->start -= S2US(11);
         n2->start -= S2US(11);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         QUEUE_LEN_ALL(0,1,2);
         QUEUE_CONTAINS(DISP, n1);
@@ -671,17 +671,17 @@
         queues_notification_insert(n_dela);
         queues_notification_insert(n_push);
 
-        queues_update(STATUS_FS);
+        queues_update(STATUS_FS, time_monotonic_now());
         QUEUE_CONTAINS(DISP, n_show);
         QUEUE_CONTAINS(WAIT, n_dela);
         QUEUE_CONTAINS(WAIT, n_push);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_CONTAINS(DISP, n_show);
         QUEUE_CONTAINS(DISP, n_dela);
         QUEUE_CONTAINS(DISP, n_push);
 
-        queues_update(STATUS_FS);
+        queues_update(STATUS_FS, time_monotonic_now());
         QUEUE_CONTAINS(DISP, n_show);
         QUEUE_CONTAINS(DISP, n_dela);
         QUEUE_CONTAINS(WAIT, n_push);
@@ -706,13 +706,13 @@
 
         QUEUE_LEN_ALL(3,0,0);
 
-        queues_update(STATUS_PAUSE);
+        queues_update(STATUS_PAUSE, time_monotonic_now());
         QUEUE_LEN_ALL(3,0,0);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0,3,0);
 
-        queues_update(STATUS_PAUSE);
+        queues_update(STATUS_PAUSE, time_monotonic_now());
         QUEUE_LEN_ALL(3,0,0);
 
         queues_teardown();
@@ -752,7 +752,7 @@
         queues_notification_insert(nl5);
 
         QUEUE_LEN_ALL(5,0,0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0,5,0);
 
         queues_notification_insert(nc1);
@@ -762,7 +762,7 @@
         queues_notification_insert(nc5);
 
         QUEUE_LEN_ALL(5,5,0);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(5,5,0);
 
         QUEUE_CONTAINS(DISP, nc1);
@@ -798,15 +798,15 @@
         queues_notification_insert(n2);
         queues_notification_insert(n3);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0,3,0);
 
         queues_notification_insert(n4);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(0,4,0);
 
         queues_notification_insert(n5);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
         QUEUE_LEN_ALL(2,3,0);
 
         queues_teardown();
@@ -833,12 +833,12 @@
 
         queues_notification_insert(n1);
         queues_notification_insert(n2);
-        queues_update(STATUS_FS);
+        queues_update(STATUS_FS, time_monotonic_now());
         QUEUE_LEN_ALL(2,0,0);
 
         queues_notification_insert(n3);
 
-        queues_update(STATUS_FS);
+        queues_update(STATUS_FS, time_monotonic_now());
 
         QUEUE_LEN_ALL(2,1,0);
         QUEUE_CONTAINS(WAIT, n1);
@@ -857,10 +857,10 @@
         n = test_notification("n", 10);
 
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         n->start -= S2US(11);
-        queues_update(STATUS_PAUSE);
+        queues_update(STATUS_PAUSE, time_monotonic_now());
 
         QUEUE_LEN_ALL(0,0,1);
 
@@ -906,7 +906,7 @@
         n->skip_display = true;
         queues_notification_insert(n);
 
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         QUEUE_LEN_ALL(0, 0, 3);
 
@@ -940,24 +940,24 @@
 
         n = test_notification("n0", 0);
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         n = test_notification("n1", 0);
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         n = test_notification("n2", 0);
         queues_notification_insert(n);
-        queues_update(STATUS_PAUSE);
+        queues_update(STATUS_PAUSE, time_monotonic_now());
 
         n = test_notification("n3", 0);
         queues_notification_insert(n);
-        queues_update(STATUS_PAUSE);
-        /* queues_update(STATUS_NORMAL); */
+        queues_update(STATUS_PAUSE, time_monotonic_now());
+        /* queues_update(STATUS_NORMAL, time_monotonic_now()); */
 
         n = test_notification("n4", 0);
         queues_notification_insert(n);
-        queues_update(STATUS_NORMAL);
+        queues_update(STATUS_NORMAL, time_monotonic_now());
 
         QUEUE_LEN_ALL(0, 5, 0);
 
diff -Nru dunst-1.8.1/test/setting.c dunst-1.9.0/test/setting.c
--- dunst-1.8.1/test/setting.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/setting.c	2022-06-27 08:43:39.000000000 -0400
@@ -64,6 +64,9 @@
                 if (!allowed_settings[i].value) {
                         continue; // it's a rule, that's harder to test
                 }
+                if (allowed_settings[i].different_default) {
+                        continue; // Skip testing, since it's an intended difference.
+                }
                 size_t offset = (char*)allowed_settings[i].value - (char*)&settings;
                 enum setting_type type = allowed_settings[i].type;
                 snprintf(message, 500, "The default of setting %s does not match. Different defaults are set in code and dunstrc"
diff -Nru dunst-1.8.1/test/test.c dunst-1.9.0/test/test.c
--- dunst-1.8.1/test/test.c	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/test/test.c	2022-06-27 08:43:39.000000000 -0400
@@ -7,6 +7,7 @@
 
 #include "../src/log.h"
 #include "../src/settings.h"
+#include "helpers.h"
 
 const char *base;
 
@@ -27,6 +28,7 @@
 SUITE_EXTERN(suite_icon_lookup);
 SUITE_EXTERN(suite_draw);
 SUITE_EXTERN(suite_rules);
+SUITE_EXTERN(suite_input);
 
 GREATEST_MAIN_DEFS();
 
@@ -66,6 +68,7 @@
         RUN_SUITE(suite_icon_lookup);
         RUN_SUITE(suite_draw);
         RUN_SUITE(suite_rules);
+        RUN_SUITE(suite_input);
 
         base = NULL;
         g_free(config_path);
diff -Nru dunst-1.8.1/.valgrind.suppressions dunst-1.9.0/.valgrind.suppressions
--- dunst-1.8.1/.valgrind.suppressions	2022-03-02 05:55:25.000000000 -0500
+++ dunst-1.9.0/.valgrind.suppressions	2022-06-27 08:43:39.000000000 -0400
@@ -99,3 +99,16 @@
    fun:gdk_pixbuf_new_from_file
    ...
 }
+
+# fontconfig memory leaks that occur when using certain pango library
+# functions, such as pango_layout_get_pixel_size
+{
+   fontconfig_pango_leaks
+   Memcheck:Leak
+   fun:*alloc
+   ...
+   obj:*fontconfig*
+   ...
+   obj:*pango*
+   ...
+}

Attachment: signature.asc
Description: This is a digitally signed message part

Reply via email to