From 9cf99ea4bffd667ae59e4328a28b86dcefacb3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 26 Jul 2024 16:23:17 +0200 Subject: [PATCH] notify: close notification by sending SIGINT to helper If the user hasn't configured a custom 'desktop-notifications.close' command, try to close the notification by sending SIGINT to the notification helper. This is best-effort: * If there's no helper running, we do nothing (except warn) * We don't verify, in any way, the notification is actually closed * We don't send any other signals, under any circumstances. That is, no SIGTERM, no SIGKILL. Ever. --- doc/foot.ini.5.scd | 5 +++ notify.c | 89 ++++++++++++++++++++++++++++++---------------- notify.h | 1 + osc.c | 1 + 4 files changed, 66 insertions(+), 30 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 195baafe..df219e7b 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -524,6 +524,11 @@ xdgtoken=95ebdfe56e4f47ddb5bba9d7dc3a2c35 Closing a notification is only supported by the Kitty Desktop Notification protocol, OSC-99. + If set to the empty string (the default), foot will instead try to + close the notification by sending SIGINT to the notification + helper process. For example, *notify-send --wait* (libnotify >= + 0.8.0) responds to SIGINT by closing the notification. + Default: _not set_ *inhibit-when-focused* diff --git a/notify.c b/notify.c index fa547126..39e8db4e 100644 --- a/notify.c +++ b/notify.c @@ -170,12 +170,16 @@ notif_done(struct reaper *reaper, pid_t pid, int status, void *data) if (notif->pid != pid) continue; - LOG_DBG("notification %s dismissed", notif->id); + LOG_DBG("notification %s closed", notif->id); if (notif->activated && notif->focus) { LOG_DBG("focus window on notification activation: \"%s\"", notif->xdg_token); - wayl_activate(term->wl, term->window, notif->xdg_token); + + if (notif->xdg_token == NULL) + LOG_WARN("cannot focus window: no activation token available"); + else + wayl_activate(term->wl, term->window, notif->xdg_token); } if (notif->activated && notif->report_activated) { @@ -240,7 +244,9 @@ notify_notify(struct terminal *term, struct notification *notif) icon_name_or_path = notif->icon_symbolic_name; } - bool track_notification = notif->focus || notif->report_activated; + bool track_notification = notif->focus || + notif->report_activated || + notif->may_be_programatically_closed; LOG_DBG("notify: title=\"%s\", body=\"%s\", icon=\"%s\" (tracking: %s)", title, body, icon_name_or_path, track_notification ? "yes" : "no"); @@ -344,44 +350,67 @@ notify_close(struct terminal *term, const char *id) { LOG_DBG("close notification %s", id); - if (term->conf->desktop_notifications.close.argv.args == NULL) - return; - tll_foreach(term->active_notifications, it) { const struct notification *notif = &it->item; if (notif->id == 0 || !streq(notif->id, id)) continue; - if (notif->external_id == 0) - return; + if (term->conf->desktop_notifications.close.argv.args == NULL) { + LOG_DBG( + "trying to close notification \"%s\" by sending SIGINT to %u", + id, notif->pid); - char **argv = NULL; - size_t argc = 0; + if (notif->pid == 0) { + LOG_WARN( + "cannot close notification \"%s\": no helper process running", + id); + } else { + /* Best-effort... */ + kill(notif->pid, SIGINT); + } + } else { + LOG_DBG( + "trying to close notification \"%s\" " + "by running user defined command", id); - char external_id[16]; - xsnprintf(external_id, sizeof(external_id), "%u", notif->external_id); + if (notif->external_id == 0) { + LOG_WARN("cannot close notification \"%s\": " + "no daemon assigned notification ID available", id); + return; + } - if (!spawn_expand_template( - &term->conf->desktop_notifications.close, 1, - (const char *[]){"id"}, - (const char *[]){external_id}, - &argc, &argv)) - { - return; + char **argv = NULL; + size_t argc = 0; + + char external_id[16]; + xsnprintf(external_id, sizeof(external_id), "%u", notif->external_id); + + if (!spawn_expand_template( + &term->conf->desktop_notifications.close, 1, + (const char *[]){"id"}, + (const char *[]){external_id}, + &argc, &argv)) + { + return; + } + + int devnull = open("/dev/null", O_RDONLY); + spawn( + term->reaper, NULL, argv, devnull, -1, -1, + NULL, (void *)term, NULL); + + if (devnull >= 0) + close(devnull); + + for (size_t i = 0; i < argc; i++) + free(argv[i]); + free(argv); } - int devnull = open("/dev/null", O_RDONLY); - spawn( - term->reaper, NULL, argv, devnull, -1, -1, - NULL, (void *)term, NULL); - - if (devnull >= 0) - close(devnull); - - for (size_t i = 0; i < argc; i++) - free(argv[i]); - free(argv); + return; } + + LOG_WARN("cannot close notification \"%s\": no such notification", id); } static void diff --git a/notify.h b/notify.h index a20c2e51..bf19d37c 100644 --- a/notify.h +++ b/notify.h @@ -39,6 +39,7 @@ struct notification { enum notify_when when; enum notify_urgency urgency; bool focus; + bool may_be_programatically_closed; bool report_activated; bool report_closed; diff --git a/osc.c b/osc.c index 2eaed869..34080467 100644 --- a/osc.c +++ b/osc.c @@ -759,6 +759,7 @@ kitty_notification(struct terminal *term, char *string) .when = when, .urgency = urgency, .focus = focus, + .may_be_programatically_closed = true, .report_activated = report_activated, .report_closed = report_closed, .stdout_fd = -1,