ui: Refactor input buffer handling

All input buffer code was moved to os/input.c, and `inbuf` is now a `RBuffer`
instance(which abstracts static buffer manipulation).
This commit is contained in:
Thiago de Arruda 2014-10-18 08:38:15 -03:00
parent 68de5d79a2
commit 42112e04a9
7 changed files with 100 additions and 330 deletions

View File

@ -21,6 +21,8 @@
#include "nvim/eval_defs.h"
// for proftime_T
#include "nvim/profile.h"
// for String
#include "nvim/api/private/defs.h"
/*
* Flags for w_valid.
@ -311,9 +313,7 @@ typedef struct {
int old_mod_mask;
buffheader_T save_readbuf1;
buffheader_T save_readbuf2;
#ifdef USE_INPUT_BUF
char_u *save_inputbuf;
#endif
String save_inputbuf;
} tasave_T;
/*

View File

@ -50,6 +50,7 @@
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/os/event.h"
#include "nvim/os/input.h"
/*
* These buffers are used for storing:
@ -1201,9 +1202,7 @@ void save_typeahead(tasave_T *tp)
readbuf1.bh_first.b_next = NULL;
tp->save_readbuf2 = readbuf2;
readbuf2.bh_first.b_next = NULL;
# ifdef USE_INPUT_BUF
tp->save_inputbuf = get_input_buf();
# endif
tp->save_inputbuf = input_buffer_save();
}
/*
@ -1224,9 +1223,7 @@ void restore_typeahead(tasave_T *tp)
readbuf1 = tp->save_readbuf1;
free_buff(&readbuf2);
readbuf2 = tp->save_readbuf2;
# ifdef USE_INPUT_BUF
set_input_buf(tp->save_inputbuf);
# endif
input_buffer_restore(tp->save_inputbuf);
}
/*
@ -2551,21 +2548,6 @@ fix_input_buffer (
return len;
}
#if defined(USE_INPUT_BUF) || defined(PROTO)
/*
* Return TRUE when bytes are in the input buffer or in the typeahead buffer.
* Normally the input buffer would be sufficient, but feedkeys() may insert
* characters in the typeahead buffer while we are waiting for input to arrive.
*/
int input_available(void)
{
return !vim_is_input_buf_empty()
|| typebuf_was_filled
;
}
#endif
/*
* map[!] : show all key mappings
* map[!] {lhs} : show key mapping for {lhs}

View File

@ -3803,50 +3803,6 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8,
return OK;
}
#if defined(FEAT_GUI) || defined(WIN3264) || defined(PROTO)
/*
* Do conversion on typed input characters in-place.
* The input and output are not NUL terminated!
* Returns the length after conversion.
*/
int convert_input(char_u *ptr, int len, int maxlen)
{
return convert_input_safe(ptr, len, maxlen, NULL, NULL);
}
#endif
/*
* Like convert_input(), but when there is an incomplete byte sequence at the
* end return that as an allocated string in "restp" and set "*restlenp" to
* the length. If "restp" is NULL it is not used.
*/
int convert_input_safe(char_u *ptr, int len, int maxlen, char_u **restp,
int *restlenp)
{
char_u *d;
int dlen = len;
int unconvertlen = 0;
d = string_convert_ext(&input_conv, ptr, &dlen,
restp == NULL ? NULL : &unconvertlen);
if (d != NULL) {
if (dlen <= maxlen) {
if (unconvertlen > 0) {
/* Move the unconverted characters to allocated memory. */
*restp = xmalloc(unconvertlen);
memmove(*restp, ptr + len - unconvertlen, unconvertlen);
*restlenp = unconvertlen;
}
memmove(ptr, d, dlen);
} else
/* result is too long, keep the unconverted text (the caller must
* have done something wrong!) */
dlen = len;
free(d);
}
return dlen;
}
/*
* Convert text "ptr[*lenp]" according to "vcp".
* Returns the result in allocated memory and sets "*lenp".

View File

@ -4,6 +4,7 @@
#include <uv.h>
#include "nvim/api/private/defs.h"
#include "nvim/os/input.h"
#include "nvim/os/event.h"
#include "nvim/os/signal.h"
@ -12,11 +13,15 @@
#include "nvim/ascii.h"
#include "nvim/vim.h"
#include "nvim/ui.h"
#include "nvim/memory.h"
#include "nvim/keymap.h"
#include "nvim/mbyte.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/term.h"
#define READ_BUFFER_SIZE 256
#define READ_BUFFER_SIZE 0xffff
#define INPUT_BUFFER_SIZE 4096
typedef enum {
kInputNone,
@ -25,6 +30,7 @@ typedef enum {
} InbufPollResult;
static RStream *read_stream;
static RBuffer *read_buffer, *input_buffer;
static bool eof = false, started_reading = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
@ -35,12 +41,15 @@ static bool eof = false, started_reading = false;
void input_init(void)
{
input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN);
if (embedded_mode) {
return;
}
read_buffer = rbuffer_new(READ_BUFFER_SIZE);
read_stream = rstream_new(read_cb,
rbuffer_new(READ_BUFFER_SIZE),
read_buffer,
NULL,
NULL);
rstream_set_file(read_stream, read_cmd_fd);
@ -66,17 +75,6 @@ void input_stop(void)
rstream_stop(read_stream);
}
// Copies (at most `count`) of was read from `read_cmd_fd` into `buf`
uint32_t input_read(char *buf, uint32_t count)
{
if (embedded_mode) {
return 0;
}
return rstream_read(read_stream, buf, count);
}
// Low level input function.
int os_inchar(uint8_t *buf, int maxlen, int32_t ms, int tb_change_cnt)
{
@ -121,7 +119,8 @@ int os_inchar(uint8_t *buf, int maxlen, int32_t ms, int tb_change_cnt)
return 0;
}
return read_from_input_buf(buf, (int64_t)maxlen);
convert_input();
return rbuffer_read(input_buffer, (char *)buf, maxlen);
}
// Check if a character is available for reading
@ -135,7 +134,7 @@ bool os_char_avail(void)
void os_breakcheck(void)
{
if (curr_tmode == TMODE_RAW && input_poll(0))
fill_input_buf(false);
convert_input();
}
/// Test whether a file descriptor refers to a terminal.
@ -147,6 +146,27 @@ bool os_isatty(int fd)
return uv_guess_handle(fd) == UV_TTY;
}
/// Return the contents of the input buffer and make it empty. The returned
/// pointer must be passed to `input_buffer_restore()` later.
String input_buffer_save(void)
{
size_t inbuf_size = rbuffer_pending(input_buffer);
String rv = {
.data = xmemdup(rbuffer_data(input_buffer), inbuf_size),
.size = inbuf_size
};
rbuffer_consumed(input_buffer, inbuf_size);
return rv;
}
/// Restore the contents of the input buffer and free `str`
void input_buffer_restore(String str)
{
rbuffer_consumed(input_buffer, rbuffer_pending(input_buffer));
rbuffer_write(input_buffer, str.data, str.size);
free(str.data);
}
static bool input_poll(int32_t ms)
{
if (embedded_mode) {
@ -165,7 +185,7 @@ static bool input_poll(int32_t ms)
// This is a replacement for the old `WaitForChar` function in os_unix.c
static InbufPollResult inbuf_poll(int32_t ms)
{
if (input_available()) {
if (typebuf_was_filled || rbuffer_pending(input_buffer)) {
return kInputAvail;
}
@ -213,6 +233,60 @@ static void read_cb(RStream *rstream, void *data, bool at_eof)
started_reading = true;
}
static void convert_input(void)
{
if (!rbuffer_available(input_buffer)) {
// No input buffer space
return;
}
bool convert = input_conv.vc_type != CONV_NONE;
// Set unconverted data/length
char *data = rbuffer_data(read_buffer);
size_t data_length = rbuffer_pending(read_buffer);
size_t converted_length = data_length;
if (convert) {
// Perform input conversion according to `input_conv`
size_t unconverted_length;
data = (char *)string_convert_ext(&input_conv,
(uint8_t *)data,
(int *)&converted_length,
(int *)&unconverted_length);
data_length = rbuffer_pending(read_buffer) - unconverted_length;
}
// Write processed data to input buffer
size_t consumed = rbuffer_write(input_buffer, data, data_length);
// Adjust raw buffer pointers
rbuffer_consumed(read_buffer, consumed);
if (convert) {
// data points to memory allocated by `string_convert_ext`, free it.
free(data);
}
if (!ctrl_c_interrupts) {
return;
}
char *inbuf = rbuffer_data(input_buffer);
size_t count = rbuffer_pending(input_buffer), consume_count = 0;
for (int i = count - 1; i >= 0; i--) {
if (inbuf[i] == 3) {
consume_count = i + 1;
break;
}
}
if (consume_count) {
// Remove everything typed before the CTRL-C
rbuffer_consumed(input_buffer, consume_count);
got_int = true;
}
}
static int push_event_key(uint8_t *buf, int maxlen)
{
static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT };
@ -228,7 +302,7 @@ static int push_event_key(uint8_t *buf, int maxlen)
}
// Check if there's pending input
bool input_ready(void)
static bool input_ready(void)
{
return rstream_pending(read_stream) > 0 || eof;
}

View File

@ -4,6 +4,8 @@
#include <stdint.h>
#include <stdbool.h>
#include "nvim/api/private/defs.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.h.generated.h"
#endif

View File

@ -228,247 +228,6 @@ void ui_breakcheck(void)
* for them.
*/
/*****************************************************************************
* Functions that handle the input buffer.
* This is used for any GUI version, and the unix terminal version.
*
* For Unix, the input characters are buffered to be able to check for a
* CTRL-C. This should be done with signals, but I don't know how to do that
* in a portable way for a tty in RAW mode.
*
* For the client-server code in the console the received keys are put in the
* input buffer.
*/
#if defined(USE_INPUT_BUF) || defined(PROTO)
/*
* Internal typeahead buffer. Includes extra space for long key code
* descriptions which would otherwise overflow. The buffer is considered full
* when only this extra space (or part of it) remains.
*/
# define INBUFLEN 4096
static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN];
static int inbufcount = 0; /* number of chars in inbuf[] */
/*
* vim_is_input_buf_full(), vim_is_input_buf_empty(), add_to_input_buf(), and
* trash_input_buf() are functions for manipulating the input buffer. These
* are used by the gui_* calls when a GUI is used to handle keyboard input.
*/
int vim_is_input_buf_full(void)
{
return inbufcount >= INBUFLEN;
}
int vim_is_input_buf_empty(void)
{
return inbufcount == 0;
}
/*
* Return the current contents of the input buffer and make it empty.
* The returned pointer must be passed to set_input_buf() later.
*/
char_u *get_input_buf(void)
{
/* We use a growarray to store the data pointer and the length. */
garray_T *gap = xmalloc(sizeof(garray_T));
/* Add one to avoid a zero size. */
gap->ga_data = xmalloc(inbufcount + 1);
if (gap->ga_data != NULL)
memmove(gap->ga_data, inbuf, (size_t)inbufcount);
gap->ga_len = inbufcount;
trash_input_buf();
return (char_u *)gap;
}
/*
* Restore the input buffer with a pointer returned from get_input_buf().
* The allocated memory is freed, this only works once!
*/
void set_input_buf(char_u *p)
{
garray_T *gap = (garray_T *)p;
if (gap != NULL) {
if (gap->ga_data != NULL) {
memmove(inbuf, gap->ga_data, gap->ga_len);
inbufcount = gap->ga_len;
free(gap->ga_data);
}
free(gap);
}
}
#if defined(FEAT_GUI) \
|| defined(FEAT_MOUSE_GPM) || defined(FEAT_SYSMOUSE) \
|| defined(FEAT_XCLIPBOARD) || defined(PROTO)
/*
* Add the given bytes to the input buffer
* Special keys start with CSI. A real CSI must have been translated to
* CSI KS_EXTRA KE_CSI. K_SPECIAL doesn't require translation.
*/
void add_to_input_buf(char_u *s, int len)
{
if (inbufcount + len > INBUFLEN + MAX_KEY_CODE_LEN)
return; /* Shouldn't ever happen! */
if ((State & (INSERT|CMDLINE)) && hangul_input_state_get())
if ((len = hangul_input_process(s, len)) == 0)
return;
while (len--)
inbuf[inbufcount++] = *s++;
}
#endif
#if ((defined(FEAT_XIM) || defined(FEAT_DND)) && defined(FEAT_GUI_GTK)) \
|| defined(FEAT_GUI_MSWIN) \
|| defined(FEAT_GUI_MAC) \
|| defined(FEAT_MBYTE_IME) \
|| defined(FEAT_GUI) \
|| defined(PROTO)
/*
* Add "str[len]" to the input buffer while escaping CSI bytes.
*/
void add_to_input_buf_csi(char_u *str, int len) {
int i;
char_u buf[2];
for (i = 0; i < len; ++i) {
add_to_input_buf(str + i, 1);
if (str[i] == CSI) {
/* Turn CSI into K_CSI. */
buf[0] = KS_EXTRA;
buf[1] = (int)KE_CSI;
add_to_input_buf(buf, 2);
}
}
}
#endif
/* Remove everything from the input buffer. Called when ^C is found */
void trash_input_buf(void)
{
inbufcount = 0;
}
/*
* Read as much data from the input buffer as possible up to maxlen, and store
* it in buf.
* Note: this function used to be Read() in unix.c
*/
int read_from_input_buf(char_u *buf, long maxlen)
{
if (inbufcount == 0) /* if the buffer is empty, fill it */
fill_input_buf(true);
if (maxlen > inbufcount)
maxlen = inbufcount;
memmove(buf, inbuf, (size_t)maxlen);
inbufcount -= maxlen;
if (inbufcount)
memmove(inbuf, inbuf + maxlen, (size_t)inbufcount);
return (int)maxlen;
}
void fill_input_buf(bool exit_on_error)
{
#if defined(UNIX) || defined(MACOS_X_UNIX)
int len;
int try;
static char_u *rest = NULL; /* unconverted rest of previous read */
static int restlen = 0;
int unconverted;
#endif
#if defined(UNIX) || defined(MACOS_X_UNIX)
if (vim_is_input_buf_full())
return;
/*
* Fill_input_buf() is only called when we really need a character.
* If we can't get any, but there is some in the buffer, just return.
* If we can't get any, and there isn't any in the buffer, we give up and
* exit Vim.
*/
if (rest != NULL) {
/* Use remainder of previous call, starts with an invalid character
* that may become valid when reading more. */
if (restlen > INBUFLEN - inbufcount)
unconverted = INBUFLEN - inbufcount;
else
unconverted = restlen;
memmove(inbuf + inbufcount, rest, unconverted);
if (unconverted == restlen) {
free(rest);
rest = NULL;
} else {
restlen -= unconverted;
memmove(rest, rest + unconverted, restlen);
}
inbufcount += unconverted;
} else
unconverted = 0;
len = 0; /* to avoid gcc warning */
for (try = 0; try < 100; ++try) {
len = input_read(
(char *)inbuf + inbufcount,
(size_t)((INBUFLEN - inbufcount) / input_conv.vc_factor));
if (len > 0 || got_int)
break;
if (!exit_on_error)
return;
}
if (len <= 0 && !got_int)
read_error_exit();
if (got_int) {
/* Interrupted, pretend a CTRL-C was typed. */
inbuf[0] = 3;
inbufcount = 1;
} else {
/*
* May perform conversion on the input characters.
* Include the unconverted rest of the previous call.
* If there is an incomplete char at the end it is kept for the next
* time, reading more bytes should make conversion possible.
* Don't do this in the unlikely event that the input buffer is too
* small ("rest" still contains more bytes).
*/
if (input_conv.vc_type != CONV_NONE) {
inbufcount -= unconverted;
len = convert_input_safe(inbuf + inbufcount,
len + unconverted, INBUFLEN - inbufcount,
rest == NULL ? &rest : NULL, &restlen);
}
while (len-- > 0) {
/*
* if a CTRL-C was typed, remove it from the buffer and set got_int
*/
if (inbuf[inbufcount] == 3 && ctrl_c_interrupts) {
/* remove everything typed before the CTRL-C */
memmove(inbuf, inbuf + inbufcount, (size_t)(len + 1));
inbufcount = 0;
got_int = TRUE;
}
++inbufcount;
}
}
#endif /* UNIX */
}
#endif /* defined(UNIX) || defined(FEAT_GUI) */
/*
* Exit because of an input read error.
*/
@ -954,3 +713,4 @@ void im_save_status(long *psave)
}
}
#endif

View File

@ -339,10 +339,6 @@ enum {
#define fnamencmp(x, y, n) vim_fnamencmp((char_u *)(x), (char_u *)(y), \
(size_t)(n))
#if defined(UNIX) || defined(FEAT_GUI)
# define USE_INPUT_BUF
#endif
#ifndef EINTR
# define read_eintr(fd, buf, count) vim_read((fd), (buf), (count))
# define write_eintr(fd, buf, count) vim_write((fd), (buf), (count))