1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
|
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <uv.h>
#include "nvim/os/input.h"
#include "nvim/os/event.h"
#include "nvim/os/rstream_defs.h"
#include "nvim/os/rstream.h"
#include "nvim/ascii.h"
#include "nvim/vim.h"
#include "nvim/ui.h"
#include "nvim/fileio.h"
#include "nvim/getchar.h"
#include "nvim/term.h"
#define READ_BUFFER_SIZE 256
typedef enum {
kInputNone,
kInputAvail,
kInputEof
} InbufPollResult;
static RStream *read_stream;
static bool eof = false, started_reading = false;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/input.c.generated.h"
#endif
// Helper function used to push bytes from the 'event' key sequence partially
// between calls to os_inchar when maxlen < 3
void input_init(void)
{
read_stream = rstream_new(read_cb, READ_BUFFER_SIZE, NULL, NULL);
rstream_set_file(read_stream, read_cmd_fd);
}
// Listen for input
void input_start(void)
{
rstream_start(read_stream);
}
// Stop listening for input
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)
{
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)
{
InbufPollResult result;
if (event_has_deferred()) {
// Return pending event bytes
return push_event_key(buf, maxlen);
}
if (ms >= 0) {
if ((result = inbuf_poll(ms)) == kInputNone) {
return 0;
}
} else {
if ((result = inbuf_poll(p_ut)) == kInputNone) {
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 there are deferred events, return the keys directly
if (event_has_deferred()) {
return push_event_key(buf, maxlen);
}
// If input was put directly in typeahead buffer bail out here.
if (typebuf_changed(tb_change_cnt)) {
return 0;
}
if (result == kInputEof) {
read_error_exit();
return 0;
}
return read_from_input_buf(buf, (int64_t)maxlen);
}
// Check if a character is available for reading
bool os_char_avail(void)
{
return inbuf_poll(0) == kInputAvail;
}
// Check for CTRL-C typed by reading all available characters.
// In cooked mode we should get SIGINT, no need to check.
void os_breakcheck(void)
{
if (curr_tmode == TMODE_RAW && input_poll(0))
fill_input_buf(false);
}
/// Test whether a file descriptor refers to a terminal.
///
/// @param fd File descriptor.
/// @return `true` if file descriptor refers to a terminal.
bool os_isatty(int fd)
{
return uv_guess_handle(fd) == UV_TTY;
}
static bool input_poll(int32_t ms)
{
EventSource input_sources[] = {
rstream_event_source(read_stream),
NULL
};
return input_ready() || event_poll(ms, input_sources) || input_ready();
}
// This is a replacement for the old `WaitForChar` function in os_unix.c
static InbufPollResult inbuf_poll(int32_t ms)
{
if (input_available()) {
return kInputAvail;
}
if (input_poll(ms)) {
return eof && rstream_available(read_stream) == 0 ?
kInputEof :
kInputAvail;
}
return kInputNone;
}
static void stderr_switch(void)
{
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
rstream_stop(read_stream);
// Use stderr for stdin, also works for shell commands.
read_cmd_fd = 2;
// Initialize and start the input stream
rstream_set_file(read_stream, read_cmd_fd);
rstream_start(read_stream);
// Set the mode back to what it was
settmode(mode);
}
static void read_cb(RStream *rstream, void *data, bool at_eof)
{
if (at_eof) {
if (!started_reading
&& rstream_is_regular_file(rstream)
&& os_isatty(STDERR_FILENO)) {
// 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;
}
}
started_reading = true;
}
static int push_event_key(uint8_t *buf, int maxlen)
{
static const uint8_t key[3] = { K_SPECIAL, KS_EXTRA, KE_EVENT };
static int key_idx = 0;
int buf_idx = 0;
do {
buf[buf_idx++] = key[key_idx++];
key_idx %= 3;
} while (key_idx > 0 && buf_idx < maxlen);
return buf_idx;
}
// Check if there's pending input
bool input_ready(void)
{
return rstream_available(read_stream) > 0 || eof;
}
|