From 3ed93142667f052909beae43463501df37a4dcca Mon Sep 17 00:00:00 2001 From: Felipe Trzaskowski Date: Fri, 12 Mar 2021 20:46:55 -0300 Subject: [PATCH 1/3] add option to detach the client process from the terminal instance --- client-protocol.h | 1 + client.c | 10 +++- config.h | 1 + server.c | 144 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 113 insertions(+), 43 deletions(-) diff --git a/client-protocol.h b/client-protocol.h index a99d4ecf..663199c9 100644 --- a/client-protocol.h +++ b/client-protocol.h @@ -17,6 +17,7 @@ struct client_data { uint8_t fullscreen:1; uint8_t hold:1; uint8_t login_shell:1; + uint8_t no_wait:1; uint16_t cwd_len; uint16_t term_len; diff --git a/client.c b/client.c index 14893a53..975269b1 100644 --- a/client.c +++ b/client.c @@ -58,6 +58,7 @@ print_usage(const char *prog_name) " -D,--working-directory=DIR directory to start in (CWD)\n" " -s,--server-socket=PATH path to the server UNIX domain socket (default=$XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock)\n" " -H,--hold remain open after child process exits\n" + " -N,--no-wait detach the client process from the running terminal, exiting immediately\n" " -d,--log-level={info|warning|error} log level (info)\n" " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" " -v,--version show the version number and quit\n"); @@ -82,6 +83,7 @@ main(int argc, char *const *argv) {"working-directory", required_argument, NULL, 'D'}, {"server-socket", required_argument, NULL, 's'}, {"hold", no_argument, NULL, 'H'}, + {"no-wait", no_argument, NULL, 'N'}, {"log-level", required_argument, NULL, 'd'}, {"log-colorize", optional_argument, NULL, 'l'}, {"version", no_argument, NULL, 'v'}, @@ -103,9 +105,10 @@ main(int argc, char *const *argv) bool maximized = false; bool fullscreen = false; bool hold = false; + bool no_wait = false; while (true) { - int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:Hd:l::vh", longopts, NULL); + int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNd:l::vh", longopts, NULL); if (c == -1) break; @@ -170,6 +173,10 @@ main(int argc, char *const *argv) hold = true; break; + case 'N': + no_wait = true; + break; + case 'd': { int lvl = log_level_from_string(optarg); if (unlikely(lvl < 0)) { @@ -290,6 +297,7 @@ main(int argc, char *const *argv) .fullscreen = fullscreen, .hold = hold, .login_shell = login_shell, + .no_wait = no_wait, .cwd_len = cwd_len, .term_len = term_len, .title_len = title_len, diff --git a/config.h b/config.h index c857fae4..1fe82257 100644 --- a/config.h +++ b/config.h @@ -72,6 +72,7 @@ struct config { wchar_t *word_delimiters; wchar_t *jump_label_letters; bool login_shell; + bool no_wait; struct { enum conf_size_type type; diff --git a/server.c b/server.c index 6fbfaee6..652c0148 100644 --- a/server.c +++ b/server.c @@ -23,6 +23,8 @@ #include "xmalloc.h" struct client; +struct terminal_instance; + struct server { const struct config *conf; struct fdm *fdm; @@ -33,6 +35,7 @@ struct server { const char *sock_path; tll(struct client *) clients; + tll(struct terminal_instance *) terminals; }; struct client { @@ -45,9 +48,21 @@ struct client { size_t idx; } buffer; - struct config conf; - struct terminal *term; + struct terminal_instance *instance; }; +static void +client_destroy(struct client *client); +static void +client_send_exit_code(struct client *client, int exit_code); + +struct terminal_instance { + struct terminal *terminal; + struct server *server; + struct client *client; + struct config conf; +}; +static void +instance_destroy(struct terminal_instance *instance, int exit_code); static void client_destroy(struct client *client) @@ -55,9 +70,10 @@ client_destroy(struct client *client) if (client == NULL) return; - if (client->term != NULL) { + if (client->instance != NULL) { LOG_WARN("client FD=%d: terminal still alive", client->fd); - term_destroy(client->term); + client->instance->client = NULL; + instance_destroy(client->instance, 1); } if (client->fd != -1) { @@ -73,13 +89,6 @@ client_destroy(struct client *client) } free(client->buffer.data); - - /* TODO: clone server conf completely, so that we can just call - * conf_destroy() here */ - free(client->conf.term); - free(client->conf.title); - free(client->conf.app_id); - free(client); } @@ -93,23 +102,50 @@ client_send_exit_code(struct client *client, int exit_code) LOG_ERRNO("failed to write slave exit code to client"); } +static void +instance_destroy(struct terminal_instance *instance, int exit_code) +{ + if (instance->terminal != NULL) { + term_destroy(instance->terminal); + } + + tll_foreach(instance->server->terminals, it) { + if (it->item == instance) { + tll_remove(instance->server->terminals, it); + break; + } + } + + if (instance->client != NULL) { + LOG_WARN("client FD=%d: is still alive", instance->client->fd); + instance->client->instance = NULL; + client_send_exit_code(instance->client, exit_code); + client_destroy(instance->client); + } + + /* TODO: clone server conf completely, so that we can just call + * conf_destroy() here */ + free(instance->conf.term); + free(instance->conf.title); + free(instance->conf.app_id); + free(instance); + +} + static void term_shutdown_handler(void *data, int exit_code) { - struct client *client = data; + struct terminal_instance *instance = data; - struct wl_shm *shm = client->server->wayl->shm; - struct terminal *term = client->term; + struct wl_shm *shm = instance->server->wayl->shm; - shm_purge(shm, shm_cookie_grid(term)); - shm_purge(shm, shm_cookie_search(term)); + shm_purge(shm, shm_cookie_grid(instance->terminal)); + shm_purge(shm, shm_cookie_search(instance->terminal)); for (enum csd_surface surf = 0; surf < CSD_SURF_COUNT; surf++) - shm_purge(shm, shm_cookie_csd(term, surf)); + shm_purge(shm, shm_cookie_csd(instance->terminal, surf)); - client_send_exit_code(client, exit_code); - - client->term = NULL; - client_destroy(client); + instance->terminal = NULL; + instance_destroy(instance, exit_code); } static bool @@ -125,7 +161,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) xassert(events & EPOLLIN); - if (client->term != NULL) { + if (client->instance != NULL) { uint8_t dummy[128]; ssize_t count = read(fd, dummy, sizeof(dummy)); LOG_WARN("client unexpectedly sent %zd bytes", count); @@ -186,7 +222,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) /* All initialization data received - time to instantiate a terminal! */ - xassert(client->term == NULL); + xassert(client->instance == NULL); xassert(client->buffer.data != NULL); xassert(client->buffer.left == 0); @@ -248,38 +284,55 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) #undef CHECK_BUF_AND_NULL #undef CHECK_BUF - client->conf = *server->conf; - client->conf.term = strlen(term_env) > 0 + struct terminal_instance *instance = malloc(sizeof(struct terminal_instance)); + + *instance = (struct terminal_instance) { + .server = server, + .conf = *server->conf, + }; + instance->conf.term = strlen(term_env) > 0 ? xstrdup(term_env) : xstrdup(server->conf->term); - client->conf.title = strlen(title) > 0 + instance->conf.title = strlen(title) > 0 ? xstrdup(title) : xstrdup(server->conf->title); - client->conf.app_id = strlen(app_id) > 0 + instance->conf.app_id = strlen(app_id) > 0 ? xstrdup(app_id) : xstrdup(server->conf->app_id); - client->conf.hold_at_exit = cdata.hold; - client->conf.login_shell = cdata.login_shell; + instance->conf.hold_at_exit = cdata.hold; + instance->conf.login_shell = cdata.login_shell; + instance->conf.no_wait = cdata.no_wait; if (cdata.maximized) - client->conf.startup_mode = STARTUP_MAXIMIZED; + instance->conf.startup_mode = STARTUP_MAXIMIZED; else if (cdata.fullscreen) - client->conf.startup_mode = STARTUP_FULLSCREEN; + instance->conf.startup_mode = STARTUP_FULLSCREEN; if (cdata.width > 0 && cdata.height > 0) { - client->conf.size.type = cdata.size_type; - client->conf.size.width = cdata.width; - client->conf.size.height = cdata.height; + instance->conf.size.type = cdata.size_type; + instance->conf.size.width = cdata.width; + instance->conf.size.height = cdata.height; } - client->term = term_init( - &client->conf, server->fdm, server->reaper, server->wayl, - "footclient", cwd, cdata.argc, argv, &term_shutdown_handler, client); + instance->terminal = term_init( + &instance->conf, server->fdm, server->reaper, server->wayl, + "footclient", cwd, cdata.argc, argv, &term_shutdown_handler, instance); - if (client->term == NULL) { + if (instance->terminal == NULL) { LOG_ERR("failed to instantiate new terminal"); client_send_exit_code(client, -1); + instance_destroy(instance, -1); goto shutdown; } - free(argv); + if (instance->conf.no_wait) { + // the server owns the instance + tll_push_back(server->terminals, instance); + client_send_exit_code(client, 0); + goto shutdown; + } else { + // the instance is attached to the client + instance->client = client; + client->instance = instance; + } + return true; shutdown: @@ -289,8 +342,8 @@ shutdown: fdm_del(fdm, fd); client->fd = -1; - if (client->term != NULL && !client->term->is_shutting_down) - term_shutdown(client->term); + if (client->instance != NULL && !client->instance->terminal->is_shutting_down) + term_shutdown(client->instance->terminal); else client_destroy(client); @@ -365,6 +418,7 @@ err: return ret; } + struct server * server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl) @@ -421,6 +475,7 @@ server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .sock_path = sock_path, .clients = tll_init(), + .terminals = tll_init(), }; if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server)) @@ -437,6 +492,7 @@ err: return NULL; } + void server_destroy(struct server *server) { @@ -450,9 +506,13 @@ server_destroy(struct server *server) client_send_exit_code(it->item, 1); client_destroy(it->item); } - tll_free(server->clients); + tll_foreach(server->terminals, it) { + instance_destroy(it->item, 1); + } + tll_free(server->terminals); + fdm_del(server->fdm, server->fd); if (server->sock_path != NULL) unlink(server->sock_path); From 2dc13cc13b0a8367c12dd217e3e5e08176e2e7ce Mon Sep 17 00:00:00 2001 From: Felipe Trzaskowski Date: Sat, 13 Mar 2021 07:41:05 -0300 Subject: [PATCH 2/3] fix code style issues and memory leak --- server.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/server.c b/server.c index 652c0148..2d813ab2 100644 --- a/server.c +++ b/server.c @@ -50,10 +50,7 @@ struct client { struct terminal_instance *instance; }; -static void -client_destroy(struct client *client); -static void -client_send_exit_code(struct client *client, int exit_code); +static void client_destroy(struct client *client); struct terminal_instance { struct terminal *terminal; @@ -61,8 +58,7 @@ struct terminal_instance { struct client *client; struct config conf; }; -static void -instance_destroy(struct terminal_instance *instance, int exit_code); +static void instance_destroy(struct terminal_instance *instance, int exit_code); static void client_destroy(struct client *client) @@ -105,9 +101,8 @@ client_send_exit_code(struct client *client, int exit_code) static void instance_destroy(struct terminal_instance *instance, int exit_code) { - if (instance->terminal != NULL) { + if (instance->terminal != NULL) term_destroy(instance->terminal); - } tll_foreach(instance->server->terminals, it) { if (it->item == instance) { @@ -117,7 +112,6 @@ instance_destroy(struct terminal_instance *instance, int exit_code) } if (instance->client != NULL) { - LOG_WARN("client FD=%d: is still alive", instance->client->fd); instance->client->instance = NULL; client_send_exit_code(instance->client, exit_code); client_destroy(instance->client); @@ -287,6 +281,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) struct terminal_instance *instance = malloc(sizeof(struct terminal_instance)); *instance = (struct terminal_instance) { + .client = NULL, .server = server, .conf = *server->conf, }; @@ -331,6 +326,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) // the instance is attached to the client instance->client = client; client->instance = instance; + free(argv); } return true; @@ -418,7 +414,6 @@ err: return ret; } - struct server * server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl) @@ -492,7 +487,6 @@ err: return NULL; } - void server_destroy(struct server *server) { @@ -506,11 +500,12 @@ server_destroy(struct server *server) client_send_exit_code(it->item, 1); client_destroy(it->item); } + tll_free(server->clients); - tll_foreach(server->terminals, it) { + tll_foreach(server->terminals, it) instance_destroy(it->item, 1); - } + tll_free(server->terminals); fdm_del(server->fdm, server->fd); From 452830bd08e5429ac00be4529e8304ac0de1faa1 Mon Sep 17 00:00:00 2001 From: Felipe Trzaskowski Date: Sat, 13 Mar 2021 07:49:57 -0300 Subject: [PATCH 3/3] add changelog entry, completions and man entry for -N, --no-wait --- CHANGELOG.md | 2 ++ completions/fish/footclient.fish | 1 + completions/zsh/_footclient | 1 + doc/footclient.1.scd | 4 ++++ 4 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6c03b88..b4193019 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,8 @@ sequences (https://codeberg.org/dnkl/foot/issues/235). * Support for transparent sixels (DCS parameter `P2=1`) (https://codeberg.org/dnkl/foot/issues/391). +* `-N,--no-wait` to `footclient` + (https://codeberg.org/dnkl/foot/issues/395). ### Changed diff --git a/completions/fish/footclient.fish b/completions/fish/footclient.fish index 09dc7305..826508ef 100644 --- a/completions/fish/footclient.fish +++ b/completions/fish/footclient.fish @@ -10,6 +10,7 @@ complete -c footclient -x -s w -l window-size-pixels complete -c footclient -x -s W -l window-size-chars -d "window WIDTHxHEIGHT, in characters (not set)" complete -c footclient -F -s s -l server-socket -d "override the default path to the foot server socket ($XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock)" complete -c footclient -s H -l hold -d "remain open after child process exits" +complete -c footclient -s N -l no-wait -d "detach the client process from the running terminal, exiting immediately" complete -c footclient -x -s d -l log-level -a "info warning error" -d "log-level (info)" complete -c footclient -x -s l -l log-colorize -a "always never auto" -d "enable or disable colorization of log output on stderr" complete -c footclient -s v -l version -d "show the version number and quit" diff --git a/completions/zsh/_footclient b/completions/zsh/_footclient index f8537748..411d4ec5 100644 --- a/completions/zsh/_footclient +++ b/completions/zsh/_footclient @@ -13,6 +13,7 @@ _arguments \ '(-W --window-size-chars)'{-W,--window-size-chars}'[window WIDTHxHEIGHT, in characters (not set)]:size_chars:()' \ '(-s --server-socket)'{-s,--server-socket}'[override the default path to the foot server socket ($XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock)]:server:_files' \ '(-H --hold)'{-H,--hold}'[remain open after child process exits]' \ + '(-N --no-wait)'{-N,--no-wait}'[detach the client process from the running terminal, exiting immediately]' \ '(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-v --version)'{-v,--version}'[show the version number and quit]' \ diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index 107e6eaa..900668a2 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -60,6 +60,10 @@ terminal has terminated). *-H*,*--hold* Remain open after child process exits. +*-N*,*--no-wait* + Detach the client process from the running terminal, exiting + immediately. + *-d*,*--log-level*={*info*,*warning*,*error*} Log level, used both for log output on stderr as well as syslog. Default: _info_.