diff --git a/docs/dunst.5.pod b/docs/dunst.5.pod index a28ce79..3f855dc 100644 --- a/docs/dunst.5.pod +++ b/docs/dunst.5.pod @@ -511,7 +511,7 @@ single notification. To avoid the corners clipping the icon or text the corner radius will be automatically lowered to half of the notification height if it exceeds it. -=item B (values: [none/do_action/close_current/close_all]) +=item B (values: [none/do_action/close_current/close_all/context/context_all]) Defines action of mouse click. A touch input in Wayland acts as a mouse left click. @@ -524,7 +524,13 @@ Don't do anything. =item B (default for mouse_middle_click) -If the notification has exactly one action, or one is marked as default, invoke it. If there are multiple and no default, open the context menu. +Invoke the action determined by the action_name rule. If there is no such +action, open the context menu. + +=item B + +If the notification has exactly one url, open it. If there are multiple +ones, open the context menu. =item B (default for mouse_left_click) @@ -534,6 +540,14 @@ Close current notification. Close all notifications. +=item B + +Open context menu for the notification. + +=item B + +Open context menu for all notifications. + =back @@ -860,6 +874,13 @@ later. Use C to match it. Setting this to true will prevent the notification from being displayed initially but will be saved in history for later viewing. +=item C + +Sets the name of the action to be invoked on do_action. If not specified, the +action set as default action or the only available action will be invoked. + +Default: "default" + =back As with the filtering attributes, each one corresponds to diff --git a/dunstrc b/dunstrc index 5339b85..0c3009c 100644 --- a/dunstrc +++ b/dunstrc @@ -277,10 +277,14 @@ # Defines list of actions for each mouse event # Possible values are: # * none: Don't do anything. - # * do_action: If the notification has exactly one action, or one is marked as default, - # invoke it. If there are multiple and no default, open the context menu. + # * do_action: Invoke the action determined by the action_name rule. If there is no + # such action, open the context menu. + # * open_url: If the notification has exactly one url, open it. If there are multiple + # ones, open the context menu. # * close_current: Close current notification. # * close_all: Close all notifications. + # * context: Open context menu for the notification. + # * context_all: Open context menu for all notifications. # These values can be strung together for each mouse event, and # will be executed in sequence. mouse_left_click = close_current @@ -373,6 +377,7 @@ # set_transient # timeout # urgency +# action_name # # Shell-like globbing will get expanded. # diff --git a/src/input.c b/src/input.c index 1495e4d..dd2a796 100644 --- a/src/input.c +++ b/src/input.c @@ -1,5 +1,6 @@ #include "input.h" #include "log.h" +#include "menu.h" #include "settings.h" #include "queues.h" #include @@ -41,7 +42,12 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int return; } - if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT) { + if (act == MOUSE_CONTEXT_ALL) { + context_menu(); + continue; + } + + if (act == MOUSE_DO_ACTION || act == MOUSE_CLOSE_CURRENT || act == MOUSE_CONTEXT || act == MOUSE_OPEN_URL) { int y = settings.separator_height; struct notification *n = NULL; int first = true; @@ -59,8 +65,12 @@ void input_handle_click(unsigned int button, bool button_down, int mouse_x, int if (n) { if (act == MOUSE_CLOSE_CURRENT) { n->marked_for_closure = REASON_USER; - } else { + } else if (act == MOUSE_DO_ACTION) { notification_do_action(n); + } else if (act == MOUSE_OPEN_URL) { + notification_open_url(n); + } else { + notification_open_context_menu(n); } } } diff --git a/src/menu.c b/src/menu.c index b99d8ae..f2bc7b9 100644 --- a/src/menu.c +++ b/src/menu.c @@ -312,13 +312,19 @@ static GList *get_actionable_notifications(void) /* see menu.h */ void context_menu(void) +{ + GList *notifications = get_actionable_notifications(); + context_menu_for(notifications); +} + +/* see menu.h */ +void context_menu_for(GList *notifications) { if (menu_ctx.locked_notifications) { LOG_W("Context menu already running, refusing to rerun"); return; } - GList *notifications = get_actionable_notifications(); menu_ctx.locked_notifications = notifications; GError *err = NULL; diff --git a/src/menu.h b/src/menu.h index bf7fd0b..5263c58 100644 --- a/src/menu.h +++ b/src/menu.h @@ -2,6 +2,8 @@ #ifndef DUNST_MENU_H #define DUNST_MENU_H +#include + /** * Extract all urls from the given string. * @@ -16,9 +18,15 @@ void invoke_action(const char *action); void regex_teardown(void); /** - * Open the context menu that lets the user select urls/actions/etc. + * Open the context menu that lets the user select urls/actions/etc for all displayed notifications. */ void context_menu(void); +/** + * Open the context menu that lets the user select urls/actions/etc for the specified notifications. + * @param notifications (nullable) List of notifications for which the context menu should be opened + */ +void context_menu_for(GList *notifications); + #endif /* vim: set ft=c tabstop=8 shiftwidth=8 expandtab textwidth=0: */ diff --git a/src/notification.c b/src/notification.c index b16d671..86f97ce 100644 --- a/src/notification.c +++ b/src/notification.c @@ -288,6 +288,7 @@ void notification_unref(struct notification *n) g_free(n->desktop_entry); g_hash_table_unref(n->actions); + g_free(n->default_action_name); if (n->icon) g_object_unref(n->icon); @@ -386,6 +387,7 @@ struct notification *notification_create(void) n->fullscreen = FS_SHOW; n->actions = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + n->default_action_name = g_strdup("default"); n->script_count = 0; return n; @@ -647,29 +649,45 @@ void notification_update_text_to_render(struct notification *n) } /* see notification.h */ -void notification_do_action(const struct notification *n) +void notification_do_action(struct notification *n) { + assert(n->default_action_name); + if (g_hash_table_size(n->actions)) { - if (g_hash_table_contains(n->actions, "default")) { - signal_action_invoked(n, "default"); + if (g_hash_table_contains(n->actions, n->default_action_name)) { + signal_action_invoked(n, n->default_action_name); return; } - if (g_hash_table_size(n->actions) == 1) { + if (strcmp(n->default_action_name, "default") == 0 && g_hash_table_size(n->actions) == 1) { GList *keys = g_hash_table_get_keys(n->actions); signal_action_invoked(n, keys->data); g_list_free(keys); return; } - context_menu(); + notification_open_context_menu(n); - } else if (n->urls) { - if (strstr(n->urls, "\n")) - context_menu(); - else - open_browser(n->urls); } } +/* see notification.h */ +void notification_open_url(struct notification *n) +{ + if (strstr(n->urls, "\n")) + notification_open_context_menu(n); + else + open_browser(n->urls); +} + +/* see notification.h */ +void notification_open_context_menu(struct notification *n) +{ + GList *notifications = NULL; + notifications = g_list_append(notifications, n); + notification_lock(n); + + context_menu_for(notifications); +} + void notification_invalidate_actions(struct notification *n) { g_hash_table_remove_all(n->actions); } diff --git a/src/notification.h b/src/notification.h index 7457460..bfaab72 100644 --- a/src/notification.h +++ b/src/notification.h @@ -61,6 +61,7 @@ struct notification { int locked; /**< If non-zero the notification is locked **/ GHashTable *actions; + char *default_action_name; /**< The name of the action to be invoked on do_action */ enum markup_mode markup; const char *format; @@ -195,11 +196,24 @@ void notification_replace_single_field(char **haystack, void notification_update_text_to_render(struct notification *n); /** - * If the notification has exactly one action, or one is marked as default, - * invoke it. If there are multiple and no default, open the context menu. If - * there are no actions, proceed similarly with urls. + * If the notification has an action named n->default_action_name or there is only one + * action and n->default_action_name is set to "default", invoke it. If there is no + * such action, open the context menu if threre are other actions. Otherwise, do nothing. */ -void notification_do_action(const struct notification *n); +void notification_do_action(struct notification *n); + +/** + * If the notification has exactly one url, invoke it. If there are multiple, + * open the context menu. If there are no urls, do nothing. + */ +void notification_open_url(struct notification *n); + +/** + * Open the context menu for the notification. + * + * Convenience function that creates the GList and passes it to context_menu_for(). + */ +void notification_open_context_menu(struct notification *n); /** * Remove all client action data from the notification. diff --git a/src/option_parser.c b/src/option_parser.c index f53c97c..141bd4e 100644 --- a/src/option_parser.c +++ b/src/option_parser.c @@ -137,6 +137,9 @@ bool string_parse_mouse_action(const char *s, enum mouse_action *ret) STRING_PARSE_RET("do_action", MOUSE_DO_ACTION); STRING_PARSE_RET("close_current", MOUSE_CLOSE_CURRENT); STRING_PARSE_RET("close_all", MOUSE_CLOSE_ALL); + STRING_PARSE_RET("context", MOUSE_CONTEXT); + STRING_PARSE_RET("context_all", MOUSE_CONTEXT_ALL); + STRING_PARSE_RET("open_url", MOUSE_OPEN_URL); return false; } diff --git a/src/rules.c b/src/rules.c index 04b8341..e604aed 100644 --- a/src/rules.c +++ b/src/rules.c @@ -26,6 +26,10 @@ void rule_apply(struct rule *r, struct notification *n) n->transient = r->set_transient; if (r->skip_display != -1) n->skip_display = r->skip_display; + if (r->action_name) { + g_free(n->default_action_name); + n->default_action_name = g_strdup(r->action_name); + } if (r->markup != MARKUP_NULL) n->markup = r->markup; if (r->new_icon) diff --git a/src/rules.h b/src/rules.h index b49d246..2e1843e 100644 --- a/src/rules.h +++ b/src/rules.h @@ -23,6 +23,7 @@ struct rule { /* actions */ gint64 timeout; enum urgency urgency; + char *action_name; enum markup_mode markup; int history_ignore; int match_transient; diff --git a/src/settings.c b/src/settings.c index ff310ec..1e27812 100644 --- a/src/settings.c +++ b/src/settings.c @@ -862,6 +862,7 @@ void load_settings(char *cmdline_config_path) g_free(c); } + r->action_name = ini_get_string(cur_section, "action_name", NULL); r->urgency = ini_get_urgency(cur_section, "urgency", r->urgency); r->msg_urgency = ini_get_urgency(cur_section, "msg_urgency", r->msg_urgency); r->fg = ini_get_string(cur_section, "foreground", r->fg); diff --git a/src/settings.h b/src/settings.h index ffc888b..89a617c 100644 --- a/src/settings.h +++ b/src/settings.h @@ -18,7 +18,7 @@ enum icon_position { ICON_LEFT, ICON_RIGHT, ICON_OFF }; enum vertical_alignment { VERTICAL_TOP, VERTICAL_CENTER, VERTICAL_BOTTOM }; enum separator_color { SEP_FOREGROUND, SEP_AUTO, SEP_FRAME, SEP_CUSTOM }; enum follow_mode { FOLLOW_NONE, FOLLOW_MOUSE, FOLLOW_KEYBOARD }; -enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL }; +enum mouse_action { MOUSE_NONE, MOUSE_DO_ACTION, MOUSE_CLOSE_CURRENT, MOUSE_CLOSE_ALL, MOUSE_CONTEXT, MOUSE_CONTEXT_ALL, MOUSE_OPEN_URL }; #ifndef ZWLR_LAYER_SHELL_V1_LAYER_ENUM #define ZWLR_LAYER_SHELL_V1_LAYER_ENUM // Needed for compiling without wayland dependency