From 99d5bf64bca5daa70e3e8c5a4d63a257ba7e6fb8 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Thu, 28 Oct 2021 17:51:44 -0700 Subject: [PATCH] foot/client: implement xdga client activation This is an application of the xdg activation protocol that will allow compositors to associate new foot toplevels with the command that launched them. footclient receives an activation token from the launcher which the compositor can use to track application startup. It passes the token to the foot server, which then activates the new window with the token to complete the startup sequence. --- client-protocol.h | 7 +++++-- client.c | 19 ++++++++++++++++++- main.c | 7 ++++++- pgo/pgo.c | 2 +- server.c | 13 ++++++++++++- terminal.c | 4 ++-- terminal.h | 2 +- wayland.c | 6 +++++- wayland.h | 2 +- 9 files changed, 51 insertions(+), 11 deletions(-) diff --git a/client-protocol.h b/client-protocol.h index c1291f28..b0f39055 100644 --- a/client-protocol.h +++ b/client-protocol.h @@ -12,15 +12,18 @@ struct client_string { struct client_data { bool hold:1; bool no_wait:1; - uint8_t reserved:6; + bool xdga_token:1; + uint8_t reserved:5; + uint8_t token_len; uint16_t cwd_len; uint16_t override_count; uint16_t argc; /* char cwd[static cwd_len]; */ + /* char token[static token_len]; */ /* struct client_string overrides[static override_count]; */ /* struct client_string argv[static argc]; */ } __attribute__((packed)); -_Static_assert(sizeof(struct client_data) == 7, "protocol struct size error"); +_Static_assert(sizeof(struct client_data) == 8, "protocol struct size error"); diff --git a/client.c b/client.c index 5d3bd58c..b6749835 100644 --- a/client.c +++ b/client.c @@ -156,6 +156,11 @@ main(int argc, char *const *argv) /* Used to format overrides */ bool no_wait = false; + /* For XDG activation */ + const char *token = getenv("XDG_ACTIVATION_TOKEN"); + bool xdga_token = token != NULL; + size_t token_len = xdga_token ? strlen(token) + 1 : 0; + char buf[1024]; /* Total packet length, not (yet) including overrides or argv[] */ @@ -373,13 +378,15 @@ main(int argc, char *const *argv) const struct client_data data = { .hold = hold, .no_wait = no_wait, + .xdga_token = xdga_token, + .token_len = token_len, .cwd_len = cwd_len, .override_count = override_count, .argc = argc, }; /* Total packet length, not (yet) including argv[] */ - total_len += sizeof(data) + cwd_len; + total_len += sizeof(data) + cwd_len + token_len; /* Add argv[] size to total packet length */ cargv = xmalloc(argc * sizeof(cargv[0])); @@ -398,6 +405,7 @@ main(int argc, char *const *argv) /* Check for size overflows */ if (total_len >= 1llu << (8 * sizeof(uint32_t)) || cwd_len >= 1 << (8 * sizeof(data.cwd_len)) || + token_len >= 1 << (8 * sizeof(data.token_len)) || override_count > (size_t)(unsigned int)data.override_count || argc > (int)(unsigned int)data.argc) { @@ -414,6 +422,15 @@ main(int argc, char *const *argv) goto err; } + /* Send XDGA token, if we have one */ + if (xdga_token) { + if (sendall(fd, token, token_len) != token_len) + { + LOG_ERRNO("failed to send xdg activation token to server"); + goto err; + } + } + /* Send overrides */ tll_foreach(overrides, it) { const struct override *o = &it->item; diff --git a/main.c b/main.c index 07f58080..851a4856 100644 --- a/main.c +++ b/main.c @@ -160,6 +160,10 @@ main(int argc, char *const *argv) static const int foot_exit_failure = -26; int ret = foot_exit_failure; + /* XDG startup notifications */ + const char *token = getenv("XDG_ACTIVATION_TOKEN"); + unsetenv("XDG_ACTIVATION_TOKEN"); + /* Startup notifications; we don't support it, but must ensure we * don't pass this on to programs launched by us */ unsetenv("DESKTOP_STARTUP_ID"); @@ -530,7 +534,8 @@ main(int argc, char *const *argv) goto out; if (!as_server && (term = term_init( - &conf, fdm, reaper, wayl, "foot", cwd, argc, argv, + &conf, fdm, reaper, wayl, "foot", cwd, token, + argc, argv, &term_shutdown_cb, &shutdown_ctx)) == NULL) { goto out; } diff --git a/pgo/pgo.c b/pgo/pgo.c index b4bd0a17..0be2bc62 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -75,7 +75,7 @@ render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor } struct wl_window * -wayl_win_init(struct terminal *term) +wayl_win_init(struct terminal *term, const char *token) { return NULL; } diff --git a/server.c b/server.c index a5d87b04..1d31abc6 100644 --- a/server.c +++ b/server.c @@ -244,6 +244,17 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) const char *cwd = (const char *)p; p += cdata.cwd_len; LOG_DBG("CWD = %.*s", cdata.cwd_len, cwd); + /* XDGA token */ + const char *token = NULL; + if (cdata.xdga_token) { + + CHECK_BUF_AND_NULL(cdata.token_len); + token = (const char *)p; p += cdata.token_len; + LOG_DBG("XDGA = %.*s", cdata.token_len, token); + } else { + LOG_DBG("No XDGA token"); + } + /* Overrides */ for (uint16_t i = 0; i < cdata.override_count; i++) { struct client_string arg; @@ -303,7 +314,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) instance->terminal = term_init( conf != NULL ? conf : server->conf, - server->fdm, server->reaper, server->wayl, "footclient", cwd, + server->fdm, server->reaper, server->wayl, "footclient", cwd, token, cdata.argc, argv, &term_shutdown_handler, instance); if (instance->terminal == NULL) { diff --git a/terminal.c b/terminal.c index 50c43d39..910a8857 100644 --- a/terminal.c +++ b/terminal.c @@ -1033,7 +1033,7 @@ static void fdm_client_terminated( struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl, const char *foot_exe, const char *cwd, - int argc, char *const *argv, + const char *token, int argc, char *const *argv, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data) { int ptmx = -1; @@ -1257,7 +1257,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, sizeof(term->colors.table)); /* Initialize the Wayland window backend */ - if ((term->window = wayl_win_init(term)) == NULL) + if ((term->window = wayl_win_init(term, token)) == NULL) goto err; /* Load fonts */ diff --git a/terminal.h b/terminal.h index 73988ba8..8c847cfd 100644 --- a/terminal.h +++ b/terminal.h @@ -642,7 +642,7 @@ struct config; struct terminal *term_init( const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl, const char *foot_exe, const char *cwd, - int argc, char *const *argv, + const char *token, int argc, char *const *argv, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data); bool term_shutdown(struct terminal *term); diff --git a/wayland.c b/wayland.c index 49c8ed92..22e3d6dc 100644 --- a/wayland.c +++ b/wayland.c @@ -1374,7 +1374,7 @@ wayl_destroy(struct wayland *wayl) } struct wl_window * -wayl_win_init(struct terminal *term) +wayl_win_init(struct terminal *term, const char *token) { struct wayland *wayl = term->wl; const struct config *conf = term->conf; @@ -1444,6 +1444,10 @@ wayl_win_init(struct terminal *term) wl_surface_commit(win->surface); + /* Complete XDG startup notification */ + if (token) + xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface); + if (conf->tweak.render_timer_osd) { if (!wayl_win_subsurface_new(win, &win->render_timer)) { LOG_ERR("failed to create render timer surface"); diff --git a/wayland.h b/wayland.h index b92f622d..0fffc0a7 100644 --- a/wayland.h +++ b/wayland.h @@ -492,7 +492,7 @@ bool wayl_reload_xcursor_theme(struct seat *seat, int new_scale); void wayl_flush(struct wayland *wayl); void wayl_roundtrip(struct wayland *wayl); -struct wl_window *wayl_win_init(struct terminal *term); +struct wl_window *wayl_win_init(struct terminal *term, const char *token); void wayl_win_destroy(struct wl_window *win); bool wayl_win_set_urgent(struct wl_window *win);