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; }