mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-10 04:27:45 -05:00
commit
bdef28c6d1
13 changed files with 177 additions and 48 deletions
|
|
@ -49,6 +49,10 @@
|
|||
allows setting/saving/restoring/querying the keypad mode.
|
||||
* Sixel support can be disabled by setting `[tweak].sixel=no`
|
||||
(https://codeberg.org/dnkl/foot/issues/950).
|
||||
* footclient: `-E,--client-environment` command line option. When
|
||||
used, the child process in the new terminal instance inherits the
|
||||
environment from the footclient process instead of the server’s
|
||||
(https://codeberg.org/dnkl/foot/issues/1004).
|
||||
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ struct client_data {
|
|||
uint16_t cwd_len;
|
||||
uint16_t override_count;
|
||||
uint16_t argc;
|
||||
uint16_t env_count;
|
||||
|
||||
/* char cwd[static cwd_len]; */
|
||||
/* char token[static token_len]; */
|
||||
/* struct client_string overrides[static override_count]; */
|
||||
/* struct client_string argv[static argc]; */
|
||||
/* struct client_string envp[static env_count]; */
|
||||
} __attribute__((packed));
|
||||
|
||||
_Static_assert(sizeof(struct client_data) == 8, "protocol struct size error");
|
||||
_Static_assert(sizeof(struct client_data) == 10, "protocol struct size error");
|
||||
|
|
|
|||
105
client.c
105
client.c
|
|
@ -25,11 +25,13 @@
|
|||
#include "version.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
struct override {
|
||||
extern char **environ;
|
||||
|
||||
struct string {
|
||||
size_t len;
|
||||
char *str;
|
||||
};
|
||||
typedef tll(struct override) override_list_t;
|
||||
typedef tll(struct string) string_list_t;
|
||||
|
||||
static volatile sig_atomic_t aborted = 0;
|
||||
|
||||
|
|
@ -91,6 +93,7 @@ print_usage(const char *prog_name)
|
|||
" -H,--hold remain open after child process exits\n"
|
||||
" -N,--no-wait detach the client process from the running terminal, exiting immediately\n"
|
||||
" -o,--override=[section.]key=value override configuration option\n"
|
||||
" -E, --client-environment exec shell using footclient's environment, instead of the server's\n"
|
||||
" -d,--log-level={info|warning|error|none} 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"
|
||||
|
|
@ -102,20 +105,45 @@ print_usage(const char *prog_name)
|
|||
}
|
||||
|
||||
static bool NOINLINE
|
||||
push_override(override_list_t *overrides, const char *s, uint64_t *total_len)
|
||||
push_string(string_list_t *string_list, const char *s, uint64_t *total_len)
|
||||
{
|
||||
size_t len = strlen(s) + 1;
|
||||
if (len >= 1 << (8 * sizeof(struct client_string))) {
|
||||
LOG_ERR("override length overflow");
|
||||
if (len >= 1 << (8 * sizeof(uint16_t))) {
|
||||
LOG_ERR("string length overflow");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct override o = {len, xstrdup(s)};
|
||||
tll_push_back(*overrides, o);
|
||||
struct string o = {len, xstrdup(s)};
|
||||
tll_push_back(*string_list, o);
|
||||
*total_len += sizeof(struct client_string) + o.len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
free_string_list(string_list_t *string_list)
|
||||
{
|
||||
tll_foreach(*string_list, it) {
|
||||
free(it->item.str);
|
||||
tll_remove(*string_list, it);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
send_string_list(int fd, const string_list_t *string_list)
|
||||
{
|
||||
tll_foreach(*string_list, it) {
|
||||
const struct client_string s = {it->item.len};
|
||||
if (sendall(fd, &s, sizeof(s)) < 0 ||
|
||||
sendall(fd, it->item.str, s.len) < 0)
|
||||
{
|
||||
LOG_ERRNO("failed to send setup packet to server");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *const *argv)
|
||||
{
|
||||
|
|
@ -140,6 +168,7 @@ main(int argc, char *const *argv)
|
|||
{"hold", no_argument, NULL, 'H'},
|
||||
{"no-wait", no_argument, NULL, 'N'},
|
||||
{"override", required_argument, NULL, 'o'},
|
||||
{"client-environment", no_argument, NULL, 'E'},
|
||||
{"log-level", required_argument, NULL, 'd'},
|
||||
{"log-colorize", optional_argument, NULL, 'l'},
|
||||
{"version", no_argument, NULL, 'v'},
|
||||
|
|
@ -152,6 +181,7 @@ main(int argc, char *const *argv)
|
|||
enum log_class log_level = LOG_CLASS_INFO;
|
||||
enum log_colorize log_colorize = LOG_COLORIZE_AUTO;
|
||||
bool hold = false;
|
||||
bool client_environment = false;
|
||||
|
||||
/* Used to format overrides */
|
||||
bool no_wait = false;
|
||||
|
|
@ -169,35 +199,36 @@ main(int argc, char *const *argv)
|
|||
/* malloc:ed and needs to be in scope of all goto's */
|
||||
int fd = -1;
|
||||
char *_cwd = NULL;
|
||||
override_list_t overrides = tll_init();
|
||||
struct client_string *cargv = NULL;
|
||||
string_list_t overrides = tll_init();
|
||||
string_list_t envp = tll_init();
|
||||
|
||||
while (true) {
|
||||
int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNo:d:l::veh", longopts, NULL);
|
||||
int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNo:Ed:l::veh", longopts, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 't':
|
||||
snprintf(buf, sizeof(buf), "term=%s", optarg);
|
||||
if (!push_override(&overrides, buf, &total_len))
|
||||
if (!push_string(&overrides, buf, &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
snprintf(buf, sizeof(buf), "title=%s", optarg);
|
||||
if (!push_override(&overrides, buf, &total_len))
|
||||
if (!push_string(&overrides, buf, &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
case 'a':
|
||||
snprintf(buf, sizeof(buf), "app-id=%s", optarg);
|
||||
if (!push_override(&overrides, buf, &total_len))
|
||||
if (!push_string(&overrides, buf, &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
if (!push_override(&overrides, "login-shell=yes", &total_len))
|
||||
if (!push_string(&overrides, "login-shell=yes", &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
|
|
@ -219,7 +250,7 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "initial-window-size-pixels=%ux%u", width, height);
|
||||
if (!push_override(&overrides, buf, &total_len))
|
||||
if (!push_string(&overrides, buf, &total_len))
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
|
|
@ -232,18 +263,18 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "initial-window-size-chars=%ux%u", width, height);
|
||||
if (!push_override(&overrides, buf, &total_len))
|
||||
if (!push_string(&overrides, buf, &total_len))
|
||||
goto err;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'm':
|
||||
if (!push_override(&overrides, "initial-window-mode=maximized", &total_len))
|
||||
if (!push_string(&overrides, "initial-window-mode=maximized", &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
if (!push_override(&overrides, "initial-window-mode=fullscreen", &total_len))
|
||||
if (!push_string(&overrides, "initial-window-mode=fullscreen", &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
|
|
@ -260,10 +291,14 @@ main(int argc, char *const *argv)
|
|||
break;
|
||||
|
||||
case 'o':
|
||||
if (!push_override(&overrides, optarg, &total_len))
|
||||
if (!push_string(&overrides, optarg, &total_len))
|
||||
goto err;
|
||||
break;
|
||||
|
||||
case 'E':
|
||||
client_environment = true;
|
||||
break;
|
||||
|
||||
case 'd': {
|
||||
int lvl = log_level_from_string(optarg);
|
||||
if (unlikely(lvl < 0)) {
|
||||
|
|
@ -373,9 +408,17 @@ main(int argc, char *const *argv)
|
|||
cwd = _cwd;
|
||||
}
|
||||
|
||||
if (client_environment) {
|
||||
for (char **e = environ; *e != NULL; e++) {
|
||||
if (!push_string(&envp, *e, &total_len))
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/* String lengths, including NULL terminator */
|
||||
const size_t cwd_len = strlen(cwd) + 1;
|
||||
const size_t override_count = tll_length(overrides);
|
||||
const size_t env_count = tll_length(envp);
|
||||
|
||||
const struct client_data data = {
|
||||
.hold = hold,
|
||||
|
|
@ -385,6 +428,7 @@ main(int argc, char *const *argv)
|
|||
.cwd_len = cwd_len,
|
||||
.override_count = override_count,
|
||||
.argc = argc,
|
||||
.env_count = env_count,
|
||||
};
|
||||
|
||||
/* Total packet length, not (yet) including argv[] */
|
||||
|
|
@ -409,7 +453,8 @@ main(int argc, char *const *argv)
|
|||
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)
|
||||
argc > (int)(unsigned int)data.argc ||
|
||||
env_count > (size_t)(unsigned int)data.env_count)
|
||||
{
|
||||
LOG_ERR("size overflow");
|
||||
goto err;
|
||||
|
|
@ -434,16 +479,8 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
/* Send overrides */
|
||||
tll_foreach(overrides, it) {
|
||||
const struct override *o = &it->item;
|
||||
struct client_string s = {o->len};
|
||||
if (sendall(fd, &s, sizeof(s)) < 0 ||
|
||||
sendall(fd, o->str, o->len) < 0)
|
||||
{
|
||||
LOG_ERRNO("failed to send setup packet (overrides) to server");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if (!send_string_list(fd, &overrides))
|
||||
goto err;
|
||||
|
||||
/* Send argv[] */
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
|
|
@ -455,6 +492,10 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
}
|
||||
|
||||
/* Send environment */
|
||||
if (!send_string_list(fd, &envp))
|
||||
goto err;
|
||||
|
||||
struct sigaction sa = {.sa_handler = &sig_handler};
|
||||
sigemptyset(&sa.sa_mask);
|
||||
if (sigaction(SIGINT, &sa, NULL) < 0 || sigaction(SIGTERM, &sa, NULL) < 0) {
|
||||
|
|
@ -473,10 +514,8 @@ main(int argc, char *const *argv)
|
|||
ret = exit_code;
|
||||
|
||||
err:
|
||||
tll_foreach(overrides, it) {
|
||||
free(it->item.str);
|
||||
tll_remove(overrides, it);
|
||||
}
|
||||
free_string_list(&envp);
|
||||
free_string_list(&overrides);
|
||||
free(cargv);
|
||||
free(_cwd);
|
||||
if (fd != -1)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ _footclient()
|
|||
"--log-colorize"
|
||||
"--maximized"
|
||||
"--override"
|
||||
"--client-environment"
|
||||
"--server-socket"
|
||||
"--term"
|
||||
"--title"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ complete -c footclient -F -s s -l server-socket
|
|||
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 o -l override -d "configuration option to override, in form SECTION.KEY=VALUE"
|
||||
complete -c footclient -s E -l client-environment -d "child process inherits footclient's environment, instead of the server's"
|
||||
complete -c footclient -x -s d -l log-level -a "info warning error none" -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"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ _arguments \
|
|||
'(-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]' \
|
||||
'(-o --override)'{-o,--override}'[configuration option to override, in form SECTION.KEY=VALUE]:()' \
|
||||
'(-E --client-environment)'{-E,--client-environment}"[child process inherits footclient's environment, instead of the server's]" \
|
||||
'(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \
|
||||
'(-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]' \
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ terminal has terminated.
|
|||
Override an option set in the configuration file. If _SECTION_ is not
|
||||
given, defaults to _main_.
|
||||
|
||||
*-E*,*--client-environment*
|
||||
The child process in the new terminal instance will use
|
||||
footclient's environment, instead of the server's.
|
||||
|
||||
*-d*,*--log-level*={*info*,*warning*,*error*,*none*}
|
||||
Log level, used both for log output on stderr as well as
|
||||
syslog. Default: _info_.
|
||||
|
|
|
|||
2
main.c
2
main.c
|
|
@ -608,7 +608,7 @@ main(int argc, char *const *argv)
|
|||
|
||||
if (!as_server && (term = term_init(
|
||||
&conf, fdm, reaper, wayl, "foot", cwd, token,
|
||||
argc, argv,
|
||||
argc, argv, NULL,
|
||||
&term_shutdown_cb, &shutdown_ctx)) == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
21
server.c
21
server.c
|
|
@ -146,6 +146,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|||
|
||||
char **argv = NULL;
|
||||
config_override_t overrides = tll_init();
|
||||
char **envp = NULL;
|
||||
|
||||
if (events & EPOLLHUP)
|
||||
goto shutdown;
|
||||
|
|
@ -281,7 +282,21 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|||
argv[i] = (char *)p; p += arg.len;
|
||||
LOG_DBG("argv[%hu] = %.*s", i, arg.len, argv[i]);
|
||||
}
|
||||
argv[cdata.argc] = NULL;
|
||||
|
||||
/* envp */
|
||||
envp = cdata.env_count != 0
|
||||
? xcalloc(cdata.env_count + 1, sizeof(envp[0]))
|
||||
: NULL;
|
||||
|
||||
for (uint16_t i = 0; i < cdata.env_count; i++) {
|
||||
struct client_string e;
|
||||
CHECK_BUF(sizeof(e));
|
||||
memcpy(&e, p, sizeof(e)); p += sizeof(e);
|
||||
|
||||
CHECK_BUF_AND_NULL(e.len);
|
||||
envp[i] = (char *)p; p += e.len;
|
||||
LOG_DBG("env[%hu] = %.*s", i, e.len, envp[i]);
|
||||
}
|
||||
|
||||
#undef CHECK_BUF_AND_NULL
|
||||
#undef CHECK_BUF
|
||||
|
|
@ -317,7 +332,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, token,
|
||||
cdata.argc, argv, &term_shutdown_handler, instance);
|
||||
cdata.argc, argv, envp, &term_shutdown_handler, instance);
|
||||
|
||||
if (instance->terminal == NULL) {
|
||||
LOG_ERR("failed to instantiate new terminal");
|
||||
|
|
@ -336,6 +351,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|||
instance->client = client;
|
||||
client->instance = instance;
|
||||
free(argv);
|
||||
free(envp);
|
||||
tll_free_and_free(overrides, free);
|
||||
}
|
||||
|
||||
|
|
@ -345,6 +361,7 @@ shutdown:
|
|||
LOG_DBG("client FD=%d: disconnected", client->fd);
|
||||
|
||||
free(argv);
|
||||
free(envp);
|
||||
tll_free_and_free(overrides, free);
|
||||
fdm_del(fdm, fd);
|
||||
client->fd = -1;
|
||||
|
|
|
|||
72
slave.c
72
slave.c
|
|
@ -23,6 +23,65 @@
|
|||
#include "tokenize.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
extern char **environ;
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
static char *
|
||||
find_file_in_path(const char *file)
|
||||
{
|
||||
if (strchr(file, '/') != NULL)
|
||||
return xstrdup(file);
|
||||
|
||||
const char *env_path = getenv("PATH");
|
||||
char *path_list = NULL;
|
||||
|
||||
if (env_path != NULL && env_path[0] != '\0')
|
||||
path_list = xstrdup(env_path);
|
||||
else {
|
||||
size_t sc_path_len = confstr(_CS_PATH, NULL, 0);
|
||||
if (sc_path_len > 0) {
|
||||
path_list = xmalloc(sc_path_len);
|
||||
confstr(_CS_PATH, path_list, sc_path_len);
|
||||
} else
|
||||
return xstrdup(file);
|
||||
}
|
||||
|
||||
for (const char *path = strtok(path_list, ":");
|
||||
path != NULL;
|
||||
path = strtok(NULL, ":"))
|
||||
{
|
||||
char *full = xasprintf("%s/%s", path, file);
|
||||
if (access(full, F_OK) == 0) {
|
||||
free(path_list);
|
||||
return full;
|
||||
}
|
||||
|
||||
free(full);
|
||||
}
|
||||
|
||||
free(path_list);
|
||||
return xstrdup(file);
|
||||
}
|
||||
|
||||
static int
|
||||
foot_execvpe(const char *file, char *const argv[], char *const envp[])
|
||||
{
|
||||
char *path = find_file_in_path(file);
|
||||
int ret = execve(path, argv, envp);
|
||||
|
||||
/*
|
||||
* Getting here is an error
|
||||
*/
|
||||
free(path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#else /* !__FreeBSD__ */
|
||||
|
||||
#define foot_execvpe(file, argv, envp) execvpe(file, argv, envp)
|
||||
|
||||
#endif /* !__FreeBSD__ */
|
||||
|
||||
static bool
|
||||
is_valid_shell(const char *shell)
|
||||
{
|
||||
|
|
@ -145,8 +204,8 @@ emit_notifications(int fd, const user_notifications_t *notifications)
|
|||
}
|
||||
|
||||
static noreturn void
|
||||
slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell,
|
||||
const user_notifications_t *notifications)
|
||||
slave_exec(int ptmx, char *argv[], char *const envp[], int err_fd,
|
||||
bool login_shell, const user_notifications_t *notifications)
|
||||
{
|
||||
int pts = -1;
|
||||
const char *pts_name = ptsname(ptmx);
|
||||
|
|
@ -232,7 +291,7 @@ slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell,
|
|||
} else
|
||||
file = argv[0];
|
||||
|
||||
execvp(file, argv);
|
||||
foot_execvpe(file, argv, envp);
|
||||
|
||||
err:
|
||||
(void)!write(err_fd, &errno, sizeof(errno));
|
||||
|
|
@ -246,8 +305,8 @@ err:
|
|||
|
||||
pid_t
|
||||
slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
||||
const char *term_env, const char *conf_shell, bool login_shell,
|
||||
const user_notifications_t *notifications)
|
||||
char *const *envp, const char *term_env, const char *conf_shell,
|
||||
bool login_shell, const user_notifications_t *notifications)
|
||||
{
|
||||
int fork_pipe[2];
|
||||
if (pipe2(fork_pipe, O_CLOEXEC) < 0) {
|
||||
|
|
@ -319,7 +378,8 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
|||
if (is_valid_shell(shell_argv[0]))
|
||||
setenv("SHELL", shell_argv[0], 1);
|
||||
|
||||
slave_exec(ptmx, shell_argv, fork_pipe[1], login_shell, notifications);
|
||||
slave_exec(ptmx, shell_argv, envp != NULL ? envp : environ,
|
||||
fork_pipe[1], login_shell, notifications);
|
||||
BUG("Unexpected return from slave_exec()");
|
||||
break;
|
||||
|
||||
|
|
|
|||
4
slave.h
4
slave.h
|
|
@ -6,6 +6,6 @@
|
|||
#include "user-notification.h"
|
||||
|
||||
pid_t slave_spawn(
|
||||
int ptmx, int argc, const char *cwd, char *const *argv, const char *term_env,
|
||||
const char *conf_shell, bool login_shell,
|
||||
int ptmx, int argc, const char *cwd, char *const *argv, char *const *envp,
|
||||
const char *term_env, const char *conf_shell, bool login_shell,
|
||||
const user_notifications_t *notifications);
|
||||
|
|
|
|||
|
|
@ -1034,7 +1034,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,
|
||||
const char *token, int argc, char *const *argv,
|
||||
const char *token, int argc, char *const *argv, char *const *envp,
|
||||
void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data)
|
||||
{
|
||||
int ptmx = -1;
|
||||
|
|
@ -1241,7 +1241,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
|
||||
/* Start the slave/client */
|
||||
if ((term->slave = slave_spawn(
|
||||
term->ptmx, argc, term->cwd, argv,
|
||||
term->ptmx, argc, term->cwd, argv, envp,
|
||||
conf->term, conf->shell, conf->login_shell,
|
||||
&conf->notifications)) == -1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -670,7 +670,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,
|
||||
const char *token, int argc, char *const *argv,
|
||||
const char *token, int argc, char *const *argv, char *const *envp,
|
||||
void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data);
|
||||
|
||||
bool term_shutdown(struct terminal *term);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue