From 062b0eb7ab69e7821ab79763bfdc3648fdbb5f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 15 Jul 2020 13:34:32 +0200 Subject: [PATCH] input: wip: initial support for piping scrollback/view to external tools --- input.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++--- wayland.h | 2 +- 2 files changed, 133 insertions(+), 8 deletions(-) diff --git a/input.c b/input.c index af0619ac..00382c4f 100644 --- a/input.c +++ b/input.c @@ -5,10 +5,12 @@ #include #include #include +#include #include #include #include #include +#include #include @@ -28,13 +30,54 @@ #include "render.h" #include "search.h" #include "selection.h" +#include "spawn.h" #include "terminal.h" +#include "tokenize.h" #include "util.h" #include "vt.h" +struct pipe_context { + char *text; + size_t idx; + size_t left; +}; + +static bool +fdm_write_pipe(struct fdm *fdm, int fd, int events, void *data) +{ + struct pipe_context *ctx = data; + + if (events & EPOLLHUP) + goto pipe_closed; + + assert(events & EPOLLOUT); + ssize_t written = write(fd, &ctx->text[ctx->idx], ctx->left); + + if (written < 0) { + LOG_WARN("failed to write to pipe: %s", strerror(errno)); + goto pipe_closed; + } + + assert(written <= ctx->left); + ctx->idx += written; + ctx->left -= written; + + if (ctx->left == 0) + goto pipe_closed; + + return true; + +pipe_closed: + free(ctx->text); + free(ctx); + fdm_del(fdm, fd); + return true; +} + static void execute_binding(struct seat *seat, struct terminal *term, - enum bind_action_normal action, uint32_t serial) + enum bind_action_normal action, const char *pipe_cmd, + uint32_t serial) { switch (action) { case BIND_ACTION_NONE: @@ -102,10 +145,89 @@ execute_binding(struct seat *seat, struct terminal *term, break; case BIND_ACTION_PIPE_SCROLLBACK: - case BIND_ACTION_PIPE_VIEW: - LOG_ERR("unimplemented"); + case BIND_ACTION_PIPE_VIEW: { + if (pipe_cmd == NULL) + break; + + struct pipe_context *ctx = NULL; + + char *cmd = strdup(pipe_cmd); + char **argv = NULL; + + if (!tokenize_cmdline(cmd, &argv)) + goto pipe_err; + + int pipe_fd[2] = {-1, -1}; + if (pipe(pipe_fd) < 0) { + LOG_ERRNO("failed to create pipe"); + goto pipe_err; + } + + char *text; + size_t len; + + bool success = action == BIND_ACTION_PIPE_SCROLLBACK + ? term_scrollback_to_text(term, &text, &len) + : term_view_to_text(term, &text, &len); + + if (!success) + goto pipe_err; + + /* Make write-end non-blocking; required by the FDM */ + { + int flags = fcntl(pipe_fd[1], F_GETFL); + if (flags < 0 || + fcntl(pipe_fd[1], F_SETFL, flags | O_NONBLOCK) < 0) + { + LOG_ERRNO("failed to make write-end of pipe non-blocking"); + goto pipe_err; + } + } + + /* Make sure write-end is closed on exec() */ + { + int flags = fcntl(pipe_fd[1], F_GETFD); + if (flags < 0 || + fcntl(pipe_fd[1], F_SETFD, flags | FD_CLOEXEC) < 0) + { + LOG_ERRNO("failed to set FD_CLOEXEC on writeend of pipe"); + goto pipe_err; + } + } + + if (!spawn(term->reaper, NULL, argv, pipe_fd[0], -1, -1)) + goto pipe_err; + + /* Not needed anymore */ + free(argv); argv = NULL; + free(cmd); cmd = NULL; + + /* Close read end */ + close(pipe_fd[0]); + + ctx = malloc(sizeof(*ctx)); + *ctx = (struct pipe_context){ + .text = text, + .left = len, + }; + + if (!fdm_add(term->fdm, pipe_fd[1], EPOLLOUT, &fdm_write_pipe, ctx)) + goto pipe_err; + break; + pipe_err: + if (pipe_fd[0] >= 0) + close(pipe_fd[0]); + if (pipe_fd[1] >= 0) + close(pipe_fd[1]); + free(text); + free(argv); + free(cmd); + free(ctx); + break; + } + case BIND_ACTION_COUNT: assert(false); break; @@ -284,7 +406,10 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, tll_foreach(bindings, it) { tll_push_back( seat->kbd.bindings.key, - ((struct key_binding_normal){.bind = it->item, .action = i})); + ((struct key_binding_normal){ + .bind = it->item, + .action = i, + .pipe_cmd = wayl->conf->bindings.spawn[i]})); } tll_free(bindings); @@ -602,14 +727,14 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, /* Match symbol */ if (it->item.bind.sym == sym) { - execute_binding(seat, term, it->item.action, serial); + execute_binding(seat, term, it->item.action, it->item.pipe_cmd, serial); goto maybe_repeat; } /* Match raw key code */ tll_foreach(it->item.bind.key_codes, code) { if (code->item == key) { - execute_binding(seat, term, it->item.action, serial); + execute_binding(seat, term, it->item.action, it->item.pipe_cmd, serial); goto maybe_repeat; } } @@ -1259,7 +1384,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, continue; } - execute_binding(seat, term, binding->action, serial); + execute_binding(seat, term, binding->action, NULL, serial); break; } } diff --git a/wayland.h b/wayland.h index 9da2c856..b11af0d3 100644 --- a/wayland.h +++ b/wayland.h @@ -47,7 +47,7 @@ enum bind_action_normal { struct key_binding_normal { struct key_binding bind; enum bind_action_normal action; - const char *spawn; + const char *pipe_cmd; }; struct mouse_binding {