mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-01 07:15:32 -04:00
add option to detach the client process from the terminal instance
This commit is contained in:
parent
464851fac1
commit
3ed9314266
4 changed files with 113 additions and 43 deletions
|
|
@ -17,6 +17,7 @@ struct client_data {
|
||||||
uint8_t fullscreen:1;
|
uint8_t fullscreen:1;
|
||||||
uint8_t hold:1;
|
uint8_t hold:1;
|
||||||
uint8_t login_shell:1;
|
uint8_t login_shell:1;
|
||||||
|
uint8_t no_wait:1;
|
||||||
|
|
||||||
uint16_t cwd_len;
|
uint16_t cwd_len;
|
||||||
uint16_t term_len;
|
uint16_t term_len;
|
||||||
|
|
|
||||||
10
client.c
10
client.c
|
|
@ -58,6 +58,7 @@ print_usage(const char *prog_name)
|
||||||
" -D,--working-directory=DIR directory to start in (CWD)\n"
|
" -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"
|
" -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"
|
" -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"
|
" -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"
|
" -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n"
|
||||||
" -v,--version show the version number and quit\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'},
|
{"working-directory", required_argument, NULL, 'D'},
|
||||||
{"server-socket", required_argument, NULL, 's'},
|
{"server-socket", required_argument, NULL, 's'},
|
||||||
{"hold", no_argument, NULL, 'H'},
|
{"hold", no_argument, NULL, 'H'},
|
||||||
|
{"no-wait", no_argument, NULL, 'N'},
|
||||||
{"log-level", required_argument, NULL, 'd'},
|
{"log-level", required_argument, NULL, 'd'},
|
||||||
{"log-colorize", optional_argument, NULL, 'l'},
|
{"log-colorize", optional_argument, NULL, 'l'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
|
|
@ -103,9 +105,10 @@ main(int argc, char *const *argv)
|
||||||
bool maximized = false;
|
bool maximized = false;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
bool hold = false;
|
bool hold = false;
|
||||||
|
bool no_wait = false;
|
||||||
|
|
||||||
while (true) {
|
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)
|
if (c == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -170,6 +173,10 @@ main(int argc, char *const *argv)
|
||||||
hold = true;
|
hold = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
no_wait = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'd': {
|
case 'd': {
|
||||||
int lvl = log_level_from_string(optarg);
|
int lvl = log_level_from_string(optarg);
|
||||||
if (unlikely(lvl < 0)) {
|
if (unlikely(lvl < 0)) {
|
||||||
|
|
@ -290,6 +297,7 @@ main(int argc, char *const *argv)
|
||||||
.fullscreen = fullscreen,
|
.fullscreen = fullscreen,
|
||||||
.hold = hold,
|
.hold = hold,
|
||||||
.login_shell = login_shell,
|
.login_shell = login_shell,
|
||||||
|
.no_wait = no_wait,
|
||||||
.cwd_len = cwd_len,
|
.cwd_len = cwd_len,
|
||||||
.term_len = term_len,
|
.term_len = term_len,
|
||||||
.title_len = title_len,
|
.title_len = title_len,
|
||||||
|
|
|
||||||
1
config.h
1
config.h
|
|
@ -72,6 +72,7 @@ struct config {
|
||||||
wchar_t *word_delimiters;
|
wchar_t *word_delimiters;
|
||||||
wchar_t *jump_label_letters;
|
wchar_t *jump_label_letters;
|
||||||
bool login_shell;
|
bool login_shell;
|
||||||
|
bool no_wait;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
enum conf_size_type type;
|
enum conf_size_type type;
|
||||||
|
|
|
||||||
144
server.c
144
server.c
|
|
@ -23,6 +23,8 @@
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
struct client;
|
struct client;
|
||||||
|
struct terminal_instance;
|
||||||
|
|
||||||
struct server {
|
struct server {
|
||||||
const struct config *conf;
|
const struct config *conf;
|
||||||
struct fdm *fdm;
|
struct fdm *fdm;
|
||||||
|
|
@ -33,6 +35,7 @@ struct server {
|
||||||
const char *sock_path;
|
const char *sock_path;
|
||||||
|
|
||||||
tll(struct client *) clients;
|
tll(struct client *) clients;
|
||||||
|
tll(struct terminal_instance *) terminals;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct client {
|
struct client {
|
||||||
|
|
@ -45,9 +48,21 @@ struct client {
|
||||||
size_t idx;
|
size_t idx;
|
||||||
} buffer;
|
} buffer;
|
||||||
|
|
||||||
struct config conf;
|
struct terminal_instance *instance;
|
||||||
struct terminal *term;
|
|
||||||
};
|
};
|
||||||
|
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
|
static void
|
||||||
client_destroy(struct client *client)
|
client_destroy(struct client *client)
|
||||||
|
|
@ -55,9 +70,10 @@ client_destroy(struct client *client)
|
||||||
if (client == NULL)
|
if (client == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (client->term != NULL) {
|
if (client->instance != NULL) {
|
||||||
LOG_WARN("client FD=%d: terminal still alive", client->fd);
|
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) {
|
if (client->fd != -1) {
|
||||||
|
|
@ -73,13 +89,6 @@ client_destroy(struct client *client)
|
||||||
}
|
}
|
||||||
|
|
||||||
free(client->buffer.data);
|
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);
|
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");
|
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
|
static void
|
||||||
term_shutdown_handler(void *data, int exit_code)
|
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 wl_shm *shm = instance->server->wayl->shm;
|
||||||
struct terminal *term = client->term;
|
|
||||||
|
|
||||||
shm_purge(shm, shm_cookie_grid(term));
|
shm_purge(shm, shm_cookie_grid(instance->terminal));
|
||||||
shm_purge(shm, shm_cookie_search(term));
|
shm_purge(shm, shm_cookie_search(instance->terminal));
|
||||||
for (enum csd_surface surf = 0; surf < CSD_SURF_COUNT; surf++)
|
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);
|
instance->terminal = NULL;
|
||||||
|
instance_destroy(instance, exit_code);
|
||||||
client->term = NULL;
|
|
||||||
client_destroy(client);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -125,7 +161,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
|
||||||
xassert(events & EPOLLIN);
|
xassert(events & EPOLLIN);
|
||||||
|
|
||||||
if (client->term != NULL) {
|
if (client->instance != NULL) {
|
||||||
uint8_t dummy[128];
|
uint8_t dummy[128];
|
||||||
ssize_t count = read(fd, dummy, sizeof(dummy));
|
ssize_t count = read(fd, dummy, sizeof(dummy));
|
||||||
LOG_WARN("client unexpectedly sent %zd bytes", count);
|
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! */
|
/* 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.data != NULL);
|
||||||
xassert(client->buffer.left == 0);
|
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_AND_NULL
|
||||||
#undef CHECK_BUF
|
#undef CHECK_BUF
|
||||||
|
|
||||||
client->conf = *server->conf;
|
struct terminal_instance *instance = malloc(sizeof(struct terminal_instance));
|
||||||
client->conf.term = strlen(term_env) > 0
|
|
||||||
|
*instance = (struct terminal_instance) {
|
||||||
|
.server = server,
|
||||||
|
.conf = *server->conf,
|
||||||
|
};
|
||||||
|
instance->conf.term = strlen(term_env) > 0
|
||||||
? xstrdup(term_env) : xstrdup(server->conf->term);
|
? 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);
|
? 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);
|
? xstrdup(app_id) : xstrdup(server->conf->app_id);
|
||||||
client->conf.hold_at_exit = cdata.hold;
|
instance->conf.hold_at_exit = cdata.hold;
|
||||||
client->conf.login_shell = cdata.login_shell;
|
instance->conf.login_shell = cdata.login_shell;
|
||||||
|
instance->conf.no_wait = cdata.no_wait;
|
||||||
|
|
||||||
if (cdata.maximized)
|
if (cdata.maximized)
|
||||||
client->conf.startup_mode = STARTUP_MAXIMIZED;
|
instance->conf.startup_mode = STARTUP_MAXIMIZED;
|
||||||
else if (cdata.fullscreen)
|
else if (cdata.fullscreen)
|
||||||
client->conf.startup_mode = STARTUP_FULLSCREEN;
|
instance->conf.startup_mode = STARTUP_FULLSCREEN;
|
||||||
|
|
||||||
if (cdata.width > 0 && cdata.height > 0) {
|
if (cdata.width > 0 && cdata.height > 0) {
|
||||||
client->conf.size.type = cdata.size_type;
|
instance->conf.size.type = cdata.size_type;
|
||||||
client->conf.size.width = cdata.width;
|
instance->conf.size.width = cdata.width;
|
||||||
client->conf.size.height = cdata.height;
|
instance->conf.size.height = cdata.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->term = term_init(
|
instance->terminal = term_init(
|
||||||
&client->conf, server->fdm, server->reaper, server->wayl,
|
&instance->conf, server->fdm, server->reaper, server->wayl,
|
||||||
"footclient", cwd, cdata.argc, argv, &term_shutdown_handler, client);
|
"footclient", cwd, cdata.argc, argv, &term_shutdown_handler, instance);
|
||||||
|
|
||||||
if (client->term == NULL) {
|
if (instance->terminal == NULL) {
|
||||||
LOG_ERR("failed to instantiate new terminal");
|
LOG_ERR("failed to instantiate new terminal");
|
||||||
client_send_exit_code(client, -1);
|
client_send_exit_code(client, -1);
|
||||||
|
instance_destroy(instance, -1);
|
||||||
goto shutdown;
|
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;
|
return true;
|
||||||
|
|
||||||
shutdown:
|
shutdown:
|
||||||
|
|
@ -289,8 +342,8 @@ shutdown:
|
||||||
fdm_del(fdm, fd);
|
fdm_del(fdm, fd);
|
||||||
client->fd = -1;
|
client->fd = -1;
|
||||||
|
|
||||||
if (client->term != NULL && !client->term->is_shutting_down)
|
if (client->instance != NULL && !client->instance->terminal->is_shutting_down)
|
||||||
term_shutdown(client->term);
|
term_shutdown(client->instance->terminal);
|
||||||
else
|
else
|
||||||
client_destroy(client);
|
client_destroy(client);
|
||||||
|
|
||||||
|
|
@ -365,6 +418,7 @@ err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct server *
|
struct server *
|
||||||
server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
struct wayland *wayl)
|
struct wayland *wayl)
|
||||||
|
|
@ -421,6 +475,7 @@ server_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.sock_path = sock_path,
|
.sock_path = sock_path,
|
||||||
|
|
||||||
.clients = tll_init(),
|
.clients = tll_init(),
|
||||||
|
.terminals = tll_init(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server))
|
if (!fdm_add(fdm, fd, EPOLLIN, &fdm_server, server))
|
||||||
|
|
@ -437,6 +492,7 @@ err:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
server_destroy(struct server *server)
|
server_destroy(struct server *server)
|
||||||
{
|
{
|
||||||
|
|
@ -450,9 +506,13 @@ server_destroy(struct server *server)
|
||||||
client_send_exit_code(it->item, 1);
|
client_send_exit_code(it->item, 1);
|
||||||
client_destroy(it->item);
|
client_destroy(it->item);
|
||||||
}
|
}
|
||||||
|
|
||||||
tll_free(server->clients);
|
tll_free(server->clients);
|
||||||
|
|
||||||
|
tll_foreach(server->terminals, it) {
|
||||||
|
instance_destroy(it->item, 1);
|
||||||
|
}
|
||||||
|
tll_free(server->terminals);
|
||||||
|
|
||||||
fdm_del(server->fdm, server->fd);
|
fdm_del(server->fdm, server->fd);
|
||||||
if (server->sock_path != NULL)
|
if (server->sock_path != NULL)
|
||||||
unlink(server->sock_path);
|
unlink(server->sock_path);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue