diff --git a/config.c b/config.c
index 817f7e9..332aded 100644
--- a/config.c
+++ b/config.c
@@ -109,6 +109,7 @@ void init_default_style(struct mako_style *style) {
style->actions = true;
style->default_timeout = 0;
+ style->hover_to_dismiss_timeout = 0;
style->ignore_timeout = false;
style->colors.background = 0x285577FF;
@@ -306,6 +307,11 @@ bool apply_style(struct mako_style *target, const struct mako_style *style) {
target->spec.default_timeout = true;
}
+ if (style->spec.hover_to_dismiss_timeout) {
+ target->hover_to_dismiss_timeout = style->hover_to_dismiss_timeout;
+ target->spec.hover_to_dismiss_timeout = true;
+ }
+
if (style->spec.ignore_timeout) {
target->ignore_timeout = style->ignore_timeout;
target->spec.ignore_timeout = true;
@@ -421,6 +427,7 @@ bool apply_superset_style(
target->spec.icons = true;
target->spec.max_icon_size = true;
target->spec.default_timeout = true;
+ target->spec.hover_to_dismiss_timeout = true;
target->spec.markup = true;
target->spec.actions = true;
target->spec.history = true;
@@ -468,6 +475,8 @@ bool apply_superset_style(
target->max_icon_size = max(style->max_icon_size, target->max_icon_size);
target->default_timeout =
max(style->default_timeout, target->default_timeout);
+ target->hover_to_dismiss_timeout =
+ max(style->hover_to_dismiss_timeout, target->hover_to_dismiss_timeout);
target->markup |= style->markup;
target->actions |= style->actions;
@@ -639,6 +648,9 @@ static bool apply_style_option(struct mako_style *style, const char *name,
} else if (strcmp(name, "default-timeout") == 0) {
return spec->default_timeout =
parse_int_ge(value, &style->default_timeout, 0);
+ } else if (strcmp(name, "hover-to-dismiss-timeout") == 0) {
+ return spec->hover_to_dismiss_timeout =
+ parse_int_ge(value, &style->hover_to_dismiss_timeout, 0);
} else if (strcmp(name, "ignore-timeout") == 0) {
return spec->ignore_timeout =
parse_boolean(value, &style->ignore_timeout);
@@ -920,6 +932,7 @@ int parse_config_arguments(struct mako_config *config, int argc, char **argv) {
{"max-history", required_argument, 0, 0},
{"history", required_argument, 0, 0},
{"default-timeout", required_argument, 0, 0},
+ {"hover-to-dismiss-timeout", required_argument, 0, 0},
{"ignore-timeout", required_argument, 0, 0},
{"output", required_argument, 0, 0},
{"layer", required_argument, 0, 0},
diff --git a/contrib/completions/bash/mako b/contrib/completions/bash/mako
index bdb5bbb..e96919d 100644
--- a/contrib/completions/bash/mako
+++ b/contrib/completions/bash/mako
@@ -33,6 +33,7 @@ _mako()
'--history'
'--sort'
'--default-timeout'
+ '--hover-to-dismiss-timeout'
'--ignore-timeout'
'--output'
'--layer'
diff --git a/contrib/completions/fish/mako.fish b/contrib/completions/fish/mako.fish
index c64d203..cf24b86 100644
--- a/contrib/completions/fish/mako.fish
+++ b/contrib/completions/fish/mako.fish
@@ -32,6 +32,7 @@ complete -c mako -l max-history -d 'Max size of history buffer' -x
complete -c mako -l history -d 'Add expired notifications to history' -xa "1 0"
complete -c mako -l sort -d 'Set notification sorting method' -x
complete -c mako -l default-timeout -d 'Notification timeout in ms' -x
+complete -c mako -l hover-to-dismiss-timeout -d 'Default hover-to-dismiss timeout in milliseconds.' -x
complete -c mako -l ignore-timeout -d 'Enable notification timeout or not' -xa "1 0"
complete -c mako -l output -d 'Show notifications on this output' -xa '(complete_outputs)'
complete -c mako -l layer -d 'Show notifications on this layer' -x
diff --git a/contrib/completions/zsh/_mako b/contrib/completions/zsh/_mako
index 4a1f01c..a2dad6c 100644
--- a/contrib/completions/zsh/_mako
+++ b/contrib/completions/zsh/_mako
@@ -26,6 +26,7 @@ _arguments \
'--max-history[Max size of history buffer.]:historical notifications:' \
'--history[Add expired notification to history.]:history:' \
'--default-timeout[Default timeout in milliseconds.]:timeout (ms):' \
+ '--hover-to-dismiss-timeout[Default hover-to-dismiss timeout in milliseconds.]:timeout (ms):' \
'--ignore-timeout[If set, mako will ignore the expire timeout sent by notifications and use the one provided by default-timeout instead.]:Use default timeout:(0 1)' \
'--output[Show notifications on this output.]:name:' \
'--layer[Arrange notifications at this layer.]:layer:(background bottom top overlay)' \
diff --git a/doc/mako.5.scd b/doc/mako.5.scd
index 18eb3fb..6e929b0 100644
--- a/doc/mako.5.scd
+++ b/doc/mako.5.scd
@@ -268,6 +268,12 @@ Default when grouped: (%g) %s\\n%b
Default: 0
+*hover-to-dismiss-timeout*=_timeout_
+ Set the default hover-to-dismiss timeout to _timeout_ in milliseconds. To disable the
+ timeout, set it to zero.
+
+ Default: 0
+
*ignore-timeout*=0|1
If set, mako will ignore the expire timeout sent by notifications and use
the one provided by _default-timeout_ instead.
diff --git a/include/config.h b/include/config.h
index 4876989..8eab2f1 100644
--- a/include/config.h
+++ b/include/config.h
@@ -40,7 +40,7 @@ enum mako_icon_location {
// structs are also mirrored.
struct mako_style_spec {
bool width, height, outer_margin, margin, padding, border_size, border_radius, font,
- markup, format, text_alignment, actions, default_timeout, ignore_timeout,
+ markup, format, text_alignment, actions, default_timeout, hover_to_dismiss_timeout, ignore_timeout,
icons, max_icon_size, icon_path, icon_border_radius, group_criteria_spec, invisible, history,
icon_location, max_visible, layer, output, anchor;
struct {
@@ -76,6 +76,7 @@ struct mako_style {
bool actions;
int default_timeout; // in ms
+ int hover_to_dismiss_timeout; // in ms
bool ignore_timeout;
struct {
diff --git a/include/mako.h b/include/mako.h
index dc2c372..f1b5274 100644
--- a/include/mako.h
+++ b/include/mako.h
@@ -47,6 +47,7 @@ struct mako_surface {
struct mako_state {
struct mako_config config;
struct mako_event_loop event_loop;
+ struct mako_timer *hover_dismiss_timer;
sd_bus *bus;
sd_bus_slot *xdg_slot, *mako_slot;
diff --git a/include/notification.h b/include/notification.h
index 48c96f7..089bd9a 100644
--- a/include/notification.h
+++ b/include/notification.h
@@ -79,6 +79,7 @@ struct mako_binding_context {
typedef char *(*mako_format_func_t)(char variable, bool *markup, void *data);
bool hotspot_at(struct mako_hotspot *hotspot, int32_t x, int32_t y);
+bool hotspot_at_offset(struct mako_hotspot *hotspot, int32_t x, int32_t y, int32_t offset);
void reset_notification(struct mako_notification *notif);
struct mako_notification *create_notification(struct mako_state *state);
diff --git a/main.c b/main.c
index c273a4b..1df0223 100644
--- a/main.c
+++ b/main.c
@@ -52,6 +52,7 @@ static const char usage[] =
" and/or priority in ascending(+) or\n"
" descending(-) order.\n"
" --default-timeout Default timeout in milliseconds.\n"
+ " --hover-to-dismiss-timeout Default hover-to-dismiss timeout in milliseconds.\n"
" --ignore-timeout <0|1> Enable/disable notification timeout.\n"
" --output Show notifications on this output.\n"
" --layer Arrange notifications at this layer.\n"
diff --git a/notification.c b/notification.c
index b556c1d..80a6864 100644
--- a/notification.c
+++ b/notification.c
@@ -27,6 +27,13 @@ bool hotspot_at(struct mako_hotspot *hotspot, int32_t x, int32_t y) {
y < hotspot->y + hotspot->height;
}
+bool hotspot_at_offset(struct mako_hotspot *hotspot, int32_t x, int32_t y, int32_t offset) {
+ return x >= hotspot->x - offset &&
+ y >= hotspot->y - offset &&
+ x < hotspot->x + hotspot->width + offset &&
+ y < hotspot->y + hotspot->height + offset;
+}
+
void reset_notification(struct mako_notification *notif) {
struct mako_action *action, *tmp;
wl_list_for_each_safe(action, tmp, ¬if->actions, link) {
diff --git a/wayland.c b/wayland.c
index 6dd9185..70de4a0 100644
--- a/wayland.c
+++ b/wayland.c
@@ -198,6 +198,11 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
struct mako_seat *seat = data;
struct mako_state *state = seat->state;
+ if (state->hover_dismiss_timer != NULL) {
+ destroy_timer(state->hover_dismiss_timer);
+ state->hover_dismiss_timer = NULL;
+ }
+
seat->pointer.x = wl_fixed_to_int(surface_x);
seat->pointer.y = wl_fixed_to_int(surface_y);
seat->pointer.surface = get_surface(state, wl_surface);
@@ -231,9 +236,36 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
}
}
+static void handle_notification_hover_dismiss_timer(void *data) {
+ struct mako_notification *notif = data;
+ struct mako_surface *surface = notif->surface;
+
+ if (notif->state->hover_dismiss_timer != NULL) {
+ notif->state->hover_dismiss_timer = NULL;
+ }
+
+ close_notification(notif, MAKO_NOTIFICATION_CLOSE_EXPIRED, true);
+ set_dirty(surface);
+}
+
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *wl_surface) {
struct mako_seat *seat = data;
+ struct mako_state *state = seat->state;
+ struct mako_style *style = &state->config.superstyle;
+
+ struct mako_notification *notif;
+ wl_list_for_each(notif, &state->notifications, link) {
+ if (style->hover_to_dismiss_timeout > 0) {
+ if (hotspot_at_offset(¬if->hotspot, seat->pointer.x, seat->pointer.y, 10)) {
+ notif->state->hover_dismiss_timer = add_event_loop_timer(
+ ¬if->state->event_loop, style->hover_to_dismiss_timeout,
+ handle_notification_hover_dismiss_timer, notif);
+ break;
+ }
+ }
+ }
+
seat->pointer.surface = NULL;
}