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