From 57de9feaa508fdd83c0d08c499232187c5f4b6c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Dec 2019 15:27:17 +0100 Subject: [PATCH 1/4] term: term_spawn_new(): new function, spawns a new foot/footclient process Bind ctrl+shift+return to it --- input.c | 5 ++++ main.c | 5 ++-- server.c | 2 +- slave.c | 1 + terminal.c | 67 +++++++++++++++++++++++++++++++++++++++++++++++++++++- terminal.h | 5 +++- 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/input.c b/input.c index 11b46cf6..ca7d0ec2 100644 --- a/input.c +++ b/input.c @@ -282,6 +282,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, search_begin(term); goto maybe_repeat; } + + else if (sym == XKB_KEY_Return) { + term_spawn_new(term); + goto maybe_repeat; + } } diff --git a/main.c b/main.c index a8776606..540f106c 100644 --- a/main.c +++ b/main.c @@ -223,8 +223,9 @@ main(int argc, char *const *argv) if ((wayl = wayl_init(fdm)) == NULL) goto out; - if (!as_server && (term = term_init(&conf, fdm, wayl, conf.term, argc, argv, - &term_shutdown_cb, &shutdown_ctx)) == NULL) + if (!as_server && (term = term_init( + &conf, fdm, wayl, conf.term, prog_name, argc, argv, + &term_shutdown_cb, &shutdown_ctx)) == NULL) goto out; if (as_server && (server = server_init(&conf, fdm, wayl)) == NULL) diff --git a/server.c b/server.c index 143af512..a8b15eba 100644 --- a/server.c +++ b/server.c @@ -213,7 +213,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) client->term = term_init( server->conf, server->fdm, server->wayl, strlen(term_env) > 0 ? term_env : server->conf->term, - argc, argv, &term_shutdown_handler, client); + "footclient" /* TODO */, argc, argv, &term_shutdown_handler, client); if (client->term == NULL) { LOG_ERR("failed to instantiate new terminal"); diff --git a/slave.c b/slave.c index 0b453c5d..bffa48a8 100644 --- a/slave.c +++ b/slave.c @@ -64,6 +64,7 @@ err: close(pts); if (ptmx != -1) close(ptmx); + close(err_fd); _exit(errno); } diff --git a/terminal.c b/terminal.c index 836785a0..4dfe5128 100644 --- a/terminal.c +++ b/terminal.c @@ -465,7 +465,7 @@ initialize_fonts(struct terminal *term, const struct config *conf) struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, - const char *term_env, int argc, char *const *argv, + const char *term_env, const char *foot_exe, int argc, char *const *argv, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data) { int ptmx = -1; @@ -600,6 +600,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, }, .shutdown_cb = shutdown_cb, .shutdown_data = shutdown_data, + .foot_exe = strdup(foot_exe), }; initialize_color_cube(term); @@ -830,6 +831,7 @@ term_destroy(struct terminal *term) free(it->item.data); tll_free(term->ptmx_buffer); tll_free(term->tab_stops); + free(term->foot_exe); int ret = EXIT_SUCCESS; @@ -1642,3 +1644,66 @@ term_flash(struct terminal *term, unsigned duration_ms) term->flash.active = true; } } + +bool +term_spawn_new(const struct terminal *term) +{ + pid_t pid = fork(); + if (pid < 0) { + LOG_ERRNO("failed to fork new terminal"); + return false; + } + + if (pid == 0) { + /* Child */ + int pipe_fds[2] = {-1, -1}; + if (pipe2(pipe_fds, O_CLOEXEC) < 0) { + LOG_ERRNO("failed to create pipe"); + goto err; + } + + /* Double fork */ + pid_t pid2 = fork(); + if (pid2 < 0) { + LOG_ERRNO("failed to double fork new terminal"); + goto err; + } + + if (pid2 == 0) { + /* Child */ + close(pipe_fds[0]); + execl(term->foot_exe, term->foot_exe, NULL); + write(pipe_fds[1], &errno, sizeof(errno)); + _exit(errno); + } + + /* Parent */ + + close(pipe_fds[1]); + + int _errno; + static_assert(sizeof(_errno) == sizeof(errno), "errno size mismatch"); + + ssize_t ret = read(pipe_fds[0], &_errno, sizeof(_errno)); + close(pipe_fds[0]); + + if (ret == 0) + _exit(0); + else if (ret < 0) + LOG_ERRNO("failed to read from pipe"); + else { + LOG_ERRNO_P("%s: failed to spawn new terminal", _errno, term->foot_exe); + errno = _errno; + waitpid(pid2, NULL, 0); + } + + err: + if (pipe_fds[0] != -1) + close(pipe_fds[0]); + _exit(errno); + } + + int result; + waitpid(pid, &result, 0); + return WIFEXITED(result) && WEXITSTATUS(result) == 0; +} diff --git a/terminal.h b/terminal.h index 4d82aeaa..b7a92be4 100644 --- a/terminal.h +++ b/terminal.h @@ -314,12 +314,14 @@ struct terminal { bool is_shutting_down; void (*shutdown_cb)(void *data, int exit_code); void *shutdown_data; + + char *foot_exe; }; struct config; struct terminal *term_init( const struct config *conf, struct fdm *fdm, struct wayland *wayl, - const char *term_env, int argc, char *const *argv, + const char *term_env, const char *foot_exe, int argc, char *const *argv, void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data); bool term_shutdown(struct terminal *term); @@ -379,3 +381,4 @@ void term_xcursor_update(struct terminal *term); void term_set_window_title(struct terminal *term, const char *title); void term_flash(struct terminal *term, unsigned duration_ms); +bool term_spawn_new(const struct terminal *term); From a484a65fefc89bce0f3b77503ad35bf86f329680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Dec 2019 15:29:42 +0100 Subject: [PATCH 2/4] term: term_spawn_new: always spawn foot/footclient from PATH --- main.c | 2 +- server.c | 2 +- terminal.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index 540f106c..ee9900cb 100644 --- a/main.c +++ b/main.c @@ -224,7 +224,7 @@ main(int argc, char *const *argv) goto out; if (!as_server && (term = term_init( - &conf, fdm, wayl, conf.term, prog_name, argc, argv, + &conf, fdm, wayl, conf.term, "foot", argc, argv, &term_shutdown_cb, &shutdown_ctx)) == NULL) goto out; diff --git a/server.c b/server.c index a8b15eba..d871f816 100644 --- a/server.c +++ b/server.c @@ -213,7 +213,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) client->term = term_init( server->conf, server->fdm, server->wayl, strlen(term_env) > 0 ? term_env : server->conf->term, - "footclient" /* TODO */, argc, argv, &term_shutdown_handler, client); + "footclient", argc, argv, &term_shutdown_handler, client); if (client->term == NULL) { LOG_ERR("failed to instantiate new terminal"); diff --git a/terminal.c b/terminal.c index 4dfe5128..bd79bd08 100644 --- a/terminal.c +++ b/terminal.c @@ -1672,7 +1672,7 @@ term_spawn_new(const struct terminal *term) if (pid2 == 0) { /* Child */ close(pipe_fds[0]); - execl(term->foot_exe, term->foot_exe, NULL); + execlp(term->foot_exe, term->foot_exe, NULL); write(pipe_fds[1], &errno, sizeof(errno)); _exit(errno); } From 016bde1bd4135a4a7d48e1c864a223cc6cedaf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Dec 2019 15:35:54 +0100 Subject: [PATCH 3/4] term: wip: track current working directory This sets the initial current working directory, as it is when the terminal is instantiated. We chdir() to it just before spawning a new terminal. --- terminal.c | 13 +++++++++++++ terminal.h | 1 + 2 files changed, 14 insertions(+) diff --git a/terminal.c b/terminal.c index bd79bd08..f7f98407 100644 --- a/terminal.c +++ b/terminal.c @@ -601,8 +601,18 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl, .shutdown_cb = shutdown_cb, .shutdown_data = shutdown_data, .foot_exe = strdup(foot_exe), + .cwd = NULL, }; + { + size_t buf_len = 1024; + do { + term->cwd = realloc(term->cwd, buf_len); + getcwd(term->cwd, buf_len); + buf_len *= 2; + } while (errno == ERANGE); + } + initialize_color_cube(term); if (!initialize_render_workers(term)) goto err; @@ -831,7 +841,9 @@ term_destroy(struct terminal *term) free(it->item.data); tll_free(term->ptmx_buffer); tll_free(term->tab_stops); + free(term->foot_exe); + free(term->cwd); int ret = EXIT_SUCCESS; @@ -1672,6 +1684,7 @@ term_spawn_new(const struct terminal *term) if (pid2 == 0) { /* Child */ close(pipe_fds[0]); + chdir(term->cwd); execlp(term->foot_exe, term->foot_exe, NULL); write(pipe_fds[1], &errno, sizeof(errno)); _exit(errno); diff --git a/terminal.h b/terminal.h index b7a92be4..0fe4c4dc 100644 --- a/terminal.h +++ b/terminal.h @@ -316,6 +316,7 @@ struct terminal { void *shutdown_data; char *foot_exe; + char *cwd; }; struct config; From c3a23cf5b7a06ed5e218c6bf912aa666836857fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Dec 2019 19:42:59 +0100 Subject: [PATCH 4/4] osc: implement OSC 7 - set PWD OSC 7 updates the terminal's view of the current working directory. The format is OSC 7;URI ST We decode the URI and updates the term structs 'cwd' member. This is then used when spawning a new terminal instance. --- osc.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/osc.c b/osc.c index d69c34b9..d327d376 100644 --- a/osc.c +++ b/osc.c @@ -298,6 +298,66 @@ parse_rgb(const char *string, uint32_t *color) return true; } +static uint8_t +nibble2hex(char c) +{ + switch (c) { + case '0' ... '9': return c - '0'; + case 'a' ... 'f': return c - 'a' + 10; + case 'A' ... 'F': return c - 'A' + 10; + } + + assert(false); + return 0; +} + +static void +osc_set_pwd(struct terminal *term, char *string) +{ + LOG_DBG("PWD: URI: %s", string); + + if (memcmp(string, "file://", 7) != 0) + return; + string += 7; + + /* Skip past hostname */ + if ((string = strchr(string, '/')) == NULL) + return; + + /* Decode %xx encoded characters */ + char *pwd = malloc(strlen(string) + 1); + char *p = pwd; + + while (true) { + /* Find next '%' */ + char *next = strchr(string, '%'); + + if (next == NULL) { + strcpy(p, string); + break; + } + + /* Copy everything leading up to the '%' */ + size_t prefix_len = next - string; + memcpy(p, string, prefix_len); + p += prefix_len; + + if (isxdigit(next[1]) && isxdigit(next[2])) { + *p++ = nibble2hex(next[1]) << 4 | nibble2hex(next[2]); + *p = '\0'; + string = next + 3; + } else { + *p++ = *next; + *p = '\0'; + string = next + 1; + } + } + + LOG_DBG("PWD: decoded: %s", pwd); + free(term->cwd); + term->cwd = pwd; +} + #if 0 static void osc_notify(struct terminal *term, char *string) @@ -396,6 +456,11 @@ osc_dispatch(struct terminal *term) break; } + case 7: + /* Update terminal's understanding of PWD */ + osc_set_pwd(term, string); + break; + case 10: case 11: { /* Set default foreground/background color */