|
160
|
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|
|
2 *
|
|
|
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
4 * of this software and associated documentation files (the "Software"), to
|
|
|
5 * deal in the Software without restriction, including without limitation the
|
|
|
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
|
7 * sell copies of the Software, and to permit persons to whom the Software is
|
|
|
8 * furnished to do so, subject to the following conditions:
|
|
|
9 *
|
|
|
10 * The above copyright notice and this permission notice shall be included in
|
|
|
11 * all copies or substantial portions of the Software.
|
|
|
12 *
|
|
|
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
|
19 * IN THE SOFTWARE.
|
|
|
20 */
|
|
|
21
|
|
|
22 #include <assert.h>
|
|
|
23 #include <io.h>
|
|
|
24 #include <string.h>
|
|
|
25 #include <stdlib.h>
|
|
|
26 #include <stdint.h>
|
|
|
27
|
|
|
28 #ifndef COMMON_LVB_REVERSE_VIDEO
|
|
|
29 # define COMMON_LVB_REVERSE_VIDEO 0x4000
|
|
|
30 #endif
|
|
|
31
|
|
|
32 #include "uv.h"
|
|
|
33 #include "internal.h"
|
|
|
34 #include "handle-inl.h"
|
|
|
35 #include "stream-inl.h"
|
|
|
36 #include "req-inl.h"
|
|
|
37
|
|
|
38 #ifndef InterlockedOr
|
|
|
39 # define InterlockedOr _InterlockedOr
|
|
|
40 #endif
|
|
|
41
|
|
|
42 #define UNICODE_REPLACEMENT_CHARACTER (0xfffd)
|
|
|
43
|
|
|
44 #define ANSI_NORMAL 0x0000
|
|
|
45 #define ANSI_ESCAPE_SEEN 0x0002
|
|
|
46 #define ANSI_CSI 0x0004
|
|
|
47 #define ANSI_ST_CONTROL 0x0008
|
|
|
48 #define ANSI_IGNORE 0x0010
|
|
|
49 #define ANSI_IN_ARG 0x0020
|
|
|
50 #define ANSI_IN_STRING 0x0040
|
|
|
51 #define ANSI_BACKSLASH_SEEN 0x0080
|
|
|
52 #define ANSI_EXTENSION 0x0100
|
|
|
53 #define ANSI_DECSCUSR 0x0200
|
|
|
54
|
|
|
55 #define MAX_INPUT_BUFFER_LENGTH 8192
|
|
|
56 #define MAX_CONSOLE_CHAR 8192
|
|
|
57
|
|
|
58 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|
|
59 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
|
|
60 #endif
|
|
|
61 #ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
|
|
|
62 #define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
|
|
|
63 #endif
|
|
|
64
|
|
|
65 #define CURSOR_SIZE_SMALL 25
|
|
|
66 #define CURSOR_SIZE_LARGE 100
|
|
|
67
|
|
|
68 static void uv__tty_capture_initial_style(
|
|
|
69 CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
|
|
|
70 CONSOLE_CURSOR_INFO* cursor_info);
|
|
|
71 static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info);
|
|
|
72 static int uv__cancel_read_console(uv_tty_t* handle);
|
|
|
73
|
|
|
74
|
|
|
75 /* Null uv_buf_t */
|
|
|
76 static const uv_buf_t uv_null_buf_ = { 0, NULL };
|
|
|
77
|
|
|
78 enum uv__read_console_status_e {
|
|
|
79 NOT_STARTED,
|
|
|
80 IN_PROGRESS,
|
|
|
81 TRAP_REQUESTED,
|
|
|
82 COMPLETED
|
|
|
83 };
|
|
|
84
|
|
|
85 static volatile LONG uv__read_console_status = NOT_STARTED;
|
|
|
86 static volatile LONG uv__restore_screen_state;
|
|
|
87 static CONSOLE_SCREEN_BUFFER_INFO uv__saved_screen_state;
|
|
|
88
|
|
|
89
|
|
|
90 /*
|
|
|
91 * The console virtual window.
|
|
|
92 *
|
|
|
93 * Normally cursor movement in windows is relative to the console screen buffer,
|
|
|
94 * e.g. the application is allowed to overwrite the 'history'. This is very
|
|
|
95 * inconvenient, it makes absolute cursor movement pretty useless. There is
|
|
|
96 * also the concept of 'client rect' which is defined by the actual size of
|
|
|
97 * the console window and the scroll position of the screen buffer, but it's
|
|
|
98 * very volatile because it changes when the user scrolls.
|
|
|
99 *
|
|
|
100 * To make cursor movement behave sensibly we define a virtual window to which
|
|
|
101 * cursor movement is confined. The virtual window is always as wide as the
|
|
|
102 * console screen buffer, but it's height is defined by the size of the
|
|
|
103 * console window. The top of the virtual window aligns with the position
|
|
|
104 * of the caret when the first stdout/err handle is created, unless that would
|
|
|
105 * mean that it would extend beyond the bottom of the screen buffer - in that
|
|
|
106 * that case it's located as far down as possible.
|
|
|
107 *
|
|
|
108 * When the user writes a long text or many newlines, such that the output
|
|
|
109 * reaches beyond the bottom of the virtual window, the virtual window is
|
|
|
110 * shifted downwards, but not resized.
|
|
|
111 *
|
|
|
112 * Since all tty i/o happens on the same console, this window is shared
|
|
|
113 * between all stdout/stderr handles.
|
|
|
114 */
|
|
|
115
|
|
|
116 static int uv_tty_virtual_offset = -1;
|
|
|
117 static int uv_tty_virtual_height = -1;
|
|
|
118 static int uv_tty_virtual_width = -1;
|
|
|
119
|
|
|
120 /* The console window size
|
|
|
121 * We keep this separate from uv_tty_virtual_*. We use those values to only
|
|
|
122 * handle signalling SIGWINCH
|
|
|
123 */
|
|
|
124
|
|
|
125 static HANDLE uv__tty_console_handle_out = INVALID_HANDLE_VALUE;
|
|
|
126 static HANDLE uv__tty_console_handle_in = INVALID_HANDLE_VALUE;
|
|
|
127 static DWORD uv__tty_console_in_original_mode = (DWORD)-1;
|
|
|
128 static volatile LONG uv__tty_console_in_need_mode_reset = 0;
|
|
|
129 static int uv__tty_console_height = -1;
|
|
|
130 static int uv__tty_console_width = -1;
|
|
|
131 static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
|
|
|
132 static uv_mutex_t uv__tty_console_resize_mutex;
|
|
|
133
|
|
|
134 static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param);
|
|
|
135 static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
|
|
|
136 DWORD event,
|
|
|
137 HWND hwnd,
|
|
|
138 LONG idObject,
|
|
|
139 LONG idChild,
|
|
|
140 DWORD dwEventThread,
|
|
|
141 DWORD dwmsEventTime);
|
|
|
142 static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param);
|
|
|
143 static void uv__tty_console_signal_resize(void);
|
|
|
144
|
|
|
145 /* We use a semaphore rather than a mutex or critical section because in some
|
|
|
146 cases (uv__cancel_read_console) we need take the lock in the main thread and
|
|
|
147 release it in another thread. Using a semaphore ensures that in such
|
|
|
148 scenario the main thread will still block when trying to acquire the lock. */
|
|
|
149 static uv_sem_t uv_tty_output_lock;
|
|
|
150
|
|
|
151 static WORD uv_tty_default_text_attributes =
|
|
|
152 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
|
153
|
|
|
154 static char uv_tty_default_fg_color = 7;
|
|
|
155 static char uv_tty_default_bg_color = 0;
|
|
|
156 static char uv_tty_default_fg_bright = 0;
|
|
|
157 static char uv_tty_default_bg_bright = 0;
|
|
|
158 static char uv_tty_default_inverse = 0;
|
|
|
159
|
|
|
160 static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info;
|
|
|
161
|
|
|
162 /* Determine whether or not ANSI support is enabled. */
|
|
|
163 static BOOL uv__need_check_vterm_state = TRUE;
|
|
|
164 static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
|
|
|
165 static void uv__determine_vterm_state(HANDLE handle);
|
|
|
166
|
|
|
167 void uv__console_init(void) {
|
|
|
168 DWORD dwMode;
|
|
|
169
|
|
|
170 if (uv_sem_init(&uv_tty_output_lock, 1))
|
|
|
171 abort();
|
|
|
172 uv__tty_console_handle_out = CreateFileW(L"CONOUT$",
|
|
|
173 GENERIC_READ | GENERIC_WRITE,
|
|
|
174 FILE_SHARE_WRITE,
|
|
|
175 0,
|
|
|
176 OPEN_EXISTING,
|
|
|
177 0,
|
|
|
178 0);
|
|
|
179 if (uv__tty_console_handle_out != INVALID_HANDLE_VALUE) {
|
|
|
180 CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
|
|
181 uv_mutex_init(&uv__tty_console_resize_mutex);
|
|
|
182 if (GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info)) {
|
|
|
183 uv__tty_console_width = sb_info.dwSize.X;
|
|
|
184 uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
|
|
|
185 }
|
|
|
186 QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
|
|
|
187 NULL,
|
|
|
188 WT_EXECUTELONGFUNCTION);
|
|
|
189 }
|
|
|
190 uv__tty_console_handle_in = CreateFileW(L"CONIN$",
|
|
|
191 GENERIC_READ | GENERIC_WRITE,
|
|
|
192 FILE_SHARE_READ,
|
|
|
193 0,
|
|
|
194 OPEN_EXISTING,
|
|
|
195 0,
|
|
|
196 0);
|
|
|
197 if (uv__tty_console_handle_in != INVALID_HANDLE_VALUE) {
|
|
|
198 if (GetConsoleMode(uv__tty_console_handle_in, &dwMode)) {
|
|
|
199 uv__tty_console_in_original_mode = dwMode;
|
|
|
200 }
|
|
|
201 }
|
|
|
202 }
|
|
|
203
|
|
|
204
|
|
|
205 int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
|
|
|
206 BOOL readable;
|
|
|
207 DWORD NumberOfEvents;
|
|
|
208 HANDLE handle;
|
|
|
209 CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
|
|
|
210 CONSOLE_CURSOR_INFO cursor_info;
|
|
|
211 (void)unused;
|
|
|
212
|
|
|
213 uv__once_init();
|
|
|
214 handle = (HANDLE) uv__get_osfhandle(fd);
|
|
|
215 if (handle == INVALID_HANDLE_VALUE)
|
|
|
216 return UV_EBADF;
|
|
|
217
|
|
|
218 if (fd <= 2) {
|
|
|
219 /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
|
|
|
220 * underlying OS handle and forget about the original fd.
|
|
|
221 * We could also opt to use the original OS handle and just never close it,
|
|
|
222 * but then there would be no reliable way to cancel pending read operations
|
|
|
223 * upon close.
|
|
|
224 */
|
|
|
225 if (!DuplicateHandle(INVALID_HANDLE_VALUE,
|
|
|
226 handle,
|
|
|
227 INVALID_HANDLE_VALUE,
|
|
|
228 &handle,
|
|
|
229 0,
|
|
|
230 FALSE,
|
|
|
231 DUPLICATE_SAME_ACCESS))
|
|
|
232 return uv_translate_sys_error(GetLastError());
|
|
|
233 fd = -1;
|
|
|
234 }
|
|
|
235
|
|
|
236 readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents);
|
|
|
237 if (!readable) {
|
|
|
238 /* Obtain the screen buffer info with the output handle. */
|
|
|
239 if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) {
|
|
|
240 return uv_translate_sys_error(GetLastError());
|
|
|
241 }
|
|
|
242
|
|
|
243 /* Obtain the cursor info with the output handle. */
|
|
|
244 if (!GetConsoleCursorInfo(handle, &cursor_info)) {
|
|
|
245 return uv_translate_sys_error(GetLastError());
|
|
|
246 }
|
|
|
247
|
|
|
248 /* Obtain the tty_output_lock because the virtual window state is shared
|
|
|
249 * between all uv_tty_t handles. */
|
|
|
250 uv_sem_wait(&uv_tty_output_lock);
|
|
|
251
|
|
|
252 if (uv__need_check_vterm_state)
|
|
|
253 uv__determine_vterm_state(handle);
|
|
|
254
|
|
|
255 /* Remember the original console text attributes and cursor info. */
|
|
|
256 uv__tty_capture_initial_style(&screen_buffer_info, &cursor_info);
|
|
|
257
|
|
|
258 uv__tty_update_virtual_window(&screen_buffer_info);
|
|
|
259
|
|
|
260 uv_sem_post(&uv_tty_output_lock);
|
|
|
261 }
|
|
|
262
|
|
|
263
|
|
|
264 uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY);
|
|
|
265 uv__connection_init((uv_stream_t*) tty);
|
|
|
266
|
|
|
267 tty->handle = handle;
|
|
|
268 tty->u.fd = fd;
|
|
|
269 tty->reqs_pending = 0;
|
|
|
270 tty->flags |= UV_HANDLE_BOUND;
|
|
|
271
|
|
|
272 if (readable) {
|
|
|
273 /* Initialize TTY input specific fields. */
|
|
|
274 tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
|
|
|
275 /* TODO: remove me in v2.x. */
|
|
|
276 tty->tty.rd.mode.unused_ = NULL;
|
|
|
277 /* Partially overwrites unused_ again. */
|
|
|
278 tty->tty.rd.mode.mode = 0;
|
|
|
279 tty->tty.rd.read_line_buffer = uv_null_buf_;
|
|
|
280 tty->tty.rd.read_raw_wait = NULL;
|
|
|
281
|
|
|
282 /* Init keycode-to-vt100 mapper state. */
|
|
|
283 tty->tty.rd.last_key_len = 0;
|
|
|
284 tty->tty.rd.last_key_offset = 0;
|
|
|
285 tty->tty.rd.last_utf16_high_surrogate = 0;
|
|
|
286 memset(&tty->tty.rd.last_input_record, 0, sizeof tty->tty.rd.last_input_record);
|
|
|
287 } else {
|
|
|
288 /* TTY output specific fields. */
|
|
|
289 tty->flags |= UV_HANDLE_WRITABLE;
|
|
|
290
|
|
|
291 /* Init utf8-to-utf16 conversion state. */
|
|
|
292 tty->tty.wr.utf8_bytes_left = 0;
|
|
|
293 tty->tty.wr.utf8_codepoint = 0;
|
|
|
294
|
|
|
295 /* Initialize eol conversion state */
|
|
|
296 tty->tty.wr.previous_eol = 0;
|
|
|
297
|
|
|
298 /* Init ANSI parser state. */
|
|
|
299 tty->tty.wr.ansi_parser_state = ANSI_NORMAL;
|
|
|
300 }
|
|
|
301
|
|
|
302 return 0;
|
|
|
303 }
|
|
|
304
|
|
|
305
|
|
|
306 /* Set the default console text attributes based on how the console was
|
|
|
307 * configured when libuv started.
|
|
|
308 */
|
|
|
309 static void uv__tty_capture_initial_style(
|
|
|
310 CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
|
|
|
311 CONSOLE_CURSOR_INFO* cursor_info) {
|
|
|
312 static int style_captured = 0;
|
|
|
313
|
|
|
314 /* Only do this once.
|
|
|
315 Assumption: Caller has acquired uv_tty_output_lock. */
|
|
|
316 if (style_captured)
|
|
|
317 return;
|
|
|
318
|
|
|
319 /* Save raw win32 attributes. */
|
|
|
320 uv_tty_default_text_attributes = screen_buffer_info->wAttributes;
|
|
|
321
|
|
|
322 /* Convert black text on black background to use white text. */
|
|
|
323 if (uv_tty_default_text_attributes == 0)
|
|
|
324 uv_tty_default_text_attributes = 7;
|
|
|
325
|
|
|
326 /* Convert Win32 attributes to ANSI colors. */
|
|
|
327 uv_tty_default_fg_color = 0;
|
|
|
328 uv_tty_default_bg_color = 0;
|
|
|
329 uv_tty_default_fg_bright = 0;
|
|
|
330 uv_tty_default_bg_bright = 0;
|
|
|
331 uv_tty_default_inverse = 0;
|
|
|
332
|
|
|
333 if (uv_tty_default_text_attributes & FOREGROUND_RED)
|
|
|
334 uv_tty_default_fg_color |= 1;
|
|
|
335
|
|
|
336 if (uv_tty_default_text_attributes & FOREGROUND_GREEN)
|
|
|
337 uv_tty_default_fg_color |= 2;
|
|
|
338
|
|
|
339 if (uv_tty_default_text_attributes & FOREGROUND_BLUE)
|
|
|
340 uv_tty_default_fg_color |= 4;
|
|
|
341
|
|
|
342 if (uv_tty_default_text_attributes & BACKGROUND_RED)
|
|
|
343 uv_tty_default_bg_color |= 1;
|
|
|
344
|
|
|
345 if (uv_tty_default_text_attributes & BACKGROUND_GREEN)
|
|
|
346 uv_tty_default_bg_color |= 2;
|
|
|
347
|
|
|
348 if (uv_tty_default_text_attributes & BACKGROUND_BLUE)
|
|
|
349 uv_tty_default_bg_color |= 4;
|
|
|
350
|
|
|
351 if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY)
|
|
|
352 uv_tty_default_fg_bright = 1;
|
|
|
353
|
|
|
354 if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY)
|
|
|
355 uv_tty_default_bg_bright = 1;
|
|
|
356
|
|
|
357 if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO)
|
|
|
358 uv_tty_default_inverse = 1;
|
|
|
359
|
|
|
360 /* Save the cursor size and the cursor state. */
|
|
|
361 uv_tty_default_cursor_info = *cursor_info;
|
|
|
362
|
|
|
363 style_captured = 1;
|
|
|
364 }
|
|
|
365
|
|
|
366
|
|
|
367 int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
|
|
368 DWORD flags;
|
|
|
369 DWORD try_set_flags;
|
|
|
370 unsigned char was_reading;
|
|
|
371 uv_alloc_cb alloc_cb;
|
|
|
372 uv_read_cb read_cb;
|
|
|
373 int err;
|
|
|
374
|
|
|
375 if (!(tty->flags & UV_HANDLE_TTY_READABLE)) {
|
|
|
376 return UV_EINVAL;
|
|
|
377 }
|
|
|
378
|
|
|
379 if ((int)mode == tty->tty.rd.mode.mode) {
|
|
|
380 return 0;
|
|
|
381 }
|
|
|
382
|
|
|
383 try_set_flags = 0;
|
|
|
384 switch (mode) {
|
|
|
385 case UV_TTY_MODE_NORMAL:
|
|
|
386 flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
|
|
387 break;
|
|
|
388 case UV_TTY_MODE_RAW_VT:
|
|
|
389 try_set_flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
|
|
|
390 InterlockedExchange(&uv__tty_console_in_need_mode_reset, 1);
|
|
|
391 /* fallthrough */
|
|
|
392 case UV_TTY_MODE_RAW:
|
|
|
393 flags = ENABLE_WINDOW_INPUT;
|
|
|
394 break;
|
|
|
395 case UV_TTY_MODE_IO:
|
|
|
396 return UV_ENOTSUP;
|
|
|
397 default:
|
|
|
398 return UV_EINVAL;
|
|
|
399 }
|
|
|
400
|
|
|
401 /* If currently reading, stop, and restart reading. */
|
|
|
402 if (tty->flags & UV_HANDLE_READING) {
|
|
|
403 was_reading = 1;
|
|
|
404 alloc_cb = tty->alloc_cb;
|
|
|
405 read_cb = tty->read_cb;
|
|
|
406 err = uv__tty_read_stop(tty);
|
|
|
407 if (err) {
|
|
|
408 return uv_translate_sys_error(err);
|
|
|
409 }
|
|
|
410 } else {
|
|
|
411 was_reading = 0;
|
|
|
412 alloc_cb = NULL;
|
|
|
413 read_cb = NULL;
|
|
|
414 }
|
|
|
415
|
|
|
416 uv_sem_wait(&uv_tty_output_lock);
|
|
|
417 if (!SetConsoleMode(tty->handle, flags | try_set_flags) &&
|
|
|
418 !SetConsoleMode(tty->handle, flags)) {
|
|
|
419 err = uv_translate_sys_error(GetLastError());
|
|
|
420 uv_sem_post(&uv_tty_output_lock);
|
|
|
421 return err;
|
|
|
422 }
|
|
|
423 uv_sem_post(&uv_tty_output_lock);
|
|
|
424
|
|
|
425 /* Update mode. */
|
|
|
426 tty->tty.rd.mode.mode = mode;
|
|
|
427
|
|
|
428 /* If we just stopped reading, restart. */
|
|
|
429 if (was_reading) {
|
|
|
430 err = uv__tty_read_start(tty, alloc_cb, read_cb);
|
|
|
431 if (err) {
|
|
|
432 return uv_translate_sys_error(err);
|
|
|
433 }
|
|
|
434 }
|
|
|
435
|
|
|
436 return 0;
|
|
|
437 }
|
|
|
438
|
|
|
439
|
|
|
440 int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
|
|
|
441 CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
442
|
|
|
443 if (!GetConsoleScreenBufferInfo(tty->handle, &info)) {
|
|
|
444 return uv_translate_sys_error(GetLastError());
|
|
|
445 }
|
|
|
446
|
|
|
447 uv_sem_wait(&uv_tty_output_lock);
|
|
|
448 uv__tty_update_virtual_window(&info);
|
|
|
449 uv_sem_post(&uv_tty_output_lock);
|
|
|
450
|
|
|
451 *width = uv_tty_virtual_width;
|
|
|
452 *height = uv_tty_virtual_height;
|
|
|
453
|
|
|
454 return 0;
|
|
|
455 }
|
|
|
456
|
|
|
457
|
|
|
458 static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) {
|
|
|
459 uv_loop_t* loop;
|
|
|
460 uv_tty_t* handle;
|
|
|
461 uv_req_t* req;
|
|
|
462
|
|
|
463 assert(data);
|
|
|
464 assert(!didTimeout);
|
|
|
465
|
|
|
466 req = (uv_req_t*) data;
|
|
|
467 handle = (uv_tty_t*) req->data;
|
|
|
468 loop = handle->loop;
|
|
|
469
|
|
|
470 UnregisterWait(handle->tty.rd.read_raw_wait);
|
|
|
471 handle->tty.rd.read_raw_wait = NULL;
|
|
|
472
|
|
|
473 SET_REQ_SUCCESS(req);
|
|
|
474 POST_COMPLETION_FOR_REQ(loop, req);
|
|
|
475 }
|
|
|
476
|
|
|
477
|
|
|
478 static void uv__tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
|
|
|
479 uv_read_t* req;
|
|
|
480 BOOL r;
|
|
|
481
|
|
|
482 assert(handle->flags & UV_HANDLE_READING);
|
|
|
483 assert(!(handle->flags & UV_HANDLE_READ_PENDING));
|
|
|
484
|
|
|
485 assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
|
|
|
486
|
|
|
487 handle->tty.rd.read_line_buffer = uv_null_buf_;
|
|
|
488
|
|
|
489 req = &handle->read_req;
|
|
|
490 memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
|
|
491
|
|
|
492 r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait,
|
|
|
493 handle->handle,
|
|
|
494 uv_tty_post_raw_read,
|
|
|
495 (void*) req,
|
|
|
496 INFINITE,
|
|
|
497 WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
|
|
|
498 if (!r) {
|
|
|
499 handle->tty.rd.read_raw_wait = NULL;
|
|
|
500 SET_REQ_ERROR(req, GetLastError());
|
|
|
501 uv__insert_pending_req(loop, (uv_req_t*)req);
|
|
|
502 }
|
|
|
503
|
|
|
504 handle->flags |= UV_HANDLE_READ_PENDING;
|
|
|
505 handle->reqs_pending++;
|
|
|
506 }
|
|
|
507
|
|
|
508
|
|
|
509 static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
|
|
|
510 uv_loop_t* loop;
|
|
|
511 uv_tty_t* handle;
|
|
|
512 uv_req_t* req;
|
|
|
513 DWORD bytes;
|
|
|
514 size_t read_bytes;
|
|
|
515 WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3];
|
|
|
516 DWORD chars;
|
|
|
517 DWORD read_chars;
|
|
|
518 LONG status;
|
|
|
519 COORD pos;
|
|
|
520 BOOL read_console_success;
|
|
|
521
|
|
|
522 assert(data);
|
|
|
523
|
|
|
524 req = (uv_req_t*) data;
|
|
|
525 handle = (uv_tty_t*) req->data;
|
|
|
526 loop = handle->loop;
|
|
|
527
|
|
|
528 assert(handle->tty.rd.read_line_buffer.base != NULL);
|
|
|
529 assert(handle->tty.rd.read_line_buffer.len > 0);
|
|
|
530
|
|
|
531 /* ReadConsole can't handle big buffers. */
|
|
|
532 if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) {
|
|
|
533 bytes = handle->tty.rd.read_line_buffer.len;
|
|
|
534 } else {
|
|
|
535 bytes = MAX_INPUT_BUFFER_LENGTH;
|
|
|
536 }
|
|
|
537
|
|
|
538 /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8
|
|
|
539 * codeunits to encode. */
|
|
|
540 chars = bytes / 3;
|
|
|
541
|
|
|
542 status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS);
|
|
|
543 if (status == TRAP_REQUESTED) {
|
|
|
544 SET_REQ_SUCCESS(req);
|
|
|
545 InterlockedExchange(&uv__read_console_status, COMPLETED);
|
|
|
546 req->u.io.overlapped.InternalHigh = 0;
|
|
|
547 POST_COMPLETION_FOR_REQ(loop, req);
|
|
|
548 return 0;
|
|
|
549 }
|
|
|
550
|
|
|
551 read_console_success = ReadConsoleW(handle->handle,
|
|
|
552 (void*) utf16,
|
|
|
553 chars,
|
|
|
554 &read_chars,
|
|
|
555 NULL);
|
|
|
556
|
|
|
557 if (read_console_success) {
|
|
|
558 read_bytes = bytes;
|
|
|
559 uv_utf16_to_wtf8(utf16,
|
|
|
560 read_chars,
|
|
|
561 &handle->tty.rd.read_line_buffer.base,
|
|
|
562 &read_bytes);
|
|
|
563 SET_REQ_SUCCESS(req);
|
|
|
564 req->u.io.overlapped.InternalHigh = (DWORD) read_bytes;
|
|
|
565 } else {
|
|
|
566 SET_REQ_ERROR(req, GetLastError());
|
|
|
567 }
|
|
|
568
|
|
|
569 status = InterlockedExchange(&uv__read_console_status, COMPLETED);
|
|
|
570
|
|
|
571 if (status == TRAP_REQUESTED) {
|
|
|
572 /* If we canceled the read by sending a VK_RETURN event, restore the
|
|
|
573 screen state to undo the visual effect of the VK_RETURN */
|
|
|
574 if (read_console_success && InterlockedOr(&uv__restore_screen_state, 0)) {
|
|
|
575 HANDLE active_screen_buffer;
|
|
|
576 active_screen_buffer = CreateFileA("conout$",
|
|
|
577 GENERIC_READ | GENERIC_WRITE,
|
|
|
578 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
579 NULL,
|
|
|
580 OPEN_EXISTING,
|
|
|
581 FILE_ATTRIBUTE_NORMAL,
|
|
|
582 NULL);
|
|
|
583 if (active_screen_buffer != INVALID_HANDLE_VALUE) {
|
|
|
584 pos = uv__saved_screen_state.dwCursorPosition;
|
|
|
585
|
|
|
586 /* If the cursor was at the bottom line of the screen buffer, the
|
|
|
587 VK_RETURN would have caused the buffer contents to scroll up by one
|
|
|
588 line. The right position to reset the cursor to is therefore one line
|
|
|
589 higher */
|
|
|
590 if (pos.Y == uv__saved_screen_state.dwSize.Y - 1)
|
|
|
591 pos.Y--;
|
|
|
592
|
|
|
593 SetConsoleCursorPosition(active_screen_buffer, pos);
|
|
|
594 CloseHandle(active_screen_buffer);
|
|
|
595 }
|
|
|
596 }
|
|
|
597 uv_sem_post(&uv_tty_output_lock);
|
|
|
598 }
|
|
|
599 POST_COMPLETION_FOR_REQ(loop, req);
|
|
|
600 return 0;
|
|
|
601 }
|
|
|
602
|
|
|
603
|
|
|
604 static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
|
|
|
605 uv_read_t* req;
|
|
|
606 BOOL r;
|
|
|
607
|
|
|
608 assert(handle->flags & UV_HANDLE_READING);
|
|
|
609 assert(!(handle->flags & UV_HANDLE_READ_PENDING));
|
|
|
610 assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
|
|
|
611
|
|
|
612 req = &handle->read_req;
|
|
|
613 memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
|
|
614
|
|
|
615 handle->tty.rd.read_line_buffer = uv_buf_init(NULL, 0);
|
|
|
616 handle->alloc_cb((uv_handle_t*) handle, 8192, &handle->tty.rd.read_line_buffer);
|
|
|
617 if (handle->tty.rd.read_line_buffer.base == NULL ||
|
|
|
618 handle->tty.rd.read_line_buffer.len == 0) {
|
|
|
619 handle->read_cb((uv_stream_t*) handle,
|
|
|
620 UV_ENOBUFS,
|
|
|
621 &handle->tty.rd.read_line_buffer);
|
|
|
622 return;
|
|
|
623 }
|
|
|
624 assert(handle->tty.rd.read_line_buffer.base != NULL);
|
|
|
625
|
|
|
626 /* Reset flags No locking is required since there cannot be a line read
|
|
|
627 in progress. We are also relying on the memory barrier provided by
|
|
|
628 QueueUserWorkItem*/
|
|
|
629 uv__restore_screen_state = FALSE;
|
|
|
630 uv__read_console_status = NOT_STARTED;
|
|
|
631 r = QueueUserWorkItem(uv_tty_line_read_thread,
|
|
|
632 (void*) req,
|
|
|
633 WT_EXECUTELONGFUNCTION);
|
|
|
634 if (!r) {
|
|
|
635 SET_REQ_ERROR(req, GetLastError());
|
|
|
636 uv__insert_pending_req(loop, (uv_req_t*)req);
|
|
|
637 }
|
|
|
638
|
|
|
639 handle->flags |= UV_HANDLE_READ_PENDING;
|
|
|
640 handle->reqs_pending++;
|
|
|
641 }
|
|
|
642
|
|
|
643
|
|
|
644 static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
|
|
|
645 if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
|
|
|
646 uv__tty_queue_read_raw(loop, handle);
|
|
|
647 } else {
|
|
|
648 uv__tty_queue_read_line(loop, handle);
|
|
|
649 }
|
|
|
650 }
|
|
|
651
|
|
|
652
|
|
|
653 static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl,
|
|
|
654 size_t* len) {
|
|
|
655 #define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \
|
|
|
656 case (vk): \
|
|
|
657 if (shift && ctrl) { \
|
|
|
658 *len = sizeof shift_ctrl_str; \
|
|
|
659 return "\033" shift_ctrl_str; \
|
|
|
660 } else if (shift) { \
|
|
|
661 *len = sizeof shift_str ; \
|
|
|
662 return "\033" shift_str; \
|
|
|
663 } else if (ctrl) { \
|
|
|
664 *len = sizeof ctrl_str; \
|
|
|
665 return "\033" ctrl_str; \
|
|
|
666 } else { \
|
|
|
667 *len = sizeof normal_str; \
|
|
|
668 return "\033" normal_str; \
|
|
|
669 }
|
|
|
670
|
|
|
671 switch (code) {
|
|
|
672 /* These mappings are the same as Cygwin's. Unmodified and alt-modified
|
|
|
673 * keypad keys comply with linux console, modifiers comply with xterm
|
|
|
674 * modifier usage. F1. f12 and shift-f1. f10 comply with linux console, f6.
|
|
|
675 * f12 with and without modifiers comply with rxvt. */
|
|
|
676 VK_CASE(VK_INSERT, "[2~", "[2;2~", "[2;5~", "[2;6~")
|
|
|
677 VK_CASE(VK_END, "[4~", "[4;2~", "[4;5~", "[4;6~")
|
|
|
678 VK_CASE(VK_DOWN, "[B", "[1;2B", "[1;5B", "[1;6B")
|
|
|
679 VK_CASE(VK_NEXT, "[6~", "[6;2~", "[6;5~", "[6;6~")
|
|
|
680 VK_CASE(VK_LEFT, "[D", "[1;2D", "[1;5D", "[1;6D")
|
|
|
681 VK_CASE(VK_CLEAR, "[G", "[1;2G", "[1;5G", "[1;6G")
|
|
|
682 VK_CASE(VK_RIGHT, "[C", "[1;2C", "[1;5C", "[1;6C")
|
|
|
683 VK_CASE(VK_UP, "[A", "[1;2A", "[1;5A", "[1;6A")
|
|
|
684 VK_CASE(VK_HOME, "[1~", "[1;2~", "[1;5~", "[1;6~")
|
|
|
685 VK_CASE(VK_PRIOR, "[5~", "[5;2~", "[5;5~", "[5;6~")
|
|
|
686 VK_CASE(VK_DELETE, "[3~", "[3;2~", "[3;5~", "[3;6~")
|
|
|
687 VK_CASE(VK_NUMPAD0, "[2~", "[2;2~", "[2;5~", "[2;6~")
|
|
|
688 VK_CASE(VK_NUMPAD1, "[4~", "[4;2~", "[4;5~", "[4;6~")
|
|
|
689 VK_CASE(VK_NUMPAD2, "[B", "[1;2B", "[1;5B", "[1;6B")
|
|
|
690 VK_CASE(VK_NUMPAD3, "[6~", "[6;2~", "[6;5~", "[6;6~")
|
|
|
691 VK_CASE(VK_NUMPAD4, "[D", "[1;2D", "[1;5D", "[1;6D")
|
|
|
692 VK_CASE(VK_NUMPAD5, "[G", "[1;2G", "[1;5G", "[1;6G")
|
|
|
693 VK_CASE(VK_NUMPAD6, "[C", "[1;2C", "[1;5C", "[1;6C")
|
|
|
694 VK_CASE(VK_NUMPAD7, "[A", "[1;2A", "[1;5A", "[1;6A")
|
|
|
695 VK_CASE(VK_NUMPAD8, "[1~", "[1;2~", "[1;5~", "[1;6~")
|
|
|
696 VK_CASE(VK_NUMPAD9, "[5~", "[5;2~", "[5;5~", "[5;6~")
|
|
|
697 VK_CASE(VK_DECIMAL, "[3~", "[3;2~", "[3;5~", "[3;6~")
|
|
|
698 VK_CASE(VK_F1, "[[A", "[23~", "[11^", "[23^" )
|
|
|
699 VK_CASE(VK_F2, "[[B", "[24~", "[12^", "[24^" )
|
|
|
700 VK_CASE(VK_F3, "[[C", "[25~", "[13^", "[25^" )
|
|
|
701 VK_CASE(VK_F4, "[[D", "[26~", "[14^", "[26^" )
|
|
|
702 VK_CASE(VK_F5, "[[E", "[28~", "[15^", "[28^" )
|
|
|
703 VK_CASE(VK_F6, "[17~", "[29~", "[17^", "[29^" )
|
|
|
704 VK_CASE(VK_F7, "[18~", "[31~", "[18^", "[31^" )
|
|
|
705 VK_CASE(VK_F8, "[19~", "[32~", "[19^", "[32^" )
|
|
|
706 VK_CASE(VK_F9, "[20~", "[33~", "[20^", "[33^" )
|
|
|
707 VK_CASE(VK_F10, "[21~", "[34~", "[21^", "[34^" )
|
|
|
708 VK_CASE(VK_F11, "[23~", "[23$", "[23^", "[23@" )
|
|
|
709 VK_CASE(VK_F12, "[24~", "[24$", "[24^", "[24@" )
|
|
|
710
|
|
|
711 default:
|
|
|
712 *len = 0;
|
|
|
713 return NULL;
|
|
|
714 }
|
|
|
715 #undef VK_CASE
|
|
|
716 }
|
|
|
717
|
|
|
718
|
|
|
719 void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
|
|
|
720 uv_req_t* req) {
|
|
|
721 /* Shortcut for handle->tty.rd.last_input_record.Event.KeyEvent. */
|
|
|
722 #define KEV handle->tty.rd.last_input_record.Event.KeyEvent
|
|
|
723
|
|
|
724 DWORD records_left, records_read;
|
|
|
725 uv_buf_t buf;
|
|
|
726 _off_t buf_used;
|
|
|
727
|
|
|
728 assert(handle->type == UV_TTY);
|
|
|
729 assert(handle->flags & UV_HANDLE_TTY_READABLE);
|
|
|
730 handle->flags &= ~UV_HANDLE_READ_PENDING;
|
|
|
731
|
|
|
732 if (!(handle->flags & UV_HANDLE_READING) ||
|
|
|
733 !(uv__is_raw_tty_mode(handle->tty.rd.mode.mode))) {
|
|
|
734 goto out;
|
|
|
735 }
|
|
|
736
|
|
|
737 if (!REQ_SUCCESS(req)) {
|
|
|
738 /* An error occurred while waiting for the event. */
|
|
|
739 if ((handle->flags & UV_HANDLE_READING)) {
|
|
|
740 handle->flags &= ~UV_HANDLE_READING;
|
|
|
741 handle->read_cb((uv_stream_t*)handle,
|
|
|
742 uv_translate_sys_error(GET_REQ_ERROR(req)),
|
|
|
743 &uv_null_buf_);
|
|
|
744 }
|
|
|
745 goto out;
|
|
|
746 }
|
|
|
747
|
|
|
748 /* Fetch the number of events */
|
|
|
749 if (!GetNumberOfConsoleInputEvents(handle->handle, &records_left)) {
|
|
|
750 handle->flags &= ~UV_HANDLE_READING;
|
|
|
751 DECREASE_ACTIVE_COUNT(loop, handle);
|
|
|
752 handle->read_cb((uv_stream_t*)handle,
|
|
|
753 uv_translate_sys_error(GetLastError()),
|
|
|
754 &uv_null_buf_);
|
|
|
755 goto out;
|
|
|
756 }
|
|
|
757
|
|
|
758 /* Windows sends a lot of events that we're not interested in, so buf will be
|
|
|
759 * allocated on demand, when there's actually something to emit. */
|
|
|
760 buf = uv_null_buf_;
|
|
|
761 buf_used = 0;
|
|
|
762
|
|
|
763 while ((records_left > 0 || handle->tty.rd.last_key_len > 0) &&
|
|
|
764 (handle->flags & UV_HANDLE_READING)) {
|
|
|
765 if (handle->tty.rd.last_key_len == 0) {
|
|
|
766 /* Read the next input record */
|
|
|
767 if (!ReadConsoleInputW(handle->handle,
|
|
|
768 &handle->tty.rd.last_input_record,
|
|
|
769 1,
|
|
|
770 &records_read)) {
|
|
|
771 handle->flags &= ~UV_HANDLE_READING;
|
|
|
772 DECREASE_ACTIVE_COUNT(loop, handle);
|
|
|
773 handle->read_cb((uv_stream_t*) handle,
|
|
|
774 uv_translate_sys_error(GetLastError()),
|
|
|
775 &buf);
|
|
|
776 goto out;
|
|
|
777 }
|
|
|
778 records_left--;
|
|
|
779
|
|
|
780 /* We might be not subscribed to EVENT_CONSOLE_LAYOUT or we might be
|
|
|
781 * running under some TTY emulator that does not send those events. */
|
|
|
782 if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) {
|
|
|
783 uv__tty_console_signal_resize();
|
|
|
784 }
|
|
|
785
|
|
|
786 /* Ignore other events that are not key events. */
|
|
|
787 if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) {
|
|
|
788 continue;
|
|
|
789 }
|
|
|
790
|
|
|
791 /* Ignore keyup events, unless the left alt key was held and a valid
|
|
|
792 * unicode character was emitted. */
|
|
|
793 if (!KEV.bKeyDown &&
|
|
|
794 (KEV.wVirtualKeyCode != VK_MENU ||
|
|
|
795 KEV.uChar.UnicodeChar == 0)) {
|
|
|
796 continue;
|
|
|
797 }
|
|
|
798
|
|
|
799 /* Ignore keypresses to numpad number keys if the left alt is held
|
|
|
800 * because the user is composing a character, or windows simulating this.
|
|
|
801 */
|
|
|
802 if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) &&
|
|
|
803 !(KEV.dwControlKeyState & ENHANCED_KEY) &&
|
|
|
804 (KEV.wVirtualKeyCode == VK_INSERT ||
|
|
|
805 KEV.wVirtualKeyCode == VK_END ||
|
|
|
806 KEV.wVirtualKeyCode == VK_DOWN ||
|
|
|
807 KEV.wVirtualKeyCode == VK_NEXT ||
|
|
|
808 KEV.wVirtualKeyCode == VK_LEFT ||
|
|
|
809 KEV.wVirtualKeyCode == VK_CLEAR ||
|
|
|
810 KEV.wVirtualKeyCode == VK_RIGHT ||
|
|
|
811 KEV.wVirtualKeyCode == VK_HOME ||
|
|
|
812 KEV.wVirtualKeyCode == VK_UP ||
|
|
|
813 KEV.wVirtualKeyCode == VK_PRIOR ||
|
|
|
814 KEV.wVirtualKeyCode == VK_NUMPAD0 ||
|
|
|
815 KEV.wVirtualKeyCode == VK_NUMPAD1 ||
|
|
|
816 KEV.wVirtualKeyCode == VK_NUMPAD2 ||
|
|
|
817 KEV.wVirtualKeyCode == VK_NUMPAD3 ||
|
|
|
818 KEV.wVirtualKeyCode == VK_NUMPAD4 ||
|
|
|
819 KEV.wVirtualKeyCode == VK_NUMPAD5 ||
|
|
|
820 KEV.wVirtualKeyCode == VK_NUMPAD6 ||
|
|
|
821 KEV.wVirtualKeyCode == VK_NUMPAD7 ||
|
|
|
822 KEV.wVirtualKeyCode == VK_NUMPAD8 ||
|
|
|
823 KEV.wVirtualKeyCode == VK_NUMPAD9)) {
|
|
|
824 continue;
|
|
|
825 }
|
|
|
826
|
|
|
827 if (KEV.uChar.UnicodeChar != 0) {
|
|
|
828 int prefix_len;
|
|
|
829 size_t char_len;
|
|
|
830 char* last_key_buf;
|
|
|
831
|
|
|
832 /* Character key pressed */
|
|
|
833 if (KEV.uChar.UnicodeChar >= 0xD800 &&
|
|
|
834 KEV.uChar.UnicodeChar < 0xDC00) {
|
|
|
835 /* UTF-16 high surrogate */
|
|
|
836 handle->tty.rd.last_utf16_high_surrogate = KEV.uChar.UnicodeChar;
|
|
|
837 continue;
|
|
|
838 }
|
|
|
839
|
|
|
840 /* Prefix with \u033 if alt was held, but alt was not used as part a
|
|
|
841 * compose sequence. */
|
|
|
842 if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
|
|
|
843 && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED |
|
|
|
844 RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) {
|
|
|
845 handle->tty.rd.last_key[0] = '\033';
|
|
|
846 prefix_len = 1;
|
|
|
847 } else {
|
|
|
848 prefix_len = 0;
|
|
|
849 }
|
|
|
850
|
|
|
851 char_len = sizeof handle->tty.rd.last_key;
|
|
|
852 last_key_buf = &handle->tty.rd.last_key[prefix_len];
|
|
|
853 if (handle->tty.rd.last_utf16_high_surrogate) {
|
|
|
854 /* UTF-16 surrogate pair */
|
|
|
855 WCHAR utf16_buffer[2];
|
|
|
856 utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate;
|
|
|
857 utf16_buffer[1] = KEV.uChar.UnicodeChar;
|
|
|
858 if (uv_utf16_to_wtf8(utf16_buffer,
|
|
|
859 2,
|
|
|
860 &last_key_buf,
|
|
|
861 &char_len))
|
|
|
862 char_len = 0;
|
|
|
863 handle->tty.rd.last_utf16_high_surrogate = 0;
|
|
|
864 } else {
|
|
|
865 /* Single UTF-16 character */
|
|
|
866 if (uv_utf16_to_wtf8(&KEV.uChar.UnicodeChar,
|
|
|
867 1,
|
|
|
868 &last_key_buf,
|
|
|
869 &char_len))
|
|
|
870 char_len = 0;
|
|
|
871 }
|
|
|
872
|
|
|
873 /* If the utf16 character(s) couldn't be converted something must be
|
|
|
874 * wrong. */
|
|
|
875 if (char_len == 0) {
|
|
|
876 handle->flags &= ~UV_HANDLE_READING;
|
|
|
877 DECREASE_ACTIVE_COUNT(loop, handle);
|
|
|
878 handle->read_cb((uv_stream_t*) handle,
|
|
|
879 uv_translate_sys_error(GetLastError()),
|
|
|
880 &buf);
|
|
|
881 goto out;
|
|
|
882 }
|
|
|
883
|
|
|
884 handle->tty.rd.last_key_len = (unsigned char) (prefix_len + char_len);
|
|
|
885 handle->tty.rd.last_key_offset = 0;
|
|
|
886 continue;
|
|
|
887
|
|
|
888 } else {
|
|
|
889 /* Function key pressed */
|
|
|
890 const char* vt100;
|
|
|
891 size_t prefix_len, vt100_len;
|
|
|
892
|
|
|
893 vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode,
|
|
|
894 !!(KEV.dwControlKeyState & SHIFT_PRESSED),
|
|
|
895 !!(KEV.dwControlKeyState & (
|
|
|
896 LEFT_CTRL_PRESSED |
|
|
|
897 RIGHT_CTRL_PRESSED)),
|
|
|
898 &vt100_len);
|
|
|
899
|
|
|
900 /* If we were unable to map to a vt100 sequence, just ignore. */
|
|
|
901 if (!vt100) {
|
|
|
902 continue;
|
|
|
903 }
|
|
|
904
|
|
|
905 /* Prefix with \x033 when the alt key was held. */
|
|
|
906 if (KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) {
|
|
|
907 handle->tty.rd.last_key[0] = '\033';
|
|
|
908 prefix_len = 1;
|
|
|
909 } else {
|
|
|
910 prefix_len = 0;
|
|
|
911 }
|
|
|
912
|
|
|
913 /* Copy the vt100 sequence to the handle buffer. */
|
|
|
914 assert(prefix_len + vt100_len < sizeof handle->tty.rd.last_key);
|
|
|
915 memcpy(&handle->tty.rd.last_key[prefix_len], vt100, vt100_len);
|
|
|
916
|
|
|
917 handle->tty.rd.last_key_len = (unsigned char) (prefix_len + vt100_len);
|
|
|
918 handle->tty.rd.last_key_offset = 0;
|
|
|
919 continue;
|
|
|
920 }
|
|
|
921 } else {
|
|
|
922 /* Copy any bytes left from the last keypress to the user buffer. */
|
|
|
923 if (handle->tty.rd.last_key_offset < handle->tty.rd.last_key_len) {
|
|
|
924 /* Allocate a buffer if needed */
|
|
|
925 if (buf_used == 0) {
|
|
|
926 buf = uv_buf_init(NULL, 0);
|
|
|
927 handle->alloc_cb((uv_handle_t*) handle, 1024, &buf);
|
|
|
928 if (buf.base == NULL || buf.len == 0) {
|
|
|
929 handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
|
|
|
930 goto out;
|
|
|
931 }
|
|
|
932 assert(buf.base != NULL);
|
|
|
933 }
|
|
|
934
|
|
|
935 buf.base[buf_used++] = handle->tty.rd.last_key[handle->tty.rd.last_key_offset++];
|
|
|
936
|
|
|
937 /* If the buffer is full, emit it */
|
|
|
938 if ((size_t) buf_used == buf.len) {
|
|
|
939 handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
|
|
|
940 buf = uv_null_buf_;
|
|
|
941 buf_used = 0;
|
|
|
942 }
|
|
|
943
|
|
|
944 continue;
|
|
|
945 }
|
|
|
946
|
|
|
947 /* Apply dwRepeat from the last input record. */
|
|
|
948 if (--KEV.wRepeatCount > 0) {
|
|
|
949 handle->tty.rd.last_key_offset = 0;
|
|
|
950 continue;
|
|
|
951 }
|
|
|
952
|
|
|
953 handle->tty.rd.last_key_len = 0;
|
|
|
954 continue;
|
|
|
955 }
|
|
|
956 }
|
|
|
957
|
|
|
958 /* Send the buffer back to the user */
|
|
|
959 if (buf_used > 0) {
|
|
|
960 handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
|
|
|
961 }
|
|
|
962
|
|
|
963 out:
|
|
|
964 /* Wait for more input events. */
|
|
|
965 if ((handle->flags & UV_HANDLE_READING) &&
|
|
|
966 !(handle->flags & UV_HANDLE_READ_PENDING)) {
|
|
|
967 uv__tty_queue_read(loop, handle);
|
|
|
968 }
|
|
|
969
|
|
|
970 DECREASE_PENDING_REQ_COUNT(handle);
|
|
|
971
|
|
|
972 #undef KEV
|
|
|
973 }
|
|
|
974
|
|
|
975
|
|
|
976
|
|
|
977 void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
|
|
|
978 uv_req_t* req) {
|
|
|
979 uv_buf_t buf;
|
|
|
980
|
|
|
981 assert(handle->type == UV_TTY);
|
|
|
982 assert(handle->flags & UV_HANDLE_TTY_READABLE);
|
|
|
983
|
|
|
984 buf = handle->tty.rd.read_line_buffer;
|
|
|
985
|
|
|
986 handle->flags &= ~UV_HANDLE_READ_PENDING;
|
|
|
987 handle->tty.rd.read_line_buffer = uv_null_buf_;
|
|
|
988
|
|
|
989 if (!REQ_SUCCESS(req)) {
|
|
|
990 /* Read was not successful */
|
|
|
991 if (handle->flags & UV_HANDLE_READING) {
|
|
|
992 /* Real error */
|
|
|
993 handle->flags &= ~UV_HANDLE_READING;
|
|
|
994 DECREASE_ACTIVE_COUNT(loop, handle);
|
|
|
995 handle->read_cb((uv_stream_t*) handle,
|
|
|
996 uv_translate_sys_error(GET_REQ_ERROR(req)),
|
|
|
997 &buf);
|
|
|
998 }
|
|
|
999 } else {
|
|
|
1000 if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING) &&
|
|
|
1001 req->u.io.overlapped.InternalHigh != 0) {
|
|
|
1002 /* Read successful. TODO: read unicode, convert to utf-8 */
|
|
|
1003 DWORD bytes = req->u.io.overlapped.InternalHigh;
|
|
|
1004 handle->read_cb((uv_stream_t*) handle, bytes, &buf);
|
|
|
1005 }
|
|
|
1006 handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
|
|
|
1007 }
|
|
|
1008
|
|
|
1009 /* Wait for more input events. */
|
|
|
1010 if ((handle->flags & UV_HANDLE_READING) &&
|
|
|
1011 !(handle->flags & UV_HANDLE_READ_PENDING)) {
|
|
|
1012 uv__tty_queue_read(loop, handle);
|
|
|
1013 }
|
|
|
1014
|
|
|
1015 DECREASE_PENDING_REQ_COUNT(handle);
|
|
|
1016 }
|
|
|
1017
|
|
|
1018
|
|
|
1019 void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
|
|
|
1020 uv_req_t* req) {
|
|
|
1021 assert(handle->type == UV_TTY);
|
|
|
1022 assert(handle->flags & UV_HANDLE_TTY_READABLE);
|
|
|
1023
|
|
|
1024 /* If the read_line_buffer member is zero, it must have been an raw read.
|
|
|
1025 * Otherwise it was a line-buffered read. FIXME: This is quite obscure. Use a
|
|
|
1026 * flag or something. */
|
|
|
1027 if (handle->tty.rd.read_line_buffer.len == 0) {
|
|
|
1028 uv_process_tty_read_raw_req(loop, handle, req);
|
|
|
1029 } else {
|
|
|
1030 uv_process_tty_read_line_req(loop, handle, req);
|
|
|
1031 }
|
|
|
1032 }
|
|
|
1033
|
|
|
1034
|
|
|
1035 int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
|
|
|
1036 uv_read_cb read_cb) {
|
|
|
1037 uv_loop_t* loop = handle->loop;
|
|
|
1038
|
|
|
1039 if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
|
|
|
1040 return ERROR_INVALID_PARAMETER;
|
|
|
1041 }
|
|
|
1042
|
|
|
1043 handle->flags |= UV_HANDLE_READING;
|
|
|
1044 INCREASE_ACTIVE_COUNT(loop, handle);
|
|
|
1045 handle->read_cb = read_cb;
|
|
|
1046 handle->alloc_cb = alloc_cb;
|
|
|
1047
|
|
|
1048 /* If reading was stopped and then started again, there could still be a read
|
|
|
1049 * request pending. */
|
|
|
1050 if (handle->flags & UV_HANDLE_READ_PENDING) {
|
|
|
1051 return 0;
|
|
|
1052 }
|
|
|
1053
|
|
|
1054 /* Maybe the user stopped reading half-way while processing key events.
|
|
|
1055 * Short-circuit if this could be the case. */
|
|
|
1056 if (handle->tty.rd.last_key_len > 0) {
|
|
|
1057 SET_REQ_SUCCESS(&handle->read_req);
|
|
|
1058 uv__insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req);
|
|
|
1059 /* Make sure no attempt is made to insert it again until it's handled. */
|
|
|
1060 handle->flags |= UV_HANDLE_READ_PENDING;
|
|
|
1061 handle->reqs_pending++;
|
|
|
1062 return 0;
|
|
|
1063 }
|
|
|
1064
|
|
|
1065 uv__tty_queue_read(loop, handle);
|
|
|
1066
|
|
|
1067 return 0;
|
|
|
1068 }
|
|
|
1069
|
|
|
1070
|
|
|
1071 int uv__tty_read_stop(uv_tty_t* handle) {
|
|
|
1072 INPUT_RECORD record;
|
|
|
1073 DWORD written, err;
|
|
|
1074
|
|
|
1075 handle->flags &= ~UV_HANDLE_READING;
|
|
|
1076 DECREASE_ACTIVE_COUNT(handle->loop, handle);
|
|
|
1077
|
|
|
1078 if (!(handle->flags & UV_HANDLE_READ_PENDING))
|
|
|
1079 return 0;
|
|
|
1080
|
|
|
1081 if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
|
|
|
1082 /* Cancel raw read. Write some bullshit event to force the console wait to
|
|
|
1083 * return. */
|
|
|
1084 memset(&record, 0, sizeof record);
|
|
|
1085 record.EventType = FOCUS_EVENT;
|
|
|
1086 if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
|
|
|
1087 return GetLastError();
|
|
|
1088 }
|
|
|
1089 } else if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) {
|
|
|
1090 /* Cancel line-buffered read if not already pending */
|
|
|
1091 err = uv__cancel_read_console(handle);
|
|
|
1092 if (err)
|
|
|
1093 return err;
|
|
|
1094
|
|
|
1095 handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
|
|
|
1096 }
|
|
|
1097
|
|
|
1098 return 0;
|
|
|
1099 }
|
|
|
1100
|
|
|
1101 static int uv__cancel_read_console(uv_tty_t* handle) {
|
|
|
1102 HANDLE active_screen_buffer = INVALID_HANDLE_VALUE;
|
|
|
1103 INPUT_RECORD record;
|
|
|
1104 DWORD written;
|
|
|
1105 DWORD err = 0;
|
|
|
1106 LONG status;
|
|
|
1107
|
|
|
1108 assert(!(handle->flags & UV_HANDLE_CANCELLATION_PENDING));
|
|
|
1109
|
|
|
1110 /* Hold the output lock during the cancellation, to ensure that further
|
|
|
1111 writes don't interfere with the screen state. It will be the ReadConsole
|
|
|
1112 thread's responsibility to release the lock. */
|
|
|
1113 uv_sem_wait(&uv_tty_output_lock);
|
|
|
1114 status = InterlockedExchange(&uv__read_console_status, TRAP_REQUESTED);
|
|
|
1115 if (status != IN_PROGRESS) {
|
|
|
1116 /* Either we have managed to set a trap for the other thread before
|
|
|
1117 ReadConsole is called, or ReadConsole has returned because the user
|
|
|
1118 has pressed ENTER. In either case, there is nothing else to do. */
|
|
|
1119 uv_sem_post(&uv_tty_output_lock);
|
|
|
1120 return 0;
|
|
|
1121 }
|
|
|
1122
|
|
|
1123 /* Save screen state before sending the VK_RETURN event */
|
|
|
1124 active_screen_buffer = CreateFileA("conout$",
|
|
|
1125 GENERIC_READ | GENERIC_WRITE,
|
|
|
1126 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
1127 NULL,
|
|
|
1128 OPEN_EXISTING,
|
|
|
1129 FILE_ATTRIBUTE_NORMAL,
|
|
|
1130 NULL);
|
|
|
1131
|
|
|
1132 if (active_screen_buffer != INVALID_HANDLE_VALUE &&
|
|
|
1133 GetConsoleScreenBufferInfo(active_screen_buffer,
|
|
|
1134 &uv__saved_screen_state)) {
|
|
|
1135 InterlockedOr(&uv__restore_screen_state, 1);
|
|
|
1136 }
|
|
|
1137
|
|
|
1138 /* Write enter key event to force the console wait to return. */
|
|
|
1139 record.EventType = KEY_EVENT;
|
|
|
1140 record.Event.KeyEvent.bKeyDown = TRUE;
|
|
|
1141 record.Event.KeyEvent.wRepeatCount = 1;
|
|
|
1142 record.Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
|
|
1143 record.Event.KeyEvent.wVirtualScanCode =
|
|
|
1144 MapVirtualKeyW(VK_RETURN, MAPVK_VK_TO_VSC);
|
|
|
1145 record.Event.KeyEvent.uChar.UnicodeChar = L'\r';
|
|
|
1146 record.Event.KeyEvent.dwControlKeyState = 0;
|
|
|
1147 if (!WriteConsoleInputW(handle->handle, &record, 1, &written))
|
|
|
1148 err = GetLastError();
|
|
|
1149
|
|
|
1150 if (active_screen_buffer != INVALID_HANDLE_VALUE)
|
|
|
1151 CloseHandle(active_screen_buffer);
|
|
|
1152
|
|
|
1153 return err;
|
|
|
1154 }
|
|
|
1155
|
|
|
1156
|
|
|
1157 static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) {
|
|
|
1158 uv_tty_virtual_width = info->dwSize.X;
|
|
|
1159 uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1;
|
|
|
1160
|
|
|
1161 /* Recompute virtual window offset row. */
|
|
|
1162 if (uv_tty_virtual_offset == -1) {
|
|
|
1163 uv_tty_virtual_offset = info->dwCursorPosition.Y;
|
|
|
1164 } else if (uv_tty_virtual_offset < info->dwCursorPosition.Y -
|
|
|
1165 uv_tty_virtual_height + 1) {
|
|
|
1166 /* If suddenly find the cursor outside of the virtual window, it must have
|
|
|
1167 * somehow scrolled. Update the virtual window offset. */
|
|
|
1168 uv_tty_virtual_offset = info->dwCursorPosition.Y -
|
|
|
1169 uv_tty_virtual_height + 1;
|
|
|
1170 }
|
|
|
1171 if (uv_tty_virtual_offset + uv_tty_virtual_height > info->dwSize.Y) {
|
|
|
1172 uv_tty_virtual_offset = info->dwSize.Y - uv_tty_virtual_height;
|
|
|
1173 }
|
|
|
1174 if (uv_tty_virtual_offset < 0) {
|
|
|
1175 uv_tty_virtual_offset = 0;
|
|
|
1176 }
|
|
|
1177 }
|
|
|
1178
|
|
|
1179
|
|
|
1180 static COORD uv__tty_make_real_coord(uv_tty_t* handle,
|
|
|
1181 CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y,
|
|
|
1182 unsigned char y_relative) {
|
|
|
1183 COORD result;
|
|
|
1184
|
|
|
1185 uv__tty_update_virtual_window(info);
|
|
|
1186
|
|
|
1187 /* Adjust y position */
|
|
|
1188 if (y_relative) {
|
|
|
1189 y = info->dwCursorPosition.Y + y;
|
|
|
1190 } else {
|
|
|
1191 y = uv_tty_virtual_offset + y;
|
|
|
1192 }
|
|
|
1193 /* Clip y to virtual client rectangle */
|
|
|
1194 if (y < uv_tty_virtual_offset) {
|
|
|
1195 y = uv_tty_virtual_offset;
|
|
|
1196 } else if (y >= uv_tty_virtual_offset + uv_tty_virtual_height) {
|
|
|
1197 y = uv_tty_virtual_offset + uv_tty_virtual_height - 1;
|
|
|
1198 }
|
|
|
1199
|
|
|
1200 /* Adjust x */
|
|
|
1201 if (x_relative) {
|
|
|
1202 x = info->dwCursorPosition.X + x;
|
|
|
1203 }
|
|
|
1204 /* Clip x */
|
|
|
1205 if (x < 0) {
|
|
|
1206 x = 0;
|
|
|
1207 } else if (x >= uv_tty_virtual_width) {
|
|
|
1208 x = uv_tty_virtual_width - 1;
|
|
|
1209 }
|
|
|
1210
|
|
|
1211 result.X = (unsigned short) x;
|
|
|
1212 result.Y = (unsigned short) y;
|
|
|
1213 return result;
|
|
|
1214 }
|
|
|
1215
|
|
|
1216
|
|
|
1217 static int uv__tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
|
|
|
1218 DWORD* error) {
|
|
|
1219 DWORD written;
|
|
|
1220
|
|
|
1221 if (*error != ERROR_SUCCESS) {
|
|
|
1222 return -1;
|
|
|
1223 }
|
|
|
1224
|
|
|
1225 if (!WriteConsoleW(handle->handle,
|
|
|
1226 (void*) buffer,
|
|
|
1227 length,
|
|
|
1228 &written,
|
|
|
1229 NULL)) {
|
|
|
1230 *error = GetLastError();
|
|
|
1231 return -1;
|
|
|
1232 }
|
|
|
1233
|
|
|
1234 return 0;
|
|
|
1235 }
|
|
|
1236
|
|
|
1237
|
|
|
1238 static int uv__tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative,
|
|
|
1239 int y, unsigned char y_relative, DWORD* error) {
|
|
|
1240 CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
1241 COORD pos;
|
|
|
1242
|
|
|
1243 if (*error != ERROR_SUCCESS) {
|
|
|
1244 return -1;
|
|
|
1245 }
|
|
|
1246
|
|
|
1247 retry:
|
|
|
1248 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
|
|
|
1249 *error = GetLastError();
|
|
|
1250 }
|
|
|
1251
|
|
|
1252 pos = uv__tty_make_real_coord(handle, &info, x, x_relative, y, y_relative);
|
|
|
1253
|
|
|
1254 if (!SetConsoleCursorPosition(handle->handle, pos)) {
|
|
|
1255 if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
|
|
1256 /* The console may be resized - retry */
|
|
|
1257 goto retry;
|
|
|
1258 } else {
|
|
|
1259 *error = GetLastError();
|
|
|
1260 return -1;
|
|
|
1261 }
|
|
|
1262 }
|
|
|
1263
|
|
|
1264 return 0;
|
|
|
1265 }
|
|
|
1266
|
|
|
1267
|
|
|
1268 static int uv__tty_reset(uv_tty_t* handle, DWORD* error) {
|
|
|
1269 const COORD origin = {0, 0};
|
|
|
1270 const WORD char_attrs = uv_tty_default_text_attributes;
|
|
|
1271 CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
|
|
|
1272 DWORD count, written;
|
|
|
1273
|
|
|
1274 if (*error != ERROR_SUCCESS) {
|
|
|
1275 return -1;
|
|
|
1276 }
|
|
|
1277
|
|
|
1278 /* Reset original text attributes. */
|
|
|
1279 if (!SetConsoleTextAttribute(handle->handle, char_attrs)) {
|
|
|
1280 *error = GetLastError();
|
|
|
1281 return -1;
|
|
|
1282 }
|
|
|
1283
|
|
|
1284 /* Move the cursor position to (0, 0). */
|
|
|
1285 if (!SetConsoleCursorPosition(handle->handle, origin)) {
|
|
|
1286 *error = GetLastError();
|
|
|
1287 return -1;
|
|
|
1288 }
|
|
|
1289
|
|
|
1290 /* Clear the screen buffer. */
|
|
|
1291 retry:
|
|
|
1292 if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) {
|
|
|
1293 *error = GetLastError();
|
|
|
1294 return -1;
|
|
|
1295 }
|
|
|
1296
|
|
|
1297 count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y;
|
|
|
1298
|
|
|
1299 if (!(FillConsoleOutputCharacterW(handle->handle,
|
|
|
1300 L'\x20',
|
|
|
1301 count,
|
|
|
1302 origin,
|
|
|
1303 &written) &&
|
|
|
1304 FillConsoleOutputAttribute(handle->handle,
|
|
|
1305 char_attrs,
|
|
|
1306 written,
|
|
|
1307 origin,
|
|
|
1308 &written))) {
|
|
|
1309 if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
|
|
1310 /* The console may be resized - retry */
|
|
|
1311 goto retry;
|
|
|
1312 } else {
|
|
|
1313 *error = GetLastError();
|
|
|
1314 return -1;
|
|
|
1315 }
|
|
|
1316 }
|
|
|
1317
|
|
|
1318 /* Move the virtual window up to the top. */
|
|
|
1319 uv_tty_virtual_offset = 0;
|
|
|
1320 uv__tty_update_virtual_window(&screen_buffer_info);
|
|
|
1321
|
|
|
1322 /* Reset the cursor size and the cursor state. */
|
|
|
1323 if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) {
|
|
|
1324 *error = GetLastError();
|
|
|
1325 return -1;
|
|
|
1326 }
|
|
|
1327
|
|
|
1328 return 0;
|
|
|
1329 }
|
|
|
1330
|
|
|
1331
|
|
|
1332 static int uv__tty_clear(uv_tty_t* handle, int dir, char entire_screen,
|
|
|
1333 DWORD* error) {
|
|
|
1334 CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
1335 COORD start, end;
|
|
|
1336 DWORD count, written;
|
|
|
1337
|
|
|
1338 int x1, x2, y1, y2;
|
|
|
1339 int x1r, x2r, y1r, y2r;
|
|
|
1340
|
|
|
1341 if (*error != ERROR_SUCCESS) {
|
|
|
1342 return -1;
|
|
|
1343 }
|
|
|
1344
|
|
|
1345 if (dir == 0) {
|
|
|
1346 /* Clear from current position */
|
|
|
1347 x1 = 0;
|
|
|
1348 x1r = 1;
|
|
|
1349 } else {
|
|
|
1350 /* Clear from column 0 */
|
|
|
1351 x1 = 0;
|
|
|
1352 x1r = 0;
|
|
|
1353 }
|
|
|
1354
|
|
|
1355 if (dir == 1) {
|
|
|
1356 /* Clear to current position */
|
|
|
1357 x2 = 0;
|
|
|
1358 x2r = 1;
|
|
|
1359 } else {
|
|
|
1360 /* Clear to end of row. We pretend the console is 65536 characters wide,
|
|
|
1361 * uv__tty_make_real_coord will clip it to the actual console width. */
|
|
|
1362 x2 = 0xffff;
|
|
|
1363 x2r = 0;
|
|
|
1364 }
|
|
|
1365
|
|
|
1366 if (!entire_screen) {
|
|
|
1367 /* Stay on our own row */
|
|
|
1368 y1 = y2 = 0;
|
|
|
1369 y1r = y2r = 1;
|
|
|
1370 } else {
|
|
|
1371 /* Apply columns direction to row */
|
|
|
1372 y1 = x1;
|
|
|
1373 y1r = x1r;
|
|
|
1374 y2 = x2;
|
|
|
1375 y2r = x2r;
|
|
|
1376 }
|
|
|
1377
|
|
|
1378 retry:
|
|
|
1379 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
|
|
|
1380 *error = GetLastError();
|
|
|
1381 return -1;
|
|
|
1382 }
|
|
|
1383
|
|
|
1384 start = uv__tty_make_real_coord(handle, &info, x1, x1r, y1, y1r);
|
|
|
1385 end = uv__tty_make_real_coord(handle, &info, x2, x2r, y2, y2r);
|
|
|
1386 count = (end.Y * info.dwSize.X + end.X) -
|
|
|
1387 (start.Y * info.dwSize.X + start.X) + 1;
|
|
|
1388
|
|
|
1389 if (!(FillConsoleOutputCharacterW(handle->handle,
|
|
|
1390 L'\x20',
|
|
|
1391 count,
|
|
|
1392 start,
|
|
|
1393 &written) &&
|
|
|
1394 FillConsoleOutputAttribute(handle->handle,
|
|
|
1395 info.wAttributes,
|
|
|
1396 written,
|
|
|
1397 start,
|
|
|
1398 &written))) {
|
|
|
1399 if (GetLastError() == ERROR_INVALID_PARAMETER) {
|
|
|
1400 /* The console may be resized - retry */
|
|
|
1401 goto retry;
|
|
|
1402 } else {
|
|
|
1403 *error = GetLastError();
|
|
|
1404 return -1;
|
|
|
1405 }
|
|
|
1406 }
|
|
|
1407
|
|
|
1408 return 0;
|
|
|
1409 }
|
|
|
1410
|
|
|
1411 #define FLIP_FGBG \
|
|
|
1412 do { \
|
|
|
1413 WORD fg = info.wAttributes & 0xF; \
|
|
|
1414 WORD bg = info.wAttributes & 0xF0; \
|
|
|
1415 info.wAttributes &= 0xFF00; \
|
|
|
1416 info.wAttributes |= fg << 4; \
|
|
|
1417 info.wAttributes |= bg >> 4; \
|
|
|
1418 } while (0)
|
|
|
1419
|
|
|
1420 static int uv__tty_set_style(uv_tty_t* handle, DWORD* error) {
|
|
|
1421 unsigned short argc = handle->tty.wr.ansi_csi_argc;
|
|
|
1422 unsigned short* argv = handle->tty.wr.ansi_csi_argv;
|
|
|
1423 int i;
|
|
|
1424 CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
1425
|
|
|
1426 char fg_color = -1, bg_color = -1;
|
|
|
1427 char fg_bright = -1, bg_bright = -1;
|
|
|
1428 char inverse = -1;
|
|
|
1429
|
|
|
1430 if (argc == 0) {
|
|
|
1431 /* Reset mode */
|
|
|
1432 fg_color = uv_tty_default_fg_color;
|
|
|
1433 bg_color = uv_tty_default_bg_color;
|
|
|
1434 fg_bright = uv_tty_default_fg_bright;
|
|
|
1435 bg_bright = uv_tty_default_bg_bright;
|
|
|
1436 inverse = uv_tty_default_inverse;
|
|
|
1437 }
|
|
|
1438
|
|
|
1439 for (i = 0; i < argc; i++) {
|
|
|
1440 short arg = argv[i];
|
|
|
1441
|
|
|
1442 if (arg == 0) {
|
|
|
1443 /* Reset mode */
|
|
|
1444 fg_color = uv_tty_default_fg_color;
|
|
|
1445 bg_color = uv_tty_default_bg_color;
|
|
|
1446 fg_bright = uv_tty_default_fg_bright;
|
|
|
1447 bg_bright = uv_tty_default_bg_bright;
|
|
|
1448 inverse = uv_tty_default_inverse;
|
|
|
1449
|
|
|
1450 } else if (arg == 1) {
|
|
|
1451 /* Foreground bright on */
|
|
|
1452 fg_bright = 1;
|
|
|
1453
|
|
|
1454 } else if (arg == 2) {
|
|
|
1455 /* Both bright off */
|
|
|
1456 fg_bright = 0;
|
|
|
1457 bg_bright = 0;
|
|
|
1458
|
|
|
1459 } else if (arg == 5) {
|
|
|
1460 /* Background bright on */
|
|
|
1461 bg_bright = 1;
|
|
|
1462
|
|
|
1463 } else if (arg == 7) {
|
|
|
1464 /* Inverse: on */
|
|
|
1465 inverse = 1;
|
|
|
1466
|
|
|
1467 } else if (arg == 21 || arg == 22) {
|
|
|
1468 /* Foreground bright off */
|
|
|
1469 fg_bright = 0;
|
|
|
1470
|
|
|
1471 } else if (arg == 25) {
|
|
|
1472 /* Background bright off */
|
|
|
1473 bg_bright = 0;
|
|
|
1474
|
|
|
1475 } else if (arg == 27) {
|
|
|
1476 /* Inverse: off */
|
|
|
1477 inverse = 0;
|
|
|
1478
|
|
|
1479 } else if (arg >= 30 && arg <= 37) {
|
|
|
1480 /* Set foreground color */
|
|
|
1481 fg_color = arg - 30;
|
|
|
1482
|
|
|
1483 } else if (arg == 39) {
|
|
|
1484 /* Default text color */
|
|
|
1485 fg_color = uv_tty_default_fg_color;
|
|
|
1486 fg_bright = uv_tty_default_fg_bright;
|
|
|
1487
|
|
|
1488 } else if (arg >= 40 && arg <= 47) {
|
|
|
1489 /* Set background color */
|
|
|
1490 bg_color = arg - 40;
|
|
|
1491
|
|
|
1492 } else if (arg == 49) {
|
|
|
1493 /* Default background color */
|
|
|
1494 bg_color = uv_tty_default_bg_color;
|
|
|
1495 bg_bright = uv_tty_default_bg_bright;
|
|
|
1496
|
|
|
1497 } else if (arg >= 90 && arg <= 97) {
|
|
|
1498 /* Set bold foreground color */
|
|
|
1499 fg_bright = 1;
|
|
|
1500 fg_color = arg - 90;
|
|
|
1501
|
|
|
1502 } else if (arg >= 100 && arg <= 107) {
|
|
|
1503 /* Set bold background color */
|
|
|
1504 bg_bright = 1;
|
|
|
1505 bg_color = arg - 100;
|
|
|
1506
|
|
|
1507 }
|
|
|
1508 }
|
|
|
1509
|
|
|
1510 if (fg_color == -1 && bg_color == -1 && fg_bright == -1 &&
|
|
|
1511 bg_bright == -1 && inverse == -1) {
|
|
|
1512 /* Nothing changed */
|
|
|
1513 return 0;
|
|
|
1514 }
|
|
|
1515
|
|
|
1516 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
|
|
|
1517 *error = GetLastError();
|
|
|
1518 return -1;
|
|
|
1519 }
|
|
|
1520
|
|
|
1521 if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
|
|
|
1522 FLIP_FGBG;
|
|
|
1523 }
|
|
|
1524
|
|
|
1525 if (fg_color != -1) {
|
|
|
1526 info.wAttributes &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
|
|
1527 if (fg_color & 1) info.wAttributes |= FOREGROUND_RED;
|
|
|
1528 if (fg_color & 2) info.wAttributes |= FOREGROUND_GREEN;
|
|
|
1529 if (fg_color & 4) info.wAttributes |= FOREGROUND_BLUE;
|
|
|
1530 }
|
|
|
1531
|
|
|
1532 if (fg_bright != -1) {
|
|
|
1533 if (fg_bright) {
|
|
|
1534 info.wAttributes |= FOREGROUND_INTENSITY;
|
|
|
1535 } else {
|
|
|
1536 info.wAttributes &= ~FOREGROUND_INTENSITY;
|
|
|
1537 }
|
|
|
1538 }
|
|
|
1539
|
|
|
1540 if (bg_color != -1) {
|
|
|
1541 info.wAttributes &= ~(BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE);
|
|
|
1542 if (bg_color & 1) info.wAttributes |= BACKGROUND_RED;
|
|
|
1543 if (bg_color & 2) info.wAttributes |= BACKGROUND_GREEN;
|
|
|
1544 if (bg_color & 4) info.wAttributes |= BACKGROUND_BLUE;
|
|
|
1545 }
|
|
|
1546
|
|
|
1547 if (bg_bright != -1) {
|
|
|
1548 if (bg_bright) {
|
|
|
1549 info.wAttributes |= BACKGROUND_INTENSITY;
|
|
|
1550 } else {
|
|
|
1551 info.wAttributes &= ~BACKGROUND_INTENSITY;
|
|
|
1552 }
|
|
|
1553 }
|
|
|
1554
|
|
|
1555 if (inverse != -1) {
|
|
|
1556 if (inverse) {
|
|
|
1557 info.wAttributes |= COMMON_LVB_REVERSE_VIDEO;
|
|
|
1558 } else {
|
|
|
1559 info.wAttributes &= ~COMMON_LVB_REVERSE_VIDEO;
|
|
|
1560 }
|
|
|
1561 }
|
|
|
1562
|
|
|
1563 if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
|
|
|
1564 FLIP_FGBG;
|
|
|
1565 }
|
|
|
1566
|
|
|
1567 if (!SetConsoleTextAttribute(handle->handle, info.wAttributes)) {
|
|
|
1568 *error = GetLastError();
|
|
|
1569 return -1;
|
|
|
1570 }
|
|
|
1571
|
|
|
1572 return 0;
|
|
|
1573 }
|
|
|
1574
|
|
|
1575
|
|
|
1576 static int uv__tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
|
|
|
1577 DWORD* error) {
|
|
|
1578 CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
1579
|
|
|
1580 if (*error != ERROR_SUCCESS) {
|
|
|
1581 return -1;
|
|
|
1582 }
|
|
|
1583
|
|
|
1584 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
|
|
|
1585 *error = GetLastError();
|
|
|
1586 return -1;
|
|
|
1587 }
|
|
|
1588
|
|
|
1589 uv__tty_update_virtual_window(&info);
|
|
|
1590
|
|
|
1591 handle->tty.wr.saved_position.X = info.dwCursorPosition.X;
|
|
|
1592 handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y -
|
|
|
1593 uv_tty_virtual_offset;
|
|
|
1594 handle->flags |= UV_HANDLE_TTY_SAVED_POSITION;
|
|
|
1595
|
|
|
1596 if (save_attributes) {
|
|
|
1597 handle->tty.wr.saved_attributes = info.wAttributes &
|
|
|
1598 (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
|
|
|
1599 handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES;
|
|
|
1600 }
|
|
|
1601
|
|
|
1602 return 0;
|
|
|
1603 }
|
|
|
1604
|
|
|
1605
|
|
|
1606 static int uv__tty_restore_state(uv_tty_t* handle,
|
|
|
1607 unsigned char restore_attributes, DWORD* error) {
|
|
|
1608 CONSOLE_SCREEN_BUFFER_INFO info;
|
|
|
1609 WORD new_attributes;
|
|
|
1610
|
|
|
1611 if (*error != ERROR_SUCCESS) {
|
|
|
1612 return -1;
|
|
|
1613 }
|
|
|
1614
|
|
|
1615 if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) {
|
|
|
1616 if (uv__tty_move_caret(handle,
|
|
|
1617 handle->tty.wr.saved_position.X,
|
|
|
1618 0,
|
|
|
1619 handle->tty.wr.saved_position.Y,
|
|
|
1620 0,
|
|
|
1621 error) != 0) {
|
|
|
1622 return -1;
|
|
|
1623 }
|
|
|
1624 }
|
|
|
1625
|
|
|
1626 if (restore_attributes &&
|
|
|
1627 (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) {
|
|
|
1628 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
|
|
|
1629 *error = GetLastError();
|
|
|
1630 return -1;
|
|
|
1631 }
|
|
|
1632
|
|
|
1633 new_attributes = info.wAttributes;
|
|
|
1634 new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
|
|
|
1635 new_attributes |= handle->tty.wr.saved_attributes;
|
|
|
1636
|
|
|
1637 if (!SetConsoleTextAttribute(handle->handle, new_attributes)) {
|
|
|
1638 *error = GetLastError();
|
|
|
1639 return -1;
|
|
|
1640 }
|
|
|
1641 }
|
|
|
1642
|
|
|
1643 return 0;
|
|
|
1644 }
|
|
|
1645
|
|
|
1646 static int uv__tty_set_cursor_visibility(uv_tty_t* handle,
|
|
|
1647 BOOL visible,
|
|
|
1648 DWORD* error) {
|
|
|
1649 CONSOLE_CURSOR_INFO cursor_info;
|
|
|
1650
|
|
|
1651 if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
|
|
|
1652 *error = GetLastError();
|
|
|
1653 return -1;
|
|
|
1654 }
|
|
|
1655
|
|
|
1656 cursor_info.bVisible = visible;
|
|
|
1657
|
|
|
1658 if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
|
|
|
1659 *error = GetLastError();
|
|
|
1660 return -1;
|
|
|
1661 }
|
|
|
1662
|
|
|
1663 return 0;
|
|
|
1664 }
|
|
|
1665
|
|
|
1666 static int uv__tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
|
|
|
1667 CONSOLE_CURSOR_INFO cursor_info;
|
|
|
1668
|
|
|
1669 if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
|
|
|
1670 *error = GetLastError();
|
|
|
1671 return -1;
|
|
|
1672 }
|
|
|
1673
|
|
|
1674 if (style == 0) {
|
|
|
1675 cursor_info.dwSize = uv_tty_default_cursor_info.dwSize;
|
|
|
1676 } else if (style <= 2) {
|
|
|
1677 cursor_info.dwSize = CURSOR_SIZE_LARGE;
|
|
|
1678 } else {
|
|
|
1679 cursor_info.dwSize = CURSOR_SIZE_SMALL;
|
|
|
1680 }
|
|
|
1681
|
|
|
1682 if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
|
|
|
1683 *error = GetLastError();
|
|
|
1684 return -1;
|
|
|
1685 }
|
|
|
1686
|
|
|
1687 return 0;
|
|
|
1688 }
|
|
|
1689
|
|
|
1690
|
|
|
1691 static int uv__tty_write_bufs(uv_tty_t* handle,
|
|
|
1692 const uv_buf_t bufs[],
|
|
|
1693 unsigned int nbufs,
|
|
|
1694 DWORD* error) {
|
|
|
1695 /* We can only write 8k characters at a time. Windows can't handle much more
|
|
|
1696 * characters in a single console write anyway. */
|
|
|
1697 WCHAR utf16_buf[MAX_CONSOLE_CHAR];
|
|
|
1698 DWORD utf16_buf_used = 0;
|
|
|
1699 unsigned int i;
|
|
|
1700
|
|
|
1701 #define FLUSH_TEXT() \
|
|
|
1702 do { \
|
|
|
1703 if (utf16_buf_used > 0) { \
|
|
|
1704 uv__tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \
|
|
|
1705 utf16_buf_used = 0; \
|
|
|
1706 } \
|
|
|
1707 } while (0)
|
|
|
1708
|
|
|
1709 #define ENSURE_BUFFER_SPACE(wchars_needed) \
|
|
|
1710 if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { \
|
|
|
1711 FLUSH_TEXT(); \
|
|
|
1712 }
|
|
|
1713
|
|
|
1714 /* Cache for fast access */
|
|
|
1715 unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left;
|
|
|
1716 unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint;
|
|
|
1717 unsigned char previous_eol = handle->tty.wr.previous_eol;
|
|
|
1718 unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state;
|
|
|
1719
|
|
|
1720 /* Store the error here. If we encounter an error, stop trying to do i/o but
|
|
|
1721 * keep parsing the buffer so we leave the parser in a consistent state. */
|
|
|
1722 *error = ERROR_SUCCESS;
|
|
|
1723
|
|
|
1724 uv_sem_wait(&uv_tty_output_lock);
|
|
|
1725
|
|
|
1726 for (i = 0; i < nbufs; i++) {
|
|
|
1727 uv_buf_t buf = bufs[i];
|
|
|
1728 unsigned int j;
|
|
|
1729
|
|
|
1730 for (j = 0; j < buf.len; j++) {
|
|
|
1731 unsigned char c = buf.base[j];
|
|
|
1732
|
|
|
1733 /* Run the character through the utf8 decoder We happily accept non
|
|
|
1734 * shortest form encodings and invalid code points - there's no real harm
|
|
|
1735 * that can be done. */
|
|
|
1736 if (utf8_bytes_left == 0) {
|
|
|
1737 /* Read utf-8 start byte */
|
|
|
1738 DWORD first_zero_bit;
|
|
|
1739 unsigned char not_c = ~c;
|
|
|
1740 #ifdef _MSC_VER /* msvc */
|
|
|
1741 if (_BitScanReverse(&first_zero_bit, not_c)) {
|
|
|
1742 #else /* assume gcc */
|
|
|
1743 if (c != 0) {
|
|
|
1744 first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c);
|
|
|
1745 #endif
|
|
|
1746 if (first_zero_bit == 7) {
|
|
|
1747 /* Ascii - pass right through */
|
|
|
1748 utf8_codepoint = (unsigned int) c;
|
|
|
1749
|
|
|
1750 } else if (first_zero_bit <= 5) {
|
|
|
1751 /* Multibyte sequence */
|
|
|
1752 utf8_codepoint = (0xff >> (8 - first_zero_bit)) & c;
|
|
|
1753 utf8_bytes_left = (char) (6 - first_zero_bit);
|
|
|
1754
|
|
|
1755 } else {
|
|
|
1756 /* Invalid continuation */
|
|
|
1757 utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
|
|
|
1758 }
|
|
|
1759
|
|
|
1760 } else {
|
|
|
1761 /* 0xff -- invalid */
|
|
|
1762 utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
|
|
|
1763 }
|
|
|
1764
|
|
|
1765 } else if ((c & 0xc0) == 0x80) {
|
|
|
1766 /* Valid continuation of utf-8 multibyte sequence */
|
|
|
1767 utf8_bytes_left--;
|
|
|
1768 utf8_codepoint <<= 6;
|
|
|
1769 utf8_codepoint |= ((unsigned int) c & 0x3f);
|
|
|
1770
|
|
|
1771 } else {
|
|
|
1772 /* Start byte where continuation was expected. */
|
|
|
1773 utf8_bytes_left = 0;
|
|
|
1774 utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
|
|
|
1775 /* Patch buf offset so this character will be parsed again as a start
|
|
|
1776 * byte. */
|
|
|
1777 j--;
|
|
|
1778 }
|
|
|
1779
|
|
|
1780 /* Maybe we need to parse more bytes to find a character. */
|
|
|
1781 if (utf8_bytes_left != 0) {
|
|
|
1782 continue;
|
|
|
1783 }
|
|
|
1784
|
|
|
1785 /* Parse vt100/ansi escape codes */
|
|
|
1786 if (uv__vterm_state == UV_TTY_SUPPORTED) {
|
|
|
1787 /* Pass through escape codes if conhost supports them. */
|
|
|
1788 } else if (ansi_parser_state == ANSI_NORMAL) {
|
|
|
1789 switch (utf8_codepoint) {
|
|
|
1790 case '\033':
|
|
|
1791 ansi_parser_state = ANSI_ESCAPE_SEEN;
|
|
|
1792 continue;
|
|
|
1793
|
|
|
1794 case 0233:
|
|
|
1795 ansi_parser_state = ANSI_CSI;
|
|
|
1796 handle->tty.wr.ansi_csi_argc = 0;
|
|
|
1797 continue;
|
|
|
1798 }
|
|
|
1799
|
|
|
1800 } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) {
|
|
|
1801 switch (utf8_codepoint) {
|
|
|
1802 case '[':
|
|
|
1803 ansi_parser_state = ANSI_CSI;
|
|
|
1804 handle->tty.wr.ansi_csi_argc = 0;
|
|
|
1805 continue;
|
|
|
1806
|
|
|
1807 case '^':
|
|
|
1808 case '_':
|
|
|
1809 case 'P':
|
|
|
1810 case ']':
|
|
|
1811 /* Not supported, but we'll have to parse until we see a stop code,
|
|
|
1812 * e. g. ESC \ or BEL. */
|
|
|
1813 ansi_parser_state = ANSI_ST_CONTROL;
|
|
|
1814 continue;
|
|
|
1815
|
|
|
1816 case '\033':
|
|
|
1817 /* Ignore double escape. */
|
|
|
1818 continue;
|
|
|
1819
|
|
|
1820 case 'c':
|
|
|
1821 /* Full console reset. */
|
|
|
1822 FLUSH_TEXT();
|
|
|
1823 uv__tty_reset(handle, error);
|
|
|
1824 ansi_parser_state = ANSI_NORMAL;
|
|
|
1825 continue;
|
|
|
1826
|
|
|
1827 case '7':
|
|
|
1828 /* Save the cursor position and text attributes. */
|
|
|
1829 FLUSH_TEXT();
|
|
|
1830 uv__tty_save_state(handle, 1, error);
|
|
|
1831 ansi_parser_state = ANSI_NORMAL;
|
|
|
1832 continue;
|
|
|
1833
|
|
|
1834 case '8':
|
|
|
1835 /* Restore the cursor position and text attributes */
|
|
|
1836 FLUSH_TEXT();
|
|
|
1837 uv__tty_restore_state(handle, 1, error);
|
|
|
1838 ansi_parser_state = ANSI_NORMAL;
|
|
|
1839 continue;
|
|
|
1840
|
|
|
1841 default:
|
|
|
1842 if (utf8_codepoint >= '@' && utf8_codepoint <= '_') {
|
|
|
1843 /* Single-char control. */
|
|
|
1844 ansi_parser_state = ANSI_NORMAL;
|
|
|
1845 continue;
|
|
|
1846 } else {
|
|
|
1847 /* Invalid - proceed as normal, */
|
|
|
1848 ansi_parser_state = ANSI_NORMAL;
|
|
|
1849 }
|
|
|
1850 }
|
|
|
1851
|
|
|
1852 } else if (ansi_parser_state == ANSI_IGNORE) {
|
|
|
1853 /* We're ignoring this command. Stop only on command character. */
|
|
|
1854 if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
|
|
|
1855 ansi_parser_state = ANSI_NORMAL;
|
|
|
1856 }
|
|
|
1857 continue;
|
|
|
1858
|
|
|
1859 } else if (ansi_parser_state == ANSI_DECSCUSR) {
|
|
|
1860 /* So far we've the sequence `ESC [ arg space`, and we're waiting for
|
|
|
1861 * the final command byte. */
|
|
|
1862 if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
|
|
|
1863 /* Command byte */
|
|
|
1864 if (utf8_codepoint == 'q') {
|
|
|
1865 /* Change the cursor shape */
|
|
|
1866 int style = handle->tty.wr.ansi_csi_argc
|
|
|
1867 ? handle->tty.wr.ansi_csi_argv[0] : 1;
|
|
|
1868 if (style >= 0 && style <= 6) {
|
|
|
1869 FLUSH_TEXT();
|
|
|
1870 uv__tty_set_cursor_shape(handle, style, error);
|
|
|
1871 }
|
|
|
1872 }
|
|
|
1873
|
|
|
1874 /* Sequence ended - go back to normal state. */
|
|
|
1875 ansi_parser_state = ANSI_NORMAL;
|
|
|
1876 continue;
|
|
|
1877 }
|
|
|
1878 /* Unexpected character, but sequence hasn't ended yet. Ignore the rest
|
|
|
1879 * of the sequence. */
|
|
|
1880 ansi_parser_state = ANSI_IGNORE;
|
|
|
1881
|
|
|
1882 } else if (ansi_parser_state & ANSI_CSI) {
|
|
|
1883 /* So far we've seen `ESC [`, and we may or may not have already parsed
|
|
|
1884 * some of the arguments that follow. */
|
|
|
1885
|
|
|
1886 if (utf8_codepoint >= '0' && utf8_codepoint <= '9') {
|
|
|
1887 /* Parse a numerical argument. */
|
|
|
1888 if (!(ansi_parser_state & ANSI_IN_ARG)) {
|
|
|
1889 /* We were not currently parsing a number, add a new one. */
|
|
|
1890 /* Check for that there are too many arguments. */
|
|
|
1891 if (handle->tty.wr.ansi_csi_argc >=
|
|
|
1892 ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
|
|
|
1893 ansi_parser_state = ANSI_IGNORE;
|
|
|
1894 continue;
|
|
|
1895 }
|
|
|
1896 ansi_parser_state |= ANSI_IN_ARG;
|
|
|
1897 handle->tty.wr.ansi_csi_argc++;
|
|
|
1898 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
|
|
|
1899 (unsigned short) utf8_codepoint - '0';
|
|
|
1900 continue;
|
|
|
1901
|
|
|
1902 } else {
|
|
|
1903 /* We were already parsing a number. Parse next digit. */
|
|
|
1904 uint32_t value = 10 *
|
|
|
1905 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1];
|
|
|
1906
|
|
|
1907 /* Check for overflow. */
|
|
|
1908 if (value > UINT16_MAX) {
|
|
|
1909 ansi_parser_state = ANSI_IGNORE;
|
|
|
1910 continue;
|
|
|
1911 }
|
|
|
1912
|
|
|
1913 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
|
|
|
1914 (unsigned short) value + (utf8_codepoint - '0');
|
|
|
1915 continue;
|
|
|
1916 }
|
|
|
1917
|
|
|
1918 } else if (utf8_codepoint == ';') {
|
|
|
1919 /* Denotes the end of an argument. */
|
|
|
1920 if (ansi_parser_state & ANSI_IN_ARG) {
|
|
|
1921 ansi_parser_state &= ~ANSI_IN_ARG;
|
|
|
1922 continue;
|
|
|
1923
|
|
|
1924 } else {
|
|
|
1925 /* If ANSI_IN_ARG is not set, add another argument and default
|
|
|
1926 * it to 0. */
|
|
|
1927
|
|
|
1928 /* Check for too many arguments */
|
|
|
1929 if (handle->tty.wr.ansi_csi_argc >=
|
|
|
1930
|
|
|
1931 ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
|
|
|
1932 ansi_parser_state = ANSI_IGNORE;
|
|
|
1933 continue;
|
|
|
1934 }
|
|
|
1935
|
|
|
1936 handle->tty.wr.ansi_csi_argc++;
|
|
|
1937 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0;
|
|
|
1938 continue;
|
|
|
1939 }
|
|
|
1940
|
|
|
1941 } else if (utf8_codepoint == '?' &&
|
|
|
1942 !(ansi_parser_state & ANSI_IN_ARG) &&
|
|
|
1943 !(ansi_parser_state & ANSI_EXTENSION) &&
|
|
|
1944 handle->tty.wr.ansi_csi_argc == 0) {
|
|
|
1945 /* Pass through '?' if it is the first character after CSI */
|
|
|
1946 /* This is an extension character from the VT100 codeset */
|
|
|
1947 /* that is supported and used by most ANSI terminals today. */
|
|
|
1948 ansi_parser_state |= ANSI_EXTENSION;
|
|
|
1949 continue;
|
|
|
1950
|
|
|
1951 } else if (utf8_codepoint == ' ' &&
|
|
|
1952 !(ansi_parser_state & ANSI_EXTENSION)) {
|
|
|
1953 /* We expect a command byte to follow after this space. The only
|
|
|
1954 * command that we current support is 'set cursor style'. */
|
|
|
1955 ansi_parser_state = ANSI_DECSCUSR;
|
|
|
1956 continue;
|
|
|
1957
|
|
|
1958 } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
|
|
|
1959 /* Command byte */
|
|
|
1960 if (ansi_parser_state & ANSI_EXTENSION) {
|
|
|
1961 /* Sequence is `ESC [ ? args command`. */
|
|
|
1962 switch (utf8_codepoint) {
|
|
|
1963 case 'l':
|
|
|
1964 /* Hide the cursor */
|
|
|
1965 if (handle->tty.wr.ansi_csi_argc == 1 &&
|
|
|
1966 handle->tty.wr.ansi_csi_argv[0] == 25) {
|
|
|
1967 FLUSH_TEXT();
|
|
|
1968 uv__tty_set_cursor_visibility(handle, 0, error);
|
|
|
1969 }
|
|
|
1970 break;
|
|
|
1971
|
|
|
1972 case 'h':
|
|
|
1973 /* Show the cursor */
|
|
|
1974 if (handle->tty.wr.ansi_csi_argc == 1 &&
|
|
|
1975 handle->tty.wr.ansi_csi_argv[0] == 25) {
|
|
|
1976 FLUSH_TEXT();
|
|
|
1977 uv__tty_set_cursor_visibility(handle, 1, error);
|
|
|
1978 }
|
|
|
1979 break;
|
|
|
1980 }
|
|
|
1981
|
|
|
1982 } else {
|
|
|
1983 /* Sequence is `ESC [ args command`. */
|
|
|
1984 int x, y, d;
|
|
|
1985 switch (utf8_codepoint) {
|
|
|
1986 case 'A':
|
|
|
1987 /* cursor up */
|
|
|
1988 FLUSH_TEXT();
|
|
|
1989 y = -(handle->tty.wr.ansi_csi_argc
|
|
|
1990 ? handle->tty.wr.ansi_csi_argv[0] : 1);
|
|
|
1991 uv__tty_move_caret(handle, 0, 1, y, 1, error);
|
|
|
1992 break;
|
|
|
1993
|
|
|
1994 case 'B':
|
|
|
1995 /* cursor down */
|
|
|
1996 FLUSH_TEXT();
|
|
|
1997 y = handle->tty.wr.ansi_csi_argc
|
|
|
1998 ? handle->tty.wr.ansi_csi_argv[0] : 1;
|
|
|
1999 uv__tty_move_caret(handle, 0, 1, y, 1, error);
|
|
|
2000 break;
|
|
|
2001
|
|
|
2002 case 'C':
|
|
|
2003 /* cursor forward */
|
|
|
2004 FLUSH_TEXT();
|
|
|
2005 x = handle->tty.wr.ansi_csi_argc
|
|
|
2006 ? handle->tty.wr.ansi_csi_argv[0] : 1;
|
|
|
2007 uv__tty_move_caret(handle, x, 1, 0, 1, error);
|
|
|
2008 break;
|
|
|
2009
|
|
|
2010 case 'D':
|
|
|
2011 /* cursor back */
|
|
|
2012 FLUSH_TEXT();
|
|
|
2013 x = -(handle->tty.wr.ansi_csi_argc
|
|
|
2014 ? handle->tty.wr.ansi_csi_argv[0] : 1);
|
|
|
2015 uv__tty_move_caret(handle, x, 1, 0, 1, error);
|
|
|
2016 break;
|
|
|
2017
|
|
|
2018 case 'E':
|
|
|
2019 /* cursor next line */
|
|
|
2020 FLUSH_TEXT();
|
|
|
2021 y = handle->tty.wr.ansi_csi_argc
|
|
|
2022 ? handle->tty.wr.ansi_csi_argv[0] : 1;
|
|
|
2023 uv__tty_move_caret(handle, 0, 0, y, 1, error);
|
|
|
2024 break;
|
|
|
2025
|
|
|
2026 case 'F':
|
|
|
2027 /* cursor previous line */
|
|
|
2028 FLUSH_TEXT();
|
|
|
2029 y = -(handle->tty.wr.ansi_csi_argc
|
|
|
2030 ? handle->tty.wr.ansi_csi_argv[0] : 1);
|
|
|
2031 uv__tty_move_caret(handle, 0, 0, y, 1, error);
|
|
|
2032 break;
|
|
|
2033
|
|
|
2034 case 'G':
|
|
|
2035 /* cursor horizontal move absolute */
|
|
|
2036 FLUSH_TEXT();
|
|
|
2037 x = (handle->tty.wr.ansi_csi_argc >= 1 &&
|
|
|
2038 handle->tty.wr.ansi_csi_argv[0])
|
|
|
2039 ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
|
|
|
2040 uv__tty_move_caret(handle, x, 0, 0, 1, error);
|
|
|
2041 break;
|
|
|
2042
|
|
|
2043 case 'H':
|
|
|
2044 case 'f':
|
|
|
2045 /* cursor move absolute */
|
|
|
2046 FLUSH_TEXT();
|
|
|
2047 y = (handle->tty.wr.ansi_csi_argc >= 1 &&
|
|
|
2048 handle->tty.wr.ansi_csi_argv[0])
|
|
|
2049 ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
|
|
|
2050 x = (handle->tty.wr.ansi_csi_argc >= 2 &&
|
|
|
2051 handle->tty.wr.ansi_csi_argv[1])
|
|
|
2052 ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0;
|
|
|
2053 uv__tty_move_caret(handle, x, 0, y, 0, error);
|
|
|
2054 break;
|
|
|
2055
|
|
|
2056 case 'J':
|
|
|
2057 /* Erase screen */
|
|
|
2058 FLUSH_TEXT();
|
|
|
2059 d = handle->tty.wr.ansi_csi_argc
|
|
|
2060 ? handle->tty.wr.ansi_csi_argv[0] : 0;
|
|
|
2061 if (d >= 0 && d <= 2) {
|
|
|
2062 uv__tty_clear(handle, d, 1, error);
|
|
|
2063 }
|
|
|
2064 break;
|
|
|
2065
|
|
|
2066 case 'K':
|
|
|
2067 /* Erase line */
|
|
|
2068 FLUSH_TEXT();
|
|
|
2069 d = handle->tty.wr.ansi_csi_argc
|
|
|
2070 ? handle->tty.wr.ansi_csi_argv[0] : 0;
|
|
|
2071 if (d >= 0 && d <= 2) {
|
|
|
2072 uv__tty_clear(handle, d, 0, error);
|
|
|
2073 }
|
|
|
2074 break;
|
|
|
2075
|
|
|
2076 case 'm':
|
|
|
2077 /* Set style */
|
|
|
2078 FLUSH_TEXT();
|
|
|
2079 uv__tty_set_style(handle, error);
|
|
|
2080 break;
|
|
|
2081
|
|
|
2082 case 's':
|
|
|
2083 /* Save the cursor position. */
|
|
|
2084 FLUSH_TEXT();
|
|
|
2085 uv__tty_save_state(handle, 0, error);
|
|
|
2086 break;
|
|
|
2087
|
|
|
2088 case 'u':
|
|
|
2089 /* Restore the cursor position */
|
|
|
2090 FLUSH_TEXT();
|
|
|
2091 uv__tty_restore_state(handle, 0, error);
|
|
|
2092 break;
|
|
|
2093 }
|
|
|
2094 }
|
|
|
2095
|
|
|
2096 /* Sequence ended - go back to normal state. */
|
|
|
2097 ansi_parser_state = ANSI_NORMAL;
|
|
|
2098 continue;
|
|
|
2099
|
|
|
2100 } else {
|
|
|
2101 /* We don't support commands that use private mode characters or
|
|
|
2102 * intermediaries. Ignore the rest of the sequence. */
|
|
|
2103 ansi_parser_state = ANSI_IGNORE;
|
|
|
2104 continue;
|
|
|
2105 }
|
|
|
2106
|
|
|
2107 } else if (ansi_parser_state & ANSI_ST_CONTROL) {
|
|
|
2108 /* Unsupported control code.
|
|
|
2109 * Ignore everything until we see `BEL` or `ESC \`. */
|
|
|
2110 if (ansi_parser_state & ANSI_IN_STRING) {
|
|
|
2111 if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) {
|
|
|
2112 if (utf8_codepoint == '"') {
|
|
|
2113 ansi_parser_state &= ~ANSI_IN_STRING;
|
|
|
2114 } else if (utf8_codepoint == '\\') {
|
|
|
2115 ansi_parser_state |= ANSI_BACKSLASH_SEEN;
|
|
|
2116 }
|
|
|
2117 } else {
|
|
|
2118 ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
|
|
|
2119 }
|
|
|
2120 } else {
|
|
|
2121 if (utf8_codepoint == '\007' || (utf8_codepoint == '\\' &&
|
|
|
2122 (ansi_parser_state & ANSI_ESCAPE_SEEN))) {
|
|
|
2123 /* End of sequence */
|
|
|
2124 ansi_parser_state = ANSI_NORMAL;
|
|
|
2125 } else if (utf8_codepoint == '\033') {
|
|
|
2126 /* Escape character */
|
|
|
2127 ansi_parser_state |= ANSI_ESCAPE_SEEN;
|
|
|
2128 } else if (utf8_codepoint == '"') {
|
|
|
2129 /* String starting */
|
|
|
2130 ansi_parser_state |= ANSI_IN_STRING;
|
|
|
2131 ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
|
|
|
2132 ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
|
|
|
2133 } else {
|
|
|
2134 ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
|
|
|
2135 }
|
|
|
2136 }
|
|
|
2137 continue;
|
|
|
2138 } else {
|
|
|
2139 /* Inconsistent state */
|
|
|
2140 abort();
|
|
|
2141 }
|
|
|
2142
|
|
|
2143 if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) {
|
|
|
2144 /* EOL conversion - emit \r\n when we see \n. */
|
|
|
2145
|
|
|
2146 if (utf8_codepoint == 0x0a && previous_eol != 0x0d) {
|
|
|
2147 /* \n was not preceded by \r; print \r\n. */
|
|
|
2148 ENSURE_BUFFER_SPACE(2);
|
|
|
2149 utf16_buf[utf16_buf_used++] = L'\r';
|
|
|
2150 utf16_buf[utf16_buf_used++] = L'\n';
|
|
|
2151 } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) {
|
|
|
2152 /* \n was followed by \r; do not print the \r, since the source was
|
|
|
2153 * either \r\n\r (so the second \r is redundant) or was \n\r (so the
|
|
|
2154 * \n was processed by the last case and an \r automatically
|
|
|
2155 * inserted). */
|
|
|
2156 } else {
|
|
|
2157 /* \r without \n; print \r as-is. */
|
|
|
2158 ENSURE_BUFFER_SPACE(1);
|
|
|
2159 utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
|
|
|
2160 }
|
|
|
2161
|
|
|
2162 previous_eol = (char) utf8_codepoint;
|
|
|
2163
|
|
|
2164 } else if (utf8_codepoint <= 0xffff) {
|
|
|
2165 /* Encode character into utf-16 buffer. */
|
|
|
2166 ENSURE_BUFFER_SPACE(1);
|
|
|
2167 utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
|
|
|
2168 previous_eol = 0;
|
|
|
2169 } else {
|
|
|
2170 ENSURE_BUFFER_SPACE(2);
|
|
|
2171 utf8_codepoint -= 0x10000;
|
|
|
2172 utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint / 0x400 + 0xD800);
|
|
|
2173 utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint % 0x400 + 0xDC00);
|
|
|
2174 previous_eol = 0;
|
|
|
2175 }
|
|
|
2176 }
|
|
|
2177 }
|
|
|
2178
|
|
|
2179 /* Flush remaining characters */
|
|
|
2180 FLUSH_TEXT();
|
|
|
2181
|
|
|
2182 /* Copy cached values back to struct. */
|
|
|
2183 handle->tty.wr.utf8_bytes_left = utf8_bytes_left;
|
|
|
2184 handle->tty.wr.utf8_codepoint = utf8_codepoint;
|
|
|
2185 handle->tty.wr.previous_eol = previous_eol;
|
|
|
2186 handle->tty.wr.ansi_parser_state = ansi_parser_state;
|
|
|
2187
|
|
|
2188 uv_sem_post(&uv_tty_output_lock);
|
|
|
2189
|
|
|
2190 if (*error == STATUS_SUCCESS) {
|
|
|
2191 return 0;
|
|
|
2192 } else {
|
|
|
2193 return -1;
|
|
|
2194 }
|
|
|
2195
|
|
|
2196 #undef FLUSH_TEXT
|
|
|
2197 }
|
|
|
2198
|
|
|
2199
|
|
|
2200 int uv__tty_write(uv_loop_t* loop,
|
|
|
2201 uv_write_t* req,
|
|
|
2202 uv_tty_t* handle,
|
|
|
2203 const uv_buf_t bufs[],
|
|
|
2204 unsigned int nbufs,
|
|
|
2205 uv_write_cb cb) {
|
|
|
2206 DWORD error;
|
|
|
2207
|
|
|
2208 UV_REQ_INIT(req, UV_WRITE);
|
|
|
2209 req->handle = (uv_stream_t*) handle;
|
|
|
2210 req->cb = cb;
|
|
|
2211
|
|
|
2212 handle->reqs_pending++;
|
|
|
2213 handle->stream.conn.write_reqs_pending++;
|
|
|
2214 REGISTER_HANDLE_REQ(loop, handle);
|
|
|
2215
|
|
|
2216 req->u.io.queued_bytes = 0;
|
|
|
2217
|
|
|
2218 if (!uv__tty_write_bufs(handle, bufs, nbufs, &error)) {
|
|
|
2219 SET_REQ_SUCCESS(req);
|
|
|
2220 } else {
|
|
|
2221 SET_REQ_ERROR(req, error);
|
|
|
2222 }
|
|
|
2223
|
|
|
2224 uv__insert_pending_req(loop, (uv_req_t*) req);
|
|
|
2225
|
|
|
2226 return 0;
|
|
|
2227 }
|
|
|
2228
|
|
|
2229
|
|
|
2230 int uv__tty_try_write(uv_tty_t* handle,
|
|
|
2231 const uv_buf_t bufs[],
|
|
|
2232 unsigned int nbufs) {
|
|
|
2233 DWORD error;
|
|
|
2234
|
|
|
2235 if (handle->stream.conn.write_reqs_pending > 0)
|
|
|
2236 return UV_EAGAIN;
|
|
|
2237
|
|
|
2238 if (uv__tty_write_bufs(handle, bufs, nbufs, &error))
|
|
|
2239 return uv_translate_sys_error(error);
|
|
|
2240
|
|
|
2241 return uv__count_bufs(bufs, nbufs);
|
|
|
2242 }
|
|
|
2243
|
|
|
2244
|
|
|
2245 void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
|
|
|
2246 uv_write_t* req) {
|
|
|
2247 int err;
|
|
|
2248
|
|
|
2249 handle->write_queue_size -= req->u.io.queued_bytes;
|
|
|
2250 UNREGISTER_HANDLE_REQ(loop, handle);
|
|
|
2251
|
|
|
2252 if (req->cb) {
|
|
|
2253 err = GET_REQ_ERROR(req);
|
|
|
2254 req->cb(req, uv_translate_sys_error(err));
|
|
|
2255 }
|
|
|
2256
|
|
|
2257
|
|
|
2258 handle->stream.conn.write_reqs_pending--;
|
|
|
2259 if (handle->stream.conn.write_reqs_pending == 0 &&
|
|
|
2260 uv__is_stream_shutting(handle))
|
|
|
2261 uv__process_tty_shutdown_req(loop,
|
|
|
2262 handle,
|
|
|
2263 handle->stream.conn.shutdown_req);
|
|
|
2264
|
|
|
2265 DECREASE_PENDING_REQ_COUNT(handle);
|
|
|
2266 }
|
|
|
2267
|
|
|
2268
|
|
|
2269 void uv__tty_close(uv_tty_t* handle) {
|
|
|
2270 assert(handle->u.fd == -1 || handle->u.fd > 2);
|
|
|
2271 if (handle->flags & UV_HANDLE_READING)
|
|
|
2272 uv__tty_read_stop(handle);
|
|
|
2273
|
|
|
2274 if (handle->u.fd == -1)
|
|
|
2275 CloseHandle(handle->handle);
|
|
|
2276 else
|
|
|
2277 _close(handle->u.fd);
|
|
|
2278
|
|
|
2279 handle->u.fd = -1;
|
|
|
2280 handle->handle = INVALID_HANDLE_VALUE;
|
|
|
2281 handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
|
|
|
2282 uv__handle_closing(handle);
|
|
|
2283
|
|
|
2284 if (handle->reqs_pending == 0)
|
|
|
2285 uv__want_endgame(handle->loop, (uv_handle_t*) handle);
|
|
|
2286 }
|
|
|
2287
|
|
|
2288
|
|
|
2289 void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req) {
|
|
|
2290 assert(stream->stream.conn.write_reqs_pending == 0);
|
|
|
2291 assert(req);
|
|
|
2292
|
|
|
2293 stream->stream.conn.shutdown_req = NULL;
|
|
|
2294 UNREGISTER_HANDLE_REQ(loop, stream);
|
|
|
2295
|
|
|
2296 /* TTY shutdown is really just a no-op */
|
|
|
2297 if (req->cb) {
|
|
|
2298 if (stream->flags & UV_HANDLE_CLOSING) {
|
|
|
2299 req->cb(req, UV_ECANCELED);
|
|
|
2300 } else {
|
|
|
2301 req->cb(req, 0);
|
|
|
2302 }
|
|
|
2303 }
|
|
|
2304
|
|
|
2305 DECREASE_PENDING_REQ_COUNT(stream);
|
|
|
2306 }
|
|
|
2307
|
|
|
2308
|
|
|
2309 void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
|
|
|
2310 assert(handle->flags & UV_HANDLE_CLOSING);
|
|
|
2311 assert(handle->reqs_pending == 0);
|
|
|
2312
|
|
|
2313 /* The wait handle used for raw reading should be unregistered when the
|
|
|
2314 * wait callback runs. */
|
|
|
2315 assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
|
|
|
2316 handle->tty.rd.read_raw_wait == NULL);
|
|
|
2317
|
|
|
2318 assert(!(handle->flags & UV_HANDLE_CLOSED));
|
|
|
2319 uv__handle_close(handle);
|
|
|
2320 }
|
|
|
2321
|
|
|
2322
|
|
|
2323 int uv_tty_reset_mode(void) {
|
|
|
2324 /**
|
|
|
2325 * Shells on Windows do know to reset output flags after a program exits,
|
|
|
2326 * but not necessarily input flags, so we do that for them.
|
|
|
2327 */
|
|
|
2328 if (
|
|
|
2329 uv__tty_console_handle_in != INVALID_HANDLE_VALUE &&
|
|
|
2330 uv__tty_console_in_original_mode != (DWORD)-1 &&
|
|
|
2331 InterlockedExchange(&uv__tty_console_in_need_mode_reset, 0) != 0
|
|
|
2332 ) {
|
|
|
2333 SetConsoleMode(uv__tty_console_handle_in, uv__tty_console_in_original_mode);
|
|
|
2334 }
|
|
|
2335 return 0;
|
|
|
2336 }
|
|
|
2337
|
|
|
2338 /* Determine whether or not this version of windows supports
|
|
|
2339 * proper ANSI color codes. Should be supported as of windows
|
|
|
2340 * 10 version 1511, build number 10.0.10586.
|
|
|
2341 */
|
|
|
2342 static void uv__determine_vterm_state(HANDLE handle) {
|
|
|
2343 DWORD dwMode = 0;
|
|
|
2344
|
|
|
2345 uv__need_check_vterm_state = FALSE;
|
|
|
2346 if (!GetConsoleMode(handle, &dwMode)) {
|
|
|
2347 return;
|
|
|
2348 }
|
|
|
2349
|
|
|
2350 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
|
2351 if (!SetConsoleMode(handle, dwMode)) {
|
|
|
2352 return;
|
|
|
2353 }
|
|
|
2354
|
|
|
2355 uv__vterm_state = UV_TTY_SUPPORTED;
|
|
|
2356 }
|
|
|
2357
|
|
|
2358 static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
|
|
|
2359 NTSTATUS status;
|
|
|
2360 ULONG_PTR conhost_pid;
|
|
|
2361 MSG msg;
|
|
|
2362
|
|
|
2363 if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL)
|
|
|
2364 return 0;
|
|
|
2365
|
|
|
2366 status = pNtQueryInformationProcess(GetCurrentProcess(),
|
|
|
2367 ProcessConsoleHostProcess,
|
|
|
2368 &conhost_pid,
|
|
|
2369 sizeof(conhost_pid),
|
|
|
2370 NULL);
|
|
|
2371
|
|
|
2372 if (!NT_SUCCESS(status)) {
|
|
|
2373 /* We couldn't retrieve our console host process, probably because this
|
|
|
2374 * is a 32-bit process running on 64-bit Windows. Fall back to receiving
|
|
|
2375 * console events from the input stream only. */
|
|
|
2376 return 0;
|
|
|
2377 }
|
|
|
2378
|
|
|
2379 /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */
|
|
|
2380 conhost_pid &= ~(ULONG_PTR)0x3;
|
|
|
2381
|
|
|
2382 uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
2383 if (uv__tty_console_resized == NULL)
|
|
|
2384 return 0;
|
|
|
2385 if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread,
|
|
|
2386 NULL,
|
|
|
2387 WT_EXECUTELONGFUNCTION) == 0)
|
|
|
2388 return 0;
|
|
|
2389
|
|
|
2390 if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT,
|
|
|
2391 EVENT_CONSOLE_LAYOUT,
|
|
|
2392 NULL,
|
|
|
2393 uv__tty_console_resize_event,
|
|
|
2394 (DWORD)conhost_pid,
|
|
|
2395 0,
|
|
|
2396 WINEVENT_OUTOFCONTEXT))
|
|
|
2397 return 0;
|
|
|
2398
|
|
|
2399 while (GetMessage(&msg, NULL, 0, 0)) {
|
|
|
2400 TranslateMessage(&msg);
|
|
|
2401 DispatchMessage(&msg);
|
|
|
2402 }
|
|
|
2403 return 0;
|
|
|
2404 }
|
|
|
2405
|
|
|
2406 static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
|
|
|
2407 DWORD event,
|
|
|
2408 HWND hwnd,
|
|
|
2409 LONG idObject,
|
|
|
2410 LONG idChild,
|
|
|
2411 DWORD dwEventThread,
|
|
|
2412 DWORD dwmsEventTime) {
|
|
|
2413 SetEvent(uv__tty_console_resized);
|
|
|
2414 }
|
|
|
2415
|
|
|
2416 static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
|
|
|
2417 for (;;) {
|
|
|
2418 /* Make sure to not overwhelm the system with resize events */
|
|
|
2419 Sleep(33);
|
|
|
2420 WaitForSingleObject(uv__tty_console_resized, INFINITE);
|
|
|
2421 ResetEvent(uv__tty_console_resized);
|
|
|
2422 uv__tty_console_signal_resize();
|
|
|
2423 }
|
|
|
2424 return 0;
|
|
|
2425 }
|
|
|
2426
|
|
|
2427 static void uv__tty_console_signal_resize(void) {
|
|
|
2428 CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
|
|
2429 int width, height;
|
|
|
2430
|
|
|
2431 if (!GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info))
|
|
|
2432 return;
|
|
|
2433
|
|
|
2434 width = sb_info.dwSize.X;
|
|
|
2435 height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
|
|
|
2436
|
|
|
2437 uv_mutex_lock(&uv__tty_console_resize_mutex);
|
|
|
2438 if (width != uv__tty_console_width || height != uv__tty_console_height) {
|
|
|
2439 uv__tty_console_width = width;
|
|
|
2440 uv__tty_console_height = height;
|
|
|
2441 uv_mutex_unlock(&uv__tty_console_resize_mutex);
|
|
|
2442 uv__signal_dispatch(SIGWINCH);
|
|
|
2443 } else {
|
|
|
2444 uv_mutex_unlock(&uv__tty_console_resize_mutex);
|
|
|
2445 }
|
|
|
2446 }
|
|
|
2447
|
|
|
2448 void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {
|
|
|
2449 uv_sem_wait(&uv_tty_output_lock);
|
|
|
2450 uv__need_check_vterm_state = FALSE;
|
|
|
2451 uv__vterm_state = state;
|
|
|
2452 uv_sem_post(&uv_tty_output_lock);
|
|
|
2453 }
|
|
|
2454
|
|
|
2455 int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {
|
|
|
2456 uv_sem_wait(&uv_tty_output_lock);
|
|
|
2457 *state = uv__vterm_state;
|
|
|
2458 uv_sem_post(&uv_tty_output_lock);
|
|
|
2459 return 0;
|
|
|
2460 }
|