xref: /netbsd-src/external/mit/libuv/dist/src/win/tty.c (revision b5c47949a45ac972130c38cf13dfd8afb1f09285)
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 
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 
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  */
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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