aboutsummaryrefslogtreecommitdiff
path: root/xC.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-08-29 14:10:23 +0200
committerPřemysl Eric Janouch <p@janouch.name>2024-11-12 17:11:26 +0100
commit30462d8bf32e1b0683af770e57867b33d1457703 (patch)
tree453309001ea26252e3ce99be8f26a708218e0fec /xC.c
parent93a4dbfe2eee094d61fc04caa0d5eb5089eab287 (diff)
downloadxK-30462d8bf32e1b0683af770e57867b33d1457703.tar.gz
xK-30462d8bf32e1b0683af770e57867b33d1457703.tar.xz
xK-30462d8bf32e1b0683af770e57867b33d1457703.zip
WIP: xC: force libedit into asynchronicity
Diffstat (limited to 'xC.c')
-rw-r--r--xC.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/xC.c b/xC.c
index df73492..2f6cf85 100644
--- a/xC.c
+++ b/xC.c
@@ -14778,9 +14778,121 @@ on_editline_return (EditLine *editline, int key)
return CC_REFRESH;
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#ifdef EDITLINE_ASYNC
+
+// This is an adjusted version of EL_BUILTIN_GETCFN.
+static int
+app_editline_wgetc (struct app_context *ctx, wchar_t *wch, bool *readable)
+{
+ char buf[MB_LEN_MAX] = {};
+ size_t buf_len = 0;
+ mbstate_t mbs;
+
+ *wch = L'\0';
+ while (ctx->polling)
+ {
+ *readable = false;
+ poller_run (&ctx->poller);
+ if (!*readable)
+ continue;
+
+ ssize_t n_read;
+ if ((n_read = read (STDIN_FILENO, buf + buf_len++, 1)) <= 0)
+ return n_read;
+
+retry:
+ // XXX: This will only really work for non-shifting encodings.
+ memset (&mbs, 0, sizeof mbs);
+ switch (mbrtowc (wch, buf, buf_len, &mbs)) {
+ case (size_t) -1:
+ // Invalid sequence, discard all bytes except any last one.
+ // Not exactly sure what this is supposed to help.
+ if (!--buf_len)
+ continue;
+
+ buf[0] = buf[buf_len];
+ buf_len = 1;
+ goto retry;
+ case (size_t) -2:
+ if (buf_len < sizeof buf)
+ continue;
+
+ errno = EILSEQ;
+ *wch = L'\0';
+ return -1;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+on_editline_tty_readable (const struct pollfd *fd, bool *readable)
+{
+ (void) fd;
+ *readable = true;
+}
+
+static int
+on_editline_wgetc (EditLine *editline, wchar_t *wch)
+{
+ struct app_context *ctx = g_ctx;
+ struct input_el *self = NULL;
+ el_get (editline, EL_CLIENTDATA, &self);
+
+ // We must not touch ourselves at any cost, Editline isn't that reentrant.
+ // TODO: Also defer SIGWINCH, since we keep the foreground process group.
+ self->active = false;
+ ctx->terminal_suspended++;
+ poller_fd_reset (&ctx->tty_event);
+
+ bool readable = false;
+ struct poller_fd tty_event_redirect =
+ poller_fd_make (&ctx->poller, ctx->tty_event.fd);
+ tty_event_redirect.dispatcher = (poller_fd_fn) on_editline_tty_readable;
+ tty_event_redirect.user_data = &readable;
+
+ poller_fd_set (&tty_event_redirect, POLLIN);
+ int result = app_editline_wgetc (ctx, wch, &readable);
+ poller_fd_reset (&tty_event_redirect);
+
+ poller_fd_set (&ctx->tty_event, POLLIN);
+ ctx->terminal_suspended--;
+ self->active = true;
+
+ // TODO: If needed, at the end of input_el_on_tty_readable():
+ // - buffer_print_backlog (ctx, ctx->current_buffer);
+ // - update Editline's prompt.
+ // What's hard is detecting when it's needed, because it's a corner case:
+ // - in general, any time buffer_print_backlog() is called,
+ // - when log_formatter() doesn't display something for current_buffer,
+ // - ...
+
+ // TODO: It seems like _buffer_switch() should be deferred,
+ // at least in case of Editline--it can't wreak much async havoc,
+ // seeing as it returns right after any command is done.
+ //
+ // What's worse is that input buffers may need to be removed,
+ // which would require _buffer_destroy() to take ownership of ::current,
+ // since we can't destroy /or change/ Editline's current history pointer.
+ return result;
+}
+
+#endif // EDITLINE_ASYNC
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static void
app_editline_init (struct input_el *self)
{
+#ifdef EDITLINE_ASYNC
+ // Avoid timeouts while reading in sequences
+ el_wset (self->editline, EL_GETCFN, on_editline_wgetc);
+#endif // EDITLINE_ASYNC
+
// el_set() leaks memory in 20150325 and other versions, we need wchar_t
el_wset (self->editline, EL_ADDFN,
L"send-line", L"Send line", on_editline_return);