diff options
| author | Přemysl Eric Janouch <p@janouch.name> | 2022-08-29 14:10:23 +0200 | 
|---|---|---|
| committer | Přemysl Eric Janouch <p@janouch.name> | 2024-11-07 11:07:36 +0100 | 
| commit | 95eba1ce1e6445d6b21ee5480b909ed8a6e47a37 (patch) | |
| tree | fb51784a25136003c4485226f5c776d7c8aaaa60 | |
| parent | dbe0fa2ee10c5577a328ea0a7411ee1c5ed4df67 (diff) | |
| download | xK-95eba1ce1e6445d6b21ee5480b909ed8a6e47a37.tar.gz xK-95eba1ce1e6445d6b21ee5480b909ed8a6e47a37.tar.xz xK-95eba1ce1e6445d6b21ee5480b909ed8a6e47a37.zip | |
WIP: xC: force libedit into asynchronicity
| -rw-r--r-- | xC.c | 112 | 
1 files changed, 112 insertions, 0 deletions
| @@ -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); | 
