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..ee9900cb 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, "foot", argc, argv, + &term_shutdown_cb, &shutdown_ctx)) == NULL) goto out; if (as_server && (server = server_init(&conf, fdm, wayl)) == NULL) 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 */ diff --git a/server.c b/server.c index 143af512..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, - 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/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..f7f98407 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,8 +600,19 @@ 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,6 +842,9 @@ term_destroy(struct terminal *term) tll_free(term->ptmx_buffer); tll_free(term->tab_stops); + free(term->foot_exe); + free(term->cwd); + int ret = EXIT_SUCCESS; if (term->slave > 0) { @@ -1642,3 +1656,67 @@ 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]); + chdir(term->cwd); + execlp(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..0fe4c4dc 100644 --- a/terminal.h +++ b/terminal.h @@ -314,12 +314,15 @@ struct terminal { bool is_shutting_down; void (*shutdown_cb)(void *data, int exit_code); void *shutdown_data; + + char *foot_exe; + char *cwd; }; 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 +382,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);