refactor(input): don't use a ring for input

Since paste data is handled via a separate channel, the data processed via `input_buffer` is typically just explicit keys as typed in by the user. Therefore it should be fine to use `memmove()` to always put the remaining data in front when refilling the buffer.
This commit is contained in:
bfredl 2024-05-31 20:36:16 +02:00
parent 1d091e52f0
commit 6d6974eae6
4 changed files with 61 additions and 54 deletions

View File

@ -313,7 +313,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_ks)
keys_esc = keys.data;
}
if (lowlevel) {
input_enqueue_raw(cstr_as_string(keys_esc));
input_enqueue_raw(keys_esc, strlen(keys_esc));
} else {
ins_typebuf(keys_esc, (remap ? REMAP_YES : REMAP_NONE),
insert ? 0 : typebuf.tb_len, !typed, false);

View File

@ -154,7 +154,6 @@ void event_init(void)
loop_init(&main_loop, NULL);
resize_events = multiqueue_new_child(main_loop.events);
input_init();
signal_init();
// mspgack-rpc initialization
channel_init();

View File

@ -882,7 +882,6 @@ void free_all_mem(void)
decor_free_all_mem();
drawline_free_all_mem();
input_free_all_mem();
if (ui_client_channel_id) {
ui_client_free_all_mem();

View File

@ -33,7 +33,7 @@
#include "nvim/state_defs.h"
#define READ_BUFFER_SIZE 0xfff
#define INPUT_BUFFER_SIZE (READ_BUFFER_SIZE * 4)
#define INPUT_BUFFER_SIZE ((READ_BUFFER_SIZE * 4) + MAX_KEY_CODE_LEN)
typedef enum {
kInputNone,
@ -42,7 +42,10 @@ typedef enum {
} InbufPollResult;
static RStream read_stream = { .s.closed = true }; // Input before UI starts.
static RBuffer *input_buffer = NULL;
static char input_buffer[INPUT_BUFFER_SIZE];
static char *input_read_pos = input_buffer;
static char *input_write_pos = input_buffer;
static bool input_eof = false;
static bool blocking = false;
static int cursorhold_time = 0; ///< time waiting for CursorHold event
@ -52,11 +55,6 @@ static int cursorhold_tb_change_cnt = 0; ///< tb_change_cnt when waiting starte
# include "os/input.c.generated.h"
#endif
void input_init(void)
{
input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN);
}
void input_start(void)
{
if (!read_stream.s.closed) {
@ -78,13 +76,6 @@ void input_stop(void)
rstream_may_close(&read_stream);
}
#ifdef EXITFREE
void input_free_all_mem(void)
{
rbuffer_free(input_buffer);
}
#endif
static void cursorhold_event(void **argv)
{
event_T event = State & MODE_INSERT ? EVENT_CURSORHOLDI : EVENT_CURSORHOLD;
@ -119,9 +110,12 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
restart_cursorhold_wait(tb_change_cnt);
}
if (maxlen && rbuffer_size(input_buffer)) {
if (maxlen && input_available()) {
restart_cursorhold_wait(tb_change_cnt);
return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen);
size_t to_read = MIN((size_t)maxlen, input_available());
memcpy(buf, input_read_pos, to_read);
input_read_pos += to_read;
return (int)to_read;
}
// No risk of a UI flood, so disable CTRL-C "interrupt" behavior if it's mapped.
@ -161,11 +155,14 @@ int os_inchar(uint8_t *buf, int maxlen, int ms, int tb_change_cnt, MultiQueue *e
return 0;
}
if (maxlen && rbuffer_size(input_buffer)) {
if (maxlen && input_available()) {
restart_cursorhold_wait(tb_change_cnt);
// Safe to convert rbuffer_read to int, it will never overflow since we use
// relatively small buffers.
return (int)rbuffer_read(input_buffer, (char *)buf, (size_t)maxlen);
// Safe to convert rbuffer_read to int, it will never overflow since
// INPUT_BUFFER_SIZE fits in an int
size_t to_read = MIN((size_t)maxlen, input_available());
memcpy(buf, input_read_pos, to_read);
input_read_pos += to_read;
return (int)to_read;
}
// If there are events, return the keys directly
@ -247,11 +244,28 @@ bool os_isatty(int fd)
return uv_guess_handle(fd) == UV_TTY;
}
void input_enqueue_raw(String keys)
size_t input_available(void)
{
if (keys.size > 0) {
rbuffer_write(input_buffer, keys.data, keys.size);
return (size_t)(input_write_pos - input_read_pos);
}
static size_t input_space(void)
{
return (size_t)(input_buffer + INPUT_BUFFER_SIZE - input_write_pos);
}
void input_enqueue_raw(const char *data, size_t size)
{
if (input_read_pos > input_buffer) {
size_t available = input_available();
memmove(input_buffer, input_read_pos, available);
input_read_pos = input_buffer;
input_write_pos = input_buffer + available;
}
size_t to_write = MIN(size, input_space());
memcpy(input_write_pos, data, to_write);
input_write_pos += to_write;
}
size_t input_enqueue(String keys)
@ -259,7 +273,7 @@ size_t input_enqueue(String keys)
const char *ptr = keys.data;
const char *end = ptr + keys.size;
while (rbuffer_space(input_buffer) >= 19 && ptr < end) {
while (input_space() >= 19 && ptr < end) {
// A "<x>" form occupies at least 1 characters, and produces up
// to 19 characters (1 + 5 * 3 for the char and 3 for a modifier).
// In the case of K_SPECIAL (0x80), 3 bytes are escaped and needed,
@ -272,7 +286,7 @@ size_t input_enqueue(String keys)
if (new_size) {
new_size = handle_mouse_event(&ptr, buf, new_size);
rbuffer_write(input_buffer, (char *)buf, new_size);
input_enqueue_raw((char *)buf, new_size);
continue;
}
@ -293,11 +307,11 @@ size_t input_enqueue(String keys)
// copy the character, escaping K_SPECIAL
if ((uint8_t)(*ptr) == K_SPECIAL) {
rbuffer_write(input_buffer, (char *)&(uint8_t){ K_SPECIAL }, 1);
rbuffer_write(input_buffer, (char *)&(uint8_t){ KS_SPECIAL }, 1);
rbuffer_write(input_buffer, (char *)&(uint8_t){ KE_FILLER }, 1);
input_enqueue_raw((char *)&(uint8_t){ K_SPECIAL }, 1);
input_enqueue_raw((char *)&(uint8_t){ KS_SPECIAL }, 1);
input_enqueue_raw((char *)&(uint8_t){ KE_FILLER }, 1);
} else {
rbuffer_write(input_buffer, ptr, 1);
input_enqueue_raw(ptr, 1);
}
ptr++;
}
@ -422,7 +436,7 @@ static unsigned handle_mouse_event(const char **ptr, uint8_t *buf, unsigned bufs
return bufsize;
}
size_t input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col)
void input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int col)
{
modifier |= check_multiclick(code, grid, row, col);
uint8_t buf[7];
@ -442,8 +456,7 @@ size_t input_enqueue_mouse(int code, uint8_t modifier, int grid, int row, int co
mouse_col = col;
size_t written = 3 + (size_t)(p - buf);
rbuffer_write(input_buffer, (char *)buf, written);
return written;
input_enqueue_raw((char *)buf, written);
}
/// @return true if the main loop is blocked and waiting for input.
@ -484,20 +497,15 @@ static InbufPollResult inbuf_poll(int ms, MultiQueue *events)
return input_eof ? kInputEof : kInputNone;
}
bool input_available(void)
{
return rbuffer_size(input_buffer) != 0;
}
static void input_read_cb(RStream *stream, RBuffer *buf, size_t c, void *data, bool at_eof)
{
if (at_eof) {
input_eof = true;
}
assert(rbuffer_space(input_buffer) >= rbuffer_size(buf));
assert(input_space() >= rbuffer_size(buf));
RBUFFER_UNTIL_EMPTY(buf, ptr, len) {
(void)rbuffer_write(input_buffer, ptr, len);
input_enqueue_raw(ptr, len);
rbuffer_consumed(buf, len);
}
}
@ -508,23 +516,24 @@ static void process_ctrl_c(void)
return;
}
size_t consume_count = 0;
RBUFFER_EACH_REVERSE(input_buffer, c, i) {
if ((uint8_t)c == Ctrl_C
|| ((uint8_t)c == 'C' && i >= 3
&& (uint8_t)(*rbuffer_get(input_buffer, i - 3)) == K_SPECIAL
&& (uint8_t)(*rbuffer_get(input_buffer, i - 2)) == KS_MODIFIER
&& (uint8_t)(*rbuffer_get(input_buffer, i - 1)) == MOD_MASK_CTRL)) {
*rbuffer_get(input_buffer, i) = Ctrl_C;
size_t available = input_available();
ssize_t i;
for (i = (ssize_t)available - 1; i >= 0; i--) {
uint8_t c = (uint8_t)input_read_pos[i];
if (c == Ctrl_C
|| (c == 'C' && i >= 3
&& (uint8_t)input_read_pos[i - 3] == K_SPECIAL
&& (uint8_t)input_read_pos[i - 2] == KS_MODIFIER
&& (uint8_t)input_read_pos[i - 1] == MOD_MASK_CTRL)) {
input_read_pos[i] = Ctrl_C;
got_int = true;
consume_count = i;
break;
}
}
if (got_int && consume_count) {
if (got_int && i > 0) {
// Remove all unprocessed input (typeahead) before the CTRL-C.
rbuffer_consumed(input_buffer, consume_count);
input_read_pos += i;
}
}
@ -548,7 +557,7 @@ static int push_event_key(uint8_t *buf, int maxlen)
bool os_input_ready(MultiQueue *events)
{
return (typebuf_was_filled // API call filled typeahead
|| rbuffer_size(input_buffer) // Input buffer filled
|| input_available() // Input buffer filled
|| pending_events(events)); // Events must be processed
}