#include #include #include #include "os/input.h" #include "os/event.h" #include "vim.h" #include "globals.h" #include "ui.h" #include "types.h" #include "fileio.h" #include "getchar.h" #include "term.h" #include "misc2.h" #define READ_BUFFER_LENGTH 4096 typedef struct { uv_buf_t uvbuf; uint32_t rpos, wpos, fpos; char_u data[READ_BUFFER_LENGTH]; bool reading; } ReadBuffer; static ReadBuffer rbuffer; static uv_pipe_t read_stream; /* Use an idle handle to make reading from the fs look like a normal libuv * event */ static uv_idle_t fread_idle; static uv_handle_type read_channel_type; static bool eof = false; static EventType inbuf_poll(int32_t ms); static void stderr_switch(void); static void alloc_cb(uv_handle_t *, size_t, uv_buf_t *); static void read_cb(uv_stream_t *, ssize_t, const uv_buf_t *); static void fread_idle_cb(uv_idle_t *, int); void input_init() { rbuffer.wpos = rbuffer.rpos = rbuffer.fpos = 0; #ifdef DEBUG memset(&rbuffer.data, 0, READ_BUFFER_LENGTH); #endif if ((read_channel_type = uv_guess_handle(read_cmd_fd)) == UV_FILE) { uv_idle_init(uv_default_loop(), &fread_idle); } else { uv_pipe_init(uv_default_loop(), &read_stream, 0); uv_pipe_open(&read_stream, read_cmd_fd); } } /* Check if there's a pending input event */ EventType input_check() { if (rbuffer.rpos < rbuffer.wpos) { return kEventInput; } if (eof) { return kEventEof; } return kEventNone; } /* Listen for input */ void input_start() { /* Pin the buffer used by libuv */ rbuffer.uvbuf.len = READ_BUFFER_LENGTH - rbuffer.wpos; rbuffer.uvbuf.base = (char *)(rbuffer.data + rbuffer.wpos); if (read_channel_type == UV_FILE) { /* Just invoke the `fread_idle_cb` as soon as there are no pending events */ uv_idle_start(&fread_idle, fread_idle_cb); } else { /* Start reading */ rbuffer.reading = false; uv_read_start((uv_stream_t *)&read_stream, alloc_cb, read_cb); } } /* Stop listening for input */ void input_stop() { if (read_channel_type == UV_FILE) { uv_idle_stop(&fread_idle); } else { uv_read_stop((uv_stream_t *)&read_stream); } } /* Copies (at most `count`) of was read from `read_cmd_fd` into `buf` */ uint32_t input_read(char *buf, uint32_t count) { uint32_t read_count = rbuffer.wpos - rbuffer.rpos; if (count < read_count) { read_count = count; } if (read_count > 0) { memcpy(buf, rbuffer.data + rbuffer.rpos, read_count); rbuffer.rpos += read_count; } if (rbuffer.wpos == READ_BUFFER_LENGTH) { /* `wpos` is at the end of the buffer, so free some space by moving unread * data... */ memmove( rbuffer.data,/* ...To the beginning of the buffer(rpos 0) */ rbuffer.data + rbuffer.rpos,/* ...From the first unread position */ rbuffer.wpos - rbuffer.rpos/* ...By the number of unread bytes */ ); rbuffer.wpos -= rbuffer.rpos; rbuffer.rpos = 0; } return read_count; } /* Low level input function. */ int mch_inchar(char_u *buf, int maxlen, long ms, int tb_change_cnt) { EventType result; if (ms >= 0) { if ((result = inbuf_poll(ms)) != kEventInput) { return 0; } } else { if ((result = inbuf_poll(p_ut)) != kEventInput) { if (trigger_cursorhold() && maxlen >= 3 && !typebuf_changed(tb_change_cnt)) { buf[0] = K_SPECIAL; buf[1] = KS_EXTRA; buf[2] = KE_CURSORHOLD; return 3; } before_blocking(); result = inbuf_poll(-1); } } /* If input was put directly in typeahead buffer bail out here. */ if (typebuf_changed(tb_change_cnt)) return 0; if (result == kEventEof) { read_error_exit(); return 0; } return read_from_input_buf(buf, (long)maxlen); } /* Check if a character is available for reading */ bool mch_char_avail() { return inbuf_poll(0) == kEventInput; } /* * Check for CTRL-C typed by reading all available characters. * In cooked mode we should get SIGINT, no need to check. */ void mch_breakcheck() { if (curr_tmode == TMODE_RAW && event_poll(0) == kEventInput) fill_input_buf(FALSE); } /* This is a replacement for the old `WaitForChar` function in os_unix.c */ static EventType inbuf_poll(int32_t ms) { if (input_available()) return kEventInput; return event_poll(ms); } static void stderr_switch() { int mode = cur_tmode; /* We probably set the wrong file descriptor to raw mode. Switch back to * cooked mode */ settmode(TMODE_COOK); /* Stop the idle handle */ uv_idle_stop(&fread_idle); /* Use stderr for stdin, also works for shell commands. */ read_cmd_fd = 2; /* Initialize and start the input stream */ uv_pipe_init(uv_default_loop(), &read_stream, 0); uv_pipe_open(&read_stream, read_cmd_fd); uv_read_start((uv_stream_t *)&read_stream, alloc_cb, read_cb); rbuffer.reading = false; /* Set the mode back to what it was */ settmode(mode); } /* Called by libuv to allocate memory for reading. */ static void alloc_cb(uv_handle_t *handle, size_t suggested, uv_buf_t *buf) { if (rbuffer.reading) { buf->len = 0; return; } buf->base = rbuffer.uvbuf.base; buf->len = rbuffer.uvbuf.len; /* Avoid `alloc_cb`, `alloc_cb` sequences on windows */ rbuffer.reading = true; } /* * Callback invoked by libuv after it copies the data into the buffer provided * by `alloc_cb`. This is also called on EOF or when `alloc_cb` returns a * 0-length buffer. */ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf) { if (cnt <= 0) { if (cnt != UV_ENOBUFS) { /* Read error or EOF, either way vim must exit */ eof = true; } return; } /* Data was already written, so all we need is to update 'wpos' to reflect * the space actually used in the buffer. */ rbuffer.wpos += cnt; } /* Called by the by the 'idle' handle to emulate a reading event */ static void fread_idle_cb(uv_idle_t *handle, int status) { uv_fs_t req; /* Synchronous read */ uv_fs_read( uv_default_loop(), &req, read_cmd_fd, &rbuffer.uvbuf, 1, rbuffer.fpos, NULL ); uv_fs_req_cleanup(&req); if (req.result <= 0) { if (rbuffer.fpos == 0 && uv_guess_handle(2) == UV_TTY) { /* Read error. Since stderr is a tty we switch to reading from it. This * is for handling for cases like "foo | xargs vim" because xargs * redirects stdin from /dev/null. Previously, this was done in ui.c */ stderr_switch(); } else { eof = true; } return; } rbuffer.wpos += req.result; rbuffer.fpos += req.result; }