notify: XDG token is now expected to be prefixed with xdgtoken=

This patch modifies our stdout reader to consume input as we go,
instead of all at once when stdout is closed. This will make it easier
to add support for reading e.g. the daemon assigned notification ID in
the future, and also ensures we see the XDG activation token "as soon
as possible".

Furthermore, to be more future proof, require the XDG activation token
to be prefixed with 'xdgtoken=', and ignore other lines.

Thus, instead of treating *all* of stdout as the XDG activation token,
parse stdout line-by-line, and ignore everything that does not begin
with 'xdgtoken='. Everything (on that line) following 'xdgtoken=' is
treated as the activation token.
This commit is contained in:
Daniel Eklöf 2024-07-23 09:33:18 +02:00
parent e52d6e3fb8
commit b319618af1
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
3 changed files with 74 additions and 30 deletions

View file

@ -463,11 +463,14 @@ Note: do not set *TERM* here; use the *term* option in the main
has been configured. has been configured.
For this to work, foot needs an XDG activation token. To this For this to work, foot needs an XDG activation token. To this
end, foot will read the command's stdout; everything printed end, foot will read the command's stdout; a line prefixed with
there, not including trailing newlines, are assumed to be part *xdgtoken=* will be recognized as containing the XDG
of the activation token. There is no harm in printing activation token:
something else on stdout - it will simply result in the
activation failing (i.e. the window will not be focused). xdgtoken=18179adf579a7a904ce73754964b1ec3
The expected format of stdout may change at any time. Please
read the changelog when upgrading foot.
*Note*: notify-send does not, out of the box, support *Note*: notify-send does not, out of the box, support
reporting the XDG activation token in any way. This means reporting the XDG activation token in any way. This means

View file

@ -28,6 +28,40 @@ notify_free(struct terminal *term, struct notification *notif)
free(notif->body); free(notif->body);
free(notif->icon); free(notif->icon);
free(notif->xdg_token); free(notif->xdg_token);
free(notif->stdout);
}
static void
consume_stdout(struct notification *notif, bool eof)
{
char *data = notif->stdout;
const char *line = data;
size_t left = notif->stdout_sz;
/* Process stdout, line-by-line */
while (left > 0) {
line = data;
size_t len = left;
char *eol = memchr(line, '\n', left);
if (eol != NULL) {
*eol = '\0';
len = strlen(line);
data = eol + 1;
} else if (!eof)
break;
/* Check for 'xdgtoken=xyz' */
if (len > 9 && memcmp(line, "xdgtoken=", 9) == 0) {
notif->xdg_token = xstrndup(&line[9], len - 9);
LOG_DBG("XDG token: \"%s\"", notif->xdg_token);
}
left -= len + (eol != NULL ? 1 : 0);
}
memmove(notif->stdout, data, left);
notif->stdout_sz = left;
} }
static bool static bool
@ -36,6 +70,7 @@ fdm_notify_stdout(struct fdm *fdm, int fd, int events, void *data)
const struct terminal *term = data; const struct terminal *term = data;
struct notification *notif = NULL; struct notification *notif = NULL;
/* Find notification */ /* Find notification */
tll_foreach(term->notifications, it) { tll_foreach(term->notifications, it) {
if (it->item.stdout_fd == fd) { if (it->item.stdout_fd == fd) {
@ -56,34 +91,25 @@ fdm_notify_stdout(struct fdm *fdm, int fd, int events, void *data)
return false; return false;
} }
if (count > 0) { if (count > 0 && notif != NULL) {
buf[count - 1] = '\0'; if (notif->stdout == NULL) {
xassert(notif->stdout_sz == 0);
if (notif != NULL) { notif->stdout = xmemdup(buf, count);
if (notif->xdg_token == NULL) { } else {
notif->xdg_token = xstrdup(buf); notif->stdout = xrealloc(notif->stdout, notif->stdout_sz + count);
} else { memcpy(&notif->stdout[notif->stdout_sz], buf, count);
char *new_token = xstrjoin(notif->xdg_token, buf);
free(notif->xdg_token);
notif->xdg_token = new_token;
}
} }
notif->stdout_sz += count;
consume_stdout(notif, false);
} }
} }
if (events & EPOLLHUP) { if (events & EPOLLHUP) {
fdm_del(fdm, fd); fdm_del(fdm, fd);
if (notif != NULL) if (notif != NULL) {
notif->stdout_fd = -1; notif->stdout_fd = -1;
consume_stdout(notif, true);
/* Strip trailing newlines */
if (notif != NULL && notif->xdg_token != NULL) {
size_t len = strlen(notif->xdg_token);
while (len > 0 && notif->xdg_token[len - 1] == '\n')
len--;
notif->xdg_token[len] = '\0';
} }
} }
@ -130,6 +156,7 @@ notify_notify(const struct terminal *term, struct notification *notif)
xassert(notif->xdg_token == NULL); xassert(notif->xdg_token == NULL);
xassert(notif->pid == 0); xassert(notif->pid == 0);
xassert(notif->stdout_fd == 0); xassert(notif->stdout_fd == 0);
xassert(notif->stdout == NULL);
notif->pid = -1; notif->pid = -1;
notif->stdout_fd = -1; notif->stdout_fd = -1;
@ -187,7 +214,9 @@ notify_notify(const struct terminal *term, struct notification *notif)
LOG_DBG(" argv[%zu] = \"%s\"", i, argv[i]); LOG_DBG(" argv[%zu] = \"%s\"", i, argv[i]);
int stdout_fds[2] = {-1, -1}; int stdout_fds[2] = {-1, -1};
if (notif->focus && pipe2(stdout_fds, O_CLOEXEC | O_NONBLOCK) < 0) { if ((notif->focus || notif->report) &&
pipe2(stdout_fds, O_CLOEXEC | O_NONBLOCK) < 0)
{
LOG_WARN("failed to create stdout pipe"); LOG_WARN("failed to create stdout pipe");
/* Non-fatal */ /* Non-fatal */
} }

View file

@ -23,18 +23,30 @@ enum notify_urgency {
}; };
struct notification { struct notification {
/*
* Set by caller of notify_notify()
*/
char *id; char *id;
char *title; char *title;
char *body; char *body;
char *icon; char *icon;
char *xdg_token;
enum notify_when when; enum notify_when when;
enum notify_urgency urgency; enum notify_urgency urgency;
bool focus; bool focus;
bool report; bool report;
pid_t pid; /*
int stdout_fd; * Used internally by notify
*/
char *xdg_token; /* XDG activation token, from daemon */
pid_t pid; /* Notifier command PID */
int stdout_fd; /* Notifier command's stdout */
char *stdout; /* Data we've reado from command's stdout */
size_t stdout_sz;
}; };
bool notify_notify(const struct terminal *term, struct notification *notif); bool notify_notify(const struct terminal *term, struct notification *notif);