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