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