1b9d4f8532635c62fcd328bbc6234d5985b1be84
[platform/upstream/cmake.git] / Utilities / cmlibuv / src / win / tty.c
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21
22 #include <assert.h>
23 #include <io.h>
24 #include <string.h>
25 #include <stdlib.h>
26
27 #if defined(_MSC_VER) && _MSC_VER < 1600
28 # include "uv/stdint-msvc2008.h"
29 #else
30 # include <stdint.h>
31 #endif
32
33 #ifndef COMMON_LVB_REVERSE_VIDEO
34 # define COMMON_LVB_REVERSE_VIDEO 0x4000
35 #endif
36
37 #include "uv.h"
38 #include "internal.h"
39 #include "handle-inl.h"
40 #include "stream-inl.h"
41 #include "req-inl.h"
42
43 #ifndef InterlockedOr
44 # define InterlockedOr _InterlockedOr
45 #endif
46
47 #define UNICODE_REPLACEMENT_CHARACTER (0xfffd)
48
49 #define ANSI_NORMAL           0x0000
50 #define ANSI_ESCAPE_SEEN      0x0002
51 #define ANSI_CSI              0x0004
52 #define ANSI_ST_CONTROL       0x0008
53 #define ANSI_IGNORE           0x0010
54 #define ANSI_IN_ARG           0x0020
55 #define ANSI_IN_STRING        0x0040
56 #define ANSI_BACKSLASH_SEEN   0x0080
57 #define ANSI_EXTENSION        0x0100
58 #define ANSI_DECSCUSR         0x0200
59
60 #define MAX_INPUT_BUFFER_LENGTH 8192
61 #define MAX_CONSOLE_CHAR 8192
62
63 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
64 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
65 #endif
66
67 #define CURSOR_SIZE_SMALL     25
68 #define CURSOR_SIZE_LARGE     100
69
70 static void uv_tty_capture_initial_style(
71     CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
72     CONSOLE_CURSOR_INFO* cursor_info);
73 static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info);
74 static int uv__cancel_read_console(uv_tty_t* handle);
75
76
77 /* Null uv_buf_t */
78 static const uv_buf_t uv_null_buf_ = { 0, NULL };
79
80 enum uv__read_console_status_e {
81   NOT_STARTED,
82   IN_PROGRESS,
83   TRAP_REQUESTED,
84   COMPLETED
85 };
86
87 static volatile LONG uv__read_console_status = NOT_STARTED;
88 static volatile LONG uv__restore_screen_state;
89 static CONSOLE_SCREEN_BUFFER_INFO uv__saved_screen_state;
90
91
92 /*
93  * The console virtual window.
94  *
95  * Normally cursor movement in windows is relative to the console screen buffer,
96  * e.g. the application is allowed to overwrite the 'history'. This is very
97  * inconvenient, it makes absolute cursor movement pretty useless. There is
98  * also the concept of 'client rect' which is defined by the actual size of
99  * the console window and the scroll position of the screen buffer, but it's
100  * very volatile because it changes when the user scrolls.
101  *
102  * To make cursor movement behave sensibly we define a virtual window to which
103  * cursor movement is confined. The virtual window is always as wide as the
104  * console screen buffer, but it's height is defined by the size of the
105  * console window. The top of the virtual window aligns with the position
106  * of the caret when the first stdout/err handle is created, unless that would
107  * mean that it would extend beyond the bottom of the screen buffer -  in that
108  * that case it's located as far down as possible.
109  *
110  * When the user writes a long text or many newlines, such that the output
111  * reaches beyond the bottom of the virtual window, the virtual window is
112  * shifted downwards, but not resized.
113  *
114  * Since all tty i/o happens on the same console, this window is shared
115  * between all stdout/stderr handles.
116  */
117
118 static int uv_tty_virtual_offset = -1;
119 static int uv_tty_virtual_height = -1;
120 static int uv_tty_virtual_width = -1;
121
122 /* The console window size
123  * We keep this separate from uv_tty_virtual_*. We use those values to only
124  * handle signalling SIGWINCH
125  */
126
127 static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE;
128 static int uv__tty_console_height = -1;
129 static int uv__tty_console_width = -1;
130 static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
131 static uv_mutex_t uv__tty_console_resize_mutex;
132
133 static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param);
134 static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
135                                                   DWORD event,
136                                                   HWND hwnd,
137                                                   LONG idObject,
138                                                   LONG idChild,
139                                                   DWORD dwEventThread,
140                                                   DWORD dwmsEventTime);
141 static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param);
142 static void uv__tty_console_signal_resize(void);
143
144 /* We use a semaphore rather than a mutex or critical section because in some
145    cases (uv__cancel_read_console) we need take the lock in the main thread and
146    release it in another thread. Using a semaphore ensures that in such
147    scenario the main thread will still block when trying to acquire the lock. */
148 static uv_sem_t uv_tty_output_lock;
149
150 static WORD uv_tty_default_text_attributes =
151     FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
152
153 static char uv_tty_default_fg_color = 7;
154 static char uv_tty_default_bg_color = 0;
155 static char uv_tty_default_fg_bright = 0;
156 static char uv_tty_default_bg_bright = 0;
157 static char uv_tty_default_inverse = 0;
158
159 static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info;
160
161 /* Determine whether or not ANSI support is enabled. */
162 static BOOL uv__need_check_vterm_state = TRUE;
163 static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
164 static void uv__determine_vterm_state(HANDLE handle);
165
166 void uv_console_init(void) {
167   if (uv_sem_init(&uv_tty_output_lock, 1))
168     abort();
169   uv__tty_console_handle = CreateFileW(L"CONOUT$",
170                                        GENERIC_READ | GENERIC_WRITE,
171                                        FILE_SHARE_WRITE,
172                                        0,
173                                        OPEN_EXISTING,
174                                        0,
175                                        0);
176   if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
177     CONSOLE_SCREEN_BUFFER_INFO sb_info;
178     QueueUserWorkItem(uv__tty_console_resize_message_loop_thread,
179                       NULL,
180                       WT_EXECUTELONGFUNCTION);
181     uv_mutex_init(&uv__tty_console_resize_mutex);
182     if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
183       uv__tty_console_width = sb_info.dwSize.X;
184       uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
185     }
186   }
187 }
188
189
190 int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
191   BOOL readable;
192   DWORD NumberOfEvents;
193   HANDLE handle;
194   CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
195   CONSOLE_CURSOR_INFO cursor_info;
196   (void)unused;
197
198   uv__once_init();
199   handle = (HANDLE) uv__get_osfhandle(fd);
200   if (handle == INVALID_HANDLE_VALUE)
201     return UV_EBADF;
202
203   if (fd <= 2) {
204     /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
205      * underlying OS handle and forget about the original fd.
206      * We could also opt to use the original OS handle and just never close it,
207      * but then there would be no reliable way to cancel pending read operations
208      * upon close.
209      */
210     if (!DuplicateHandle(INVALID_HANDLE_VALUE,
211                          handle,
212                          INVALID_HANDLE_VALUE,
213                          &handle,
214                          0,
215                          FALSE,
216                          DUPLICATE_SAME_ACCESS))
217       return uv_translate_sys_error(GetLastError());
218     fd = -1;
219   }
220
221   readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents);
222   if (!readable) {
223     /* Obtain the screen buffer info with the output handle. */
224     if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) {
225       return uv_translate_sys_error(GetLastError());
226     }
227
228     /* Obtain the cursor info with the output handle. */
229     if (!GetConsoleCursorInfo(handle, &cursor_info)) {
230       return uv_translate_sys_error(GetLastError());
231     }
232
233     /* Obtain the tty_output_lock because the virtual window state is shared
234      * between all uv_tty_t handles. */
235     uv_sem_wait(&uv_tty_output_lock);
236
237     if (uv__need_check_vterm_state)
238       uv__determine_vterm_state(handle);
239
240     /* Remember the original console text attributes and cursor info. */
241     uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info);
242
243     uv_tty_update_virtual_window(&screen_buffer_info);
244
245     uv_sem_post(&uv_tty_output_lock);
246   }
247
248
249   uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY);
250   uv_connection_init((uv_stream_t*) tty);
251
252   tty->handle = handle;
253   tty->u.fd = fd;
254   tty->reqs_pending = 0;
255   tty->flags |= UV_HANDLE_BOUND;
256
257   if (readable) {
258     /* Initialize TTY input specific fields. */
259     tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
260     /* TODO: remove me in v2.x. */
261     tty->tty.rd.unused_ = NULL;
262     tty->tty.rd.read_line_buffer = uv_null_buf_;
263     tty->tty.rd.read_raw_wait = NULL;
264
265     /* Init keycode-to-vt100 mapper state. */
266     tty->tty.rd.last_key_len = 0;
267     tty->tty.rd.last_key_offset = 0;
268     tty->tty.rd.last_utf16_high_surrogate = 0;
269     memset(&tty->tty.rd.last_input_record, 0, sizeof tty->tty.rd.last_input_record);
270   } else {
271     /* TTY output specific fields. */
272     tty->flags |= UV_HANDLE_WRITABLE;
273
274     /* Init utf8-to-utf16 conversion state. */
275     tty->tty.wr.utf8_bytes_left = 0;
276     tty->tty.wr.utf8_codepoint = 0;
277
278     /* Initialize eol conversion state */
279     tty->tty.wr.previous_eol = 0;
280
281     /* Init ANSI parser state. */
282     tty->tty.wr.ansi_parser_state = ANSI_NORMAL;
283   }
284
285   return 0;
286 }
287
288
289 /* Set the default console text attributes based on how the console was
290  * configured when libuv started.
291  */
292 static void uv_tty_capture_initial_style(
293     CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info,
294     CONSOLE_CURSOR_INFO* cursor_info) {
295   static int style_captured = 0;
296
297   /* Only do this once.
298      Assumption: Caller has acquired uv_tty_output_lock. */
299   if (style_captured)
300     return;
301
302   /* Save raw win32 attributes. */
303   uv_tty_default_text_attributes = screen_buffer_info->wAttributes;
304
305   /* Convert black text on black background to use white text. */
306   if (uv_tty_default_text_attributes == 0)
307     uv_tty_default_text_attributes = 7;
308
309   /* Convert Win32 attributes to ANSI colors. */
310   uv_tty_default_fg_color = 0;
311   uv_tty_default_bg_color = 0;
312   uv_tty_default_fg_bright = 0;
313   uv_tty_default_bg_bright = 0;
314   uv_tty_default_inverse = 0;
315
316   if (uv_tty_default_text_attributes & FOREGROUND_RED)
317     uv_tty_default_fg_color |= 1;
318
319   if (uv_tty_default_text_attributes & FOREGROUND_GREEN)
320     uv_tty_default_fg_color |= 2;
321
322   if (uv_tty_default_text_attributes & FOREGROUND_BLUE)
323     uv_tty_default_fg_color |= 4;
324
325   if (uv_tty_default_text_attributes & BACKGROUND_RED)
326     uv_tty_default_bg_color |= 1;
327
328   if (uv_tty_default_text_attributes & BACKGROUND_GREEN)
329     uv_tty_default_bg_color |= 2;
330
331   if (uv_tty_default_text_attributes & BACKGROUND_BLUE)
332     uv_tty_default_bg_color |= 4;
333
334   if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY)
335     uv_tty_default_fg_bright = 1;
336
337   if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY)
338     uv_tty_default_bg_bright = 1;
339
340   if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO)
341     uv_tty_default_inverse = 1;
342
343   /* Save the cursor size and the cursor state. */
344   uv_tty_default_cursor_info = *cursor_info;
345
346   style_captured = 1;
347 }
348
349
350 int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
351   DWORD flags;
352   unsigned char was_reading;
353   uv_alloc_cb alloc_cb;
354   uv_read_cb read_cb;
355   int err;
356
357   if (!(tty->flags & UV_HANDLE_TTY_READABLE)) {
358     return UV_EINVAL;
359   }
360
361   if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
362     return 0;
363   }
364
365   switch (mode) {
366     case UV_TTY_MODE_NORMAL:
367       flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
368       break;
369     case UV_TTY_MODE_RAW:
370       flags = ENABLE_WINDOW_INPUT;
371       break;
372     case UV_TTY_MODE_IO:
373       return UV_ENOTSUP;
374     default:
375       return UV_EINVAL;
376   }
377
378   /* If currently reading, stop, and restart reading. */
379   if (tty->flags & UV_HANDLE_READING) {
380     was_reading = 1;
381     alloc_cb = tty->alloc_cb;
382     read_cb = tty->read_cb;
383     err = uv_tty_read_stop(tty);
384     if (err) {
385       return uv_translate_sys_error(err);
386     }
387   } else {
388     was_reading = 0;
389     alloc_cb = NULL;
390     read_cb = NULL;
391   }
392
393   uv_sem_wait(&uv_tty_output_lock);
394   if (!SetConsoleMode(tty->handle, flags)) {
395     err = uv_translate_sys_error(GetLastError());
396     uv_sem_post(&uv_tty_output_lock);
397     return err;
398   }
399   uv_sem_post(&uv_tty_output_lock);
400
401   /* Update flag. */
402   tty->flags &= ~UV_HANDLE_TTY_RAW;
403   tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
404
405   /* If we just stopped reading, restart. */
406   if (was_reading) {
407     err = uv_tty_read_start(tty, alloc_cb, read_cb);
408     if (err) {
409       return uv_translate_sys_error(err);
410     }
411   }
412
413   return 0;
414 }
415
416
417 int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
418   CONSOLE_SCREEN_BUFFER_INFO info;
419
420   if (!GetConsoleScreenBufferInfo(tty->handle, &info)) {
421     return uv_translate_sys_error(GetLastError());
422   }
423
424   uv_sem_wait(&uv_tty_output_lock);
425   uv_tty_update_virtual_window(&info);
426   uv_sem_post(&uv_tty_output_lock);
427
428   *width = uv_tty_virtual_width;
429   *height = uv_tty_virtual_height;
430
431   return 0;
432 }
433
434
435 static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) {
436   uv_loop_t* loop;
437   uv_tty_t* handle;
438   uv_req_t* req;
439
440   assert(data);
441   assert(!didTimeout);
442
443   req = (uv_req_t*) data;
444   handle = (uv_tty_t*) req->data;
445   loop = handle->loop;
446
447   UnregisterWait(handle->tty.rd.read_raw_wait);
448   handle->tty.rd.read_raw_wait = NULL;
449
450   SET_REQ_SUCCESS(req);
451   POST_COMPLETION_FOR_REQ(loop, req);
452 }
453
454
455 static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
456   uv_read_t* req;
457   BOOL r;
458
459   assert(handle->flags & UV_HANDLE_READING);
460   assert(!(handle->flags & UV_HANDLE_READ_PENDING));
461
462   assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
463
464   handle->tty.rd.read_line_buffer = uv_null_buf_;
465
466   req = &handle->read_req;
467   memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
468
469   r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait,
470                                   handle->handle,
471                                   uv_tty_post_raw_read,
472                                   (void*) req,
473                                   INFINITE,
474                                   WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
475   if (!r) {
476     handle->tty.rd.read_raw_wait = NULL;
477     SET_REQ_ERROR(req, GetLastError());
478     uv_insert_pending_req(loop, (uv_req_t*)req);
479   }
480
481   handle->flags |= UV_HANDLE_READ_PENDING;
482   handle->reqs_pending++;
483 }
484
485
486 static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
487   uv_loop_t* loop;
488   uv_tty_t* handle;
489   uv_req_t* req;
490   DWORD bytes, read_bytes;
491   WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3];
492   DWORD chars, read_chars;
493   LONG status;
494   COORD pos;
495   BOOL read_console_success;
496
497   assert(data);
498
499   req = (uv_req_t*) data;
500   handle = (uv_tty_t*) req->data;
501   loop = handle->loop;
502
503   assert(handle->tty.rd.read_line_buffer.base != NULL);
504   assert(handle->tty.rd.read_line_buffer.len > 0);
505
506   /* ReadConsole can't handle big buffers. */
507   if (handle->tty.rd.read_line_buffer.len < MAX_INPUT_BUFFER_LENGTH) {
508     bytes = handle->tty.rd.read_line_buffer.len;
509   } else {
510     bytes = MAX_INPUT_BUFFER_LENGTH;
511   }
512
513   /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8
514    * codeunits to encode. */
515   chars = bytes / 3;
516
517   status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS);
518   if (status == TRAP_REQUESTED) {
519     SET_REQ_SUCCESS(req);
520     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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 - uv_tty_virtual_offset;
1576   handle->flags |= UV_HANDLE_TTY_SAVED_POSITION;
1577
1578   if (save_attributes) {
1579     handle->tty.wr.saved_attributes = info.wAttributes &
1580         (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
1581     handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES;
1582   }
1583
1584   return 0;
1585 }
1586
1587
1588 static int uv_tty_restore_state(uv_tty_t* handle,
1589     unsigned char restore_attributes, DWORD* error) {
1590   CONSOLE_SCREEN_BUFFER_INFO info;
1591   WORD new_attributes;
1592
1593   if (*error != ERROR_SUCCESS) {
1594     return -1;
1595   }
1596
1597   if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) {
1598     if (uv_tty_move_caret(handle,
1599                           handle->tty.wr.saved_position.X,
1600                           0,
1601                           handle->tty.wr.saved_position.Y,
1602                           0,
1603                           error) != 0) {
1604       return -1;
1605     }
1606   }
1607
1608   if (restore_attributes &&
1609       (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) {
1610     if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
1611       *error = GetLastError();
1612       return -1;
1613     }
1614
1615     new_attributes = info.wAttributes;
1616     new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
1617     new_attributes |= handle->tty.wr.saved_attributes;
1618
1619     if (!SetConsoleTextAttribute(handle->handle, new_attributes)) {
1620       *error = GetLastError();
1621       return -1;
1622     }
1623   }
1624
1625   return 0;
1626 }
1627
1628 static int uv_tty_set_cursor_visibility(uv_tty_t* handle,
1629                                         BOOL visible,
1630                                         DWORD* error) {
1631   CONSOLE_CURSOR_INFO cursor_info;
1632
1633   if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
1634     *error = GetLastError();
1635     return -1;
1636   }
1637
1638   cursor_info.bVisible = visible;
1639
1640   if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
1641     *error = GetLastError();
1642     return -1;
1643   }
1644
1645   return 0;
1646 }
1647
1648 static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
1649   CONSOLE_CURSOR_INFO cursor_info;
1650
1651   if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
1652     *error = GetLastError();
1653     return -1;
1654   }
1655
1656   if (style == 0) {
1657     cursor_info.dwSize = uv_tty_default_cursor_info.dwSize;
1658   } else if (style <= 2) {
1659     cursor_info.dwSize = CURSOR_SIZE_LARGE;
1660   } else {
1661     cursor_info.dwSize = CURSOR_SIZE_SMALL;
1662   }
1663
1664   if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
1665     *error = GetLastError();
1666     return -1;
1667   }
1668
1669   return 0;
1670 }
1671
1672
1673 static int uv_tty_write_bufs(uv_tty_t* handle,
1674                              const uv_buf_t bufs[],
1675                              unsigned int nbufs,
1676                              DWORD* error) {
1677   /* We can only write 8k characters at a time. Windows can't handle much more
1678    * characters in a single console write anyway. */
1679   WCHAR utf16_buf[MAX_CONSOLE_CHAR];
1680   DWORD utf16_buf_used = 0;
1681   unsigned int i;
1682
1683 #define FLUSH_TEXT()                                                \
1684   do {                                                              \
1685     if (utf16_buf_used > 0) {                                       \
1686       uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error);   \
1687       utf16_buf_used = 0;                                           \
1688     }                                                               \
1689   } while (0)
1690
1691 #define ENSURE_BUFFER_SPACE(wchars_needed)                          \
1692   if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) {     \
1693     FLUSH_TEXT();                                                   \
1694   }
1695
1696   /* Cache for fast access */
1697   unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left;
1698   unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint;
1699   unsigned char previous_eol = handle->tty.wr.previous_eol;
1700   unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state;
1701
1702   /* Store the error here. If we encounter an error, stop trying to do i/o but
1703    * keep parsing the buffer so we leave the parser in a consistent state. */
1704   *error = ERROR_SUCCESS;
1705
1706   uv_sem_wait(&uv_tty_output_lock);
1707
1708   for (i = 0; i < nbufs; i++) {
1709     uv_buf_t buf = bufs[i];
1710     unsigned int j;
1711
1712     for (j = 0; j < buf.len; j++) {
1713       unsigned char c = buf.base[j];
1714
1715       /* Run the character through the utf8 decoder We happily accept non
1716        * shortest form encodings and invalid code points - there's no real harm
1717        * that can be done. */
1718       if (utf8_bytes_left == 0) {
1719         /* Read utf-8 start byte */
1720         DWORD first_zero_bit;
1721         unsigned char not_c = ~c;
1722 #ifdef _MSC_VER /* msvc */
1723         if (_BitScanReverse(&first_zero_bit, not_c)) {
1724 #else /* assume gcc */
1725         if (c != 0) {
1726           first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c);
1727 #endif
1728           if (first_zero_bit == 7) {
1729             /* Ascii - pass right through */
1730             utf8_codepoint = (unsigned int) c;
1731
1732           } else if (first_zero_bit <= 5) {
1733             /* Multibyte sequence */
1734             utf8_codepoint = (0xff >> (8 - first_zero_bit)) & c;
1735             utf8_bytes_left = (char) (6 - first_zero_bit);
1736
1737           } else {
1738             /* Invalid continuation */
1739             utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
1740           }
1741
1742         } else {
1743           /* 0xff -- invalid */
1744           utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
1745         }
1746
1747       } else if ((c & 0xc0) == 0x80) {
1748         /* Valid continuation of utf-8 multibyte sequence */
1749         utf8_bytes_left--;
1750         utf8_codepoint <<= 6;
1751         utf8_codepoint |= ((unsigned int) c & 0x3f);
1752
1753       } else {
1754         /* Start byte where continuation was expected. */
1755         utf8_bytes_left = 0;
1756         utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
1757         /* Patch buf offset so this character will be parsed again as a start
1758          * byte. */
1759         j--;
1760       }
1761
1762       /* Maybe we need to parse more bytes to find a character. */
1763       if (utf8_bytes_left != 0) {
1764         continue;
1765       }
1766
1767       /* Parse vt100/ansi escape codes */
1768       if (uv__vterm_state == UV_TTY_SUPPORTED) {
1769         /* Pass through escape codes if conhost supports them. */
1770       } else if (ansi_parser_state == ANSI_NORMAL) {
1771         switch (utf8_codepoint) {
1772           case '\033':
1773             ansi_parser_state = ANSI_ESCAPE_SEEN;
1774             continue;
1775
1776           case 0233:
1777             ansi_parser_state = ANSI_CSI;
1778             handle->tty.wr.ansi_csi_argc = 0;
1779             continue;
1780         }
1781
1782       } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) {
1783         switch (utf8_codepoint) {
1784           case '[':
1785             ansi_parser_state = ANSI_CSI;
1786             handle->tty.wr.ansi_csi_argc = 0;
1787             continue;
1788
1789           case '^':
1790           case '_':
1791           case 'P':
1792           case ']':
1793             /* Not supported, but we'll have to parse until we see a stop code,
1794              * e. g. ESC \ or BEL. */
1795             ansi_parser_state = ANSI_ST_CONTROL;
1796             continue;
1797
1798           case '\033':
1799             /* Ignore double escape. */
1800             continue;
1801
1802           case 'c':
1803             /* Full console reset. */
1804             FLUSH_TEXT();
1805             uv_tty_reset(handle, error);
1806             ansi_parser_state = ANSI_NORMAL;
1807             continue;
1808
1809           case '7':
1810             /* Save the cursor position and text attributes. */
1811             FLUSH_TEXT();
1812             uv_tty_save_state(handle, 1, error);
1813             ansi_parser_state = ANSI_NORMAL;
1814             continue;
1815
1816           case '8':
1817             /* Restore the cursor position and text attributes */
1818             FLUSH_TEXT();
1819             uv_tty_restore_state(handle, 1, error);
1820             ansi_parser_state = ANSI_NORMAL;
1821             continue;
1822
1823           default:
1824             if (utf8_codepoint >= '@' && utf8_codepoint <= '_') {
1825               /* Single-char control. */
1826               ansi_parser_state = ANSI_NORMAL;
1827               continue;
1828             } else {
1829               /* Invalid - proceed as normal, */
1830               ansi_parser_state = ANSI_NORMAL;
1831             }
1832         }
1833
1834       } else if (ansi_parser_state == ANSI_IGNORE) {
1835         /* We're ignoring this command. Stop only on command character. */
1836         if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
1837           ansi_parser_state = ANSI_NORMAL;
1838         }
1839         continue;
1840
1841       } else if (ansi_parser_state == ANSI_DECSCUSR) {
1842         /* So far we've the sequence `ESC [ arg space`, and we're waiting for
1843          * the final command byte. */
1844         if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
1845           /* Command byte */
1846           if (utf8_codepoint == 'q') {
1847             /* Change the cursor shape */
1848             int style = handle->tty.wr.ansi_csi_argc
1849               ? handle->tty.wr.ansi_csi_argv[0] : 1;
1850             if (style >= 0 && style <= 6) {
1851               FLUSH_TEXT();
1852               uv_tty_set_cursor_shape(handle, style, error);
1853             }
1854           }
1855
1856           /* Sequence ended - go back to normal state. */
1857           ansi_parser_state = ANSI_NORMAL;
1858           continue;
1859         }
1860         /* Unexpected character, but sequence hasn't ended yet. Ignore the rest
1861          * of the sequence. */
1862         ansi_parser_state = ANSI_IGNORE;
1863
1864       } else if (ansi_parser_state & ANSI_CSI) {
1865         /* So far we've seen `ESC [`, and we may or may not have already parsed
1866          * some of the arguments that follow. */
1867
1868         if (utf8_codepoint >= '0' && utf8_codepoint <= '9') {
1869           /* Parse a numerical argument. */
1870           if (!(ansi_parser_state & ANSI_IN_ARG)) {
1871             /* We were not currently parsing a number, add a new one. */
1872             /* Check for that there are too many arguments. */
1873             if (handle->tty.wr.ansi_csi_argc >=
1874                 ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
1875               ansi_parser_state = ANSI_IGNORE;
1876               continue;
1877             }
1878             ansi_parser_state |= ANSI_IN_ARG;
1879             handle->tty.wr.ansi_csi_argc++;
1880             handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
1881                 (unsigned short) utf8_codepoint - '0';
1882             continue;
1883
1884           } else {
1885             /* We were already parsing a number. Parse next digit. */
1886             uint32_t value = 10 *
1887                 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1];
1888
1889             /* Check for overflow. */
1890             if (value > UINT16_MAX) {
1891               ansi_parser_state = ANSI_IGNORE;
1892               continue;
1893             }
1894
1895             handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
1896                 (unsigned short) value + (utf8_codepoint - '0');
1897             continue;
1898           }
1899
1900         } else if (utf8_codepoint == ';') {
1901           /* Denotes the end of an argument. */
1902           if (ansi_parser_state & ANSI_IN_ARG) {
1903             ansi_parser_state &= ~ANSI_IN_ARG;
1904             continue;
1905
1906           } else {
1907             /* If ANSI_IN_ARG is not set, add another argument and default
1908              * it to 0. */
1909
1910             /* Check for too many arguments */
1911             if (handle->tty.wr.ansi_csi_argc >=
1912
1913                 ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
1914               ansi_parser_state = ANSI_IGNORE;
1915               continue;
1916             }
1917
1918             handle->tty.wr.ansi_csi_argc++;
1919             handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0;
1920             continue;
1921           }
1922
1923         } else if (utf8_codepoint == '?' &&
1924                    !(ansi_parser_state & ANSI_IN_ARG) &&
1925                    !(ansi_parser_state & ANSI_EXTENSION) &&
1926                    handle->tty.wr.ansi_csi_argc == 0) {
1927           /* Pass through '?' if it is the first character after CSI */
1928           /* This is an extension character from the VT100 codeset */
1929           /* that is supported and used by most ANSI terminals today. */
1930           ansi_parser_state |= ANSI_EXTENSION;
1931           continue;
1932
1933         } else if (utf8_codepoint == ' ' &&
1934                    !(ansi_parser_state & ANSI_EXTENSION)) {
1935           /* We expect a command byte to follow after this space. The only
1936            * command that we current support is 'set cursor style'. */
1937           ansi_parser_state = ANSI_DECSCUSR;
1938           continue;
1939
1940         } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
1941           /* Command byte */
1942           if (ansi_parser_state & ANSI_EXTENSION) {
1943             /* Sequence is `ESC [ ? args command`. */
1944             switch (utf8_codepoint) {
1945               case 'l':
1946                 /* Hide the cursor */
1947                 if (handle->tty.wr.ansi_csi_argc == 1 &&
1948                     handle->tty.wr.ansi_csi_argv[0] == 25) {
1949                   FLUSH_TEXT();
1950                   uv_tty_set_cursor_visibility(handle, 0, error);
1951                 }
1952                 break;
1953
1954               case 'h':
1955                 /* Show the cursor */
1956                 if (handle->tty.wr.ansi_csi_argc == 1 &&
1957                     handle->tty.wr.ansi_csi_argv[0] == 25) {
1958                   FLUSH_TEXT();
1959                   uv_tty_set_cursor_visibility(handle, 1, error);
1960                 }
1961                 break;
1962             }
1963
1964           } else {
1965             /* Sequence is `ESC [ args command`. */
1966             int x, y, d;
1967             switch (utf8_codepoint) {
1968               case 'A':
1969                 /* cursor up */
1970                 FLUSH_TEXT();
1971                 y = -(handle->tty.wr.ansi_csi_argc
1972                   ? handle->tty.wr.ansi_csi_argv[0] : 1);
1973                 uv_tty_move_caret(handle, 0, 1, y, 1, error);
1974                 break;
1975
1976               case 'B':
1977                 /* cursor down */
1978                 FLUSH_TEXT();
1979                 y = handle->tty.wr.ansi_csi_argc
1980                   ? handle->tty.wr.ansi_csi_argv[0] : 1;
1981                 uv_tty_move_caret(handle, 0, 1, y, 1, error);
1982                 break;
1983
1984               case 'C':
1985                 /* cursor forward */
1986                 FLUSH_TEXT();
1987                 x = handle->tty.wr.ansi_csi_argc
1988                   ? handle->tty.wr.ansi_csi_argv[0] : 1;
1989                 uv_tty_move_caret(handle, x, 1, 0, 1, error);
1990                 break;
1991
1992               case 'D':
1993                 /* cursor back */
1994                 FLUSH_TEXT();
1995                 x = -(handle->tty.wr.ansi_csi_argc
1996                   ? handle->tty.wr.ansi_csi_argv[0] : 1);
1997                 uv_tty_move_caret(handle, x, 1, 0, 1, error);
1998                 break;
1999
2000               case 'E':
2001                 /* cursor next line */
2002                 FLUSH_TEXT();
2003                 y = handle->tty.wr.ansi_csi_argc
2004                   ? handle->tty.wr.ansi_csi_argv[0] : 1;
2005                 uv_tty_move_caret(handle, 0, 0, y, 1, error);
2006                 break;
2007
2008               case 'F':
2009                 /* cursor previous line */
2010                 FLUSH_TEXT();
2011                 y = -(handle->tty.wr.ansi_csi_argc
2012                   ? handle->tty.wr.ansi_csi_argv[0] : 1);
2013                 uv_tty_move_caret(handle, 0, 0, y, 1, error);
2014                 break;
2015
2016               case 'G':
2017                 /* cursor horizontal move absolute */
2018                 FLUSH_TEXT();
2019                 x = (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                 uv_tty_move_caret(handle, x, 0, 0, 1, error);
2023                 break;
2024
2025               case 'H':
2026               case 'f':
2027                 /* cursor move absolute */
2028                 FLUSH_TEXT();
2029                 y = (handle->tty.wr.ansi_csi_argc >= 1 &&
2030                      handle->tty.wr.ansi_csi_argv[0])
2031                   ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0;
2032                 x = (handle->tty.wr.ansi_csi_argc >= 2 &&
2033                      handle->tty.wr.ansi_csi_argv[1])
2034                   ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0;
2035                 uv_tty_move_caret(handle, x, 0, y, 0, error);
2036                 break;
2037
2038               case 'J':
2039                 /* Erase screen */
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, 1, error);
2045                 }
2046                 break;
2047
2048               case 'K':
2049                 /* Erase line */
2050                 FLUSH_TEXT();
2051                 d = handle->tty.wr.ansi_csi_argc
2052                   ? handle->tty.wr.ansi_csi_argv[0] : 0;
2053                 if (d >= 0 && d <= 2) {
2054                   uv_tty_clear(handle, d, 0, error);
2055                 }
2056                 break;
2057
2058               case 'm':
2059                 /* Set style */
2060                 FLUSH_TEXT();
2061                 uv_tty_set_style(handle, error);
2062                 break;
2063
2064               case 's':
2065                 /* Save the cursor position. */
2066                 FLUSH_TEXT();
2067                 uv_tty_save_state(handle, 0, error);
2068                 break;
2069
2070               case 'u':
2071                 /* Restore the cursor position */
2072                 FLUSH_TEXT();
2073                 uv_tty_restore_state(handle, 0, error);
2074                 break;
2075             }
2076           }
2077
2078           /* Sequence ended - go back to normal state. */
2079           ansi_parser_state = ANSI_NORMAL;
2080           continue;
2081
2082         } else {
2083           /* We don't support commands that use private mode characters or
2084            * intermediaries. Ignore the rest of the sequence. */
2085           ansi_parser_state = ANSI_IGNORE;
2086           continue;
2087         }
2088
2089       } else if (ansi_parser_state & ANSI_ST_CONTROL) {
2090         /* Unsupported control code.
2091          * Ignore everything until we see `BEL` or `ESC \`. */
2092         if (ansi_parser_state & ANSI_IN_STRING) {
2093           if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) {
2094             if (utf8_codepoint == '"') {
2095               ansi_parser_state &= ~ANSI_IN_STRING;
2096             } else if (utf8_codepoint == '\\') {
2097               ansi_parser_state |= ANSI_BACKSLASH_SEEN;
2098             }
2099           } else {
2100             ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
2101           }
2102         } else {
2103           if (utf8_codepoint == '\007' || (utf8_codepoint == '\\' &&
2104               (ansi_parser_state & ANSI_ESCAPE_SEEN))) {
2105             /* End of sequence */
2106             ansi_parser_state = ANSI_NORMAL;
2107           } else if (utf8_codepoint == '\033') {
2108             /* Escape character */
2109             ansi_parser_state |= ANSI_ESCAPE_SEEN;
2110           } else if (utf8_codepoint == '"') {
2111              /* String starting */
2112             ansi_parser_state |= ANSI_IN_STRING;
2113             ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
2114             ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
2115           } else {
2116             ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
2117           }
2118         }
2119         continue;
2120       } else {
2121         /* Inconsistent state */
2122         abort();
2123       }
2124
2125       if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) {
2126         /* EOL conversion - emit \r\n when we see \n. */
2127
2128         if (utf8_codepoint == 0x0a && previous_eol != 0x0d) {
2129           /* \n was not preceded by \r; print \r\n. */
2130           ENSURE_BUFFER_SPACE(2);
2131           utf16_buf[utf16_buf_used++] = L'\r';
2132           utf16_buf[utf16_buf_used++] = L'\n';
2133         } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) {
2134           /* \n was followed by \r; do not print the \r, since the source was
2135            * either \r\n\r (so the second \r is redundant) or was \n\r (so the
2136            * \n was processed by the last case and an \r automatically
2137            * inserted). */
2138         } else {
2139           /* \r without \n; print \r as-is. */
2140           ENSURE_BUFFER_SPACE(1);
2141           utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
2142         }
2143
2144         previous_eol = (char) utf8_codepoint;
2145
2146       } else if (utf8_codepoint <= 0xffff) {
2147         /* Encode character into utf-16 buffer. */
2148         ENSURE_BUFFER_SPACE(1);
2149         utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
2150         previous_eol = 0;
2151       } else {
2152         ENSURE_BUFFER_SPACE(2);
2153         utf8_codepoint -= 0x10000;
2154         utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint / 0x400 + 0xD800);
2155         utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint % 0x400 + 0xDC00);
2156         previous_eol = 0;
2157       }
2158     }
2159   }
2160
2161   /* Flush remaining characters */
2162   FLUSH_TEXT();
2163
2164   /* Copy cached values back to struct. */
2165   handle->tty.wr.utf8_bytes_left = utf8_bytes_left;
2166   handle->tty.wr.utf8_codepoint = utf8_codepoint;
2167   handle->tty.wr.previous_eol = previous_eol;
2168   handle->tty.wr.ansi_parser_state = ansi_parser_state;
2169
2170   uv_sem_post(&uv_tty_output_lock);
2171
2172   if (*error == STATUS_SUCCESS) {
2173     return 0;
2174   } else {
2175     return -1;
2176   }
2177
2178 #undef FLUSH_TEXT
2179 }
2180
2181
2182 int uv_tty_write(uv_loop_t* loop,
2183                  uv_write_t* req,
2184                  uv_tty_t* handle,
2185                  const uv_buf_t bufs[],
2186                  unsigned int nbufs,
2187                  uv_write_cb cb) {
2188   DWORD error;
2189
2190   UV_REQ_INIT(req, UV_WRITE);
2191   req->handle = (uv_stream_t*) handle;
2192   req->cb = cb;
2193
2194   handle->reqs_pending++;
2195   handle->stream.conn.write_reqs_pending++;
2196   REGISTER_HANDLE_REQ(loop, handle, req);
2197
2198   req->u.io.queued_bytes = 0;
2199
2200   if (!uv_tty_write_bufs(handle, bufs, nbufs, &error)) {
2201     SET_REQ_SUCCESS(req);
2202   } else {
2203     SET_REQ_ERROR(req, error);
2204   }
2205
2206   uv_insert_pending_req(loop, (uv_req_t*) req);
2207
2208   return 0;
2209 }
2210
2211
2212 int uv__tty_try_write(uv_tty_t* handle,
2213                       const uv_buf_t bufs[],
2214                       unsigned int nbufs) {
2215   DWORD error;
2216
2217   if (handle->stream.conn.write_reqs_pending > 0)
2218     return UV_EAGAIN;
2219
2220   if (uv_tty_write_bufs(handle, bufs, nbufs, &error))
2221     return uv_translate_sys_error(error);
2222
2223   return uv__count_bufs(bufs, nbufs);
2224 }
2225
2226
2227 void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
2228   uv_write_t* req) {
2229   int err;
2230
2231   handle->write_queue_size -= req->u.io.queued_bytes;
2232   UNREGISTER_HANDLE_REQ(loop, handle, req);
2233
2234   if (req->cb) {
2235     err = GET_REQ_ERROR(req);
2236     req->cb(req, uv_translate_sys_error(err));
2237   }
2238
2239   handle->stream.conn.write_reqs_pending--;
2240   if (handle->stream.conn.shutdown_req != NULL &&
2241       handle->stream.conn.write_reqs_pending == 0) {
2242     uv_want_endgame(loop, (uv_handle_t*)handle);
2243   }
2244
2245   DECREASE_PENDING_REQ_COUNT(handle);
2246 }
2247
2248
2249 void uv_tty_close(uv_tty_t* handle) {
2250   assert(handle->u.fd == -1 || handle->u.fd > 2);
2251   if (handle->flags & UV_HANDLE_READING)
2252     uv_tty_read_stop(handle);
2253
2254   if (handle->u.fd == -1)
2255     CloseHandle(handle->handle);
2256   else
2257     close(handle->u.fd);
2258
2259   handle->u.fd = -1;
2260   handle->handle = INVALID_HANDLE_VALUE;
2261   handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
2262   uv__handle_closing(handle);
2263
2264   if (handle->reqs_pending == 0) {
2265     uv_want_endgame(handle->loop, (uv_handle_t*) handle);
2266   }
2267 }
2268
2269
2270 void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
2271   if (!(handle->flags & UV_HANDLE_TTY_READABLE) &&
2272       handle->stream.conn.shutdown_req != NULL &&
2273       handle->stream.conn.write_reqs_pending == 0) {
2274     UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req);
2275
2276     /* TTY shutdown is really just a no-op */
2277     if (handle->stream.conn.shutdown_req->cb) {
2278       if (handle->flags & UV_HANDLE_CLOSING) {
2279         handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED);
2280       } else {
2281         handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0);
2282       }
2283     }
2284
2285     handle->stream.conn.shutdown_req = NULL;
2286
2287     DECREASE_PENDING_REQ_COUNT(handle);
2288     return;
2289   }
2290
2291   if (handle->flags & UV_HANDLE_CLOSING &&
2292       handle->reqs_pending == 0) {
2293     /* The wait handle used for raw reading should be unregistered when the
2294      * wait callback runs. */
2295     assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
2296            handle->tty.rd.read_raw_wait == NULL);
2297
2298     assert(!(handle->flags & UV_HANDLE_CLOSED));
2299     uv__handle_close(handle);
2300   }
2301 }
2302
2303
2304 /*
2305  * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
2306  * TODO: find a way to remove it
2307  */
2308 void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
2309     uv_req_t* raw_req) {
2310   abort();
2311 }
2312
2313
2314 /*
2315  * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
2316  * TODO: find a way to remove it
2317  */
2318 void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
2319     uv_connect_t* req) {
2320   abort();
2321 }
2322
2323
2324 int uv_tty_reset_mode(void) {
2325   /* Not necessary to do anything. */
2326   return 0;
2327 }
2328
2329 /* Determine whether or not this version of windows supports
2330  * proper ANSI color codes. Should be supported as of windows
2331  * 10 version 1511, build number 10.0.10586.
2332  */
2333 static void uv__determine_vterm_state(HANDLE handle) {
2334   DWORD dwMode = 0;
2335
2336   uv__need_check_vterm_state = FALSE;
2337   if (!GetConsoleMode(handle, &dwMode)) {
2338     return;
2339   }
2340
2341   dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
2342   if (!SetConsoleMode(handle, dwMode)) {
2343     return;
2344   }
2345
2346   uv__vterm_state = UV_TTY_SUPPORTED;
2347 }
2348
2349 static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
2350   NTSTATUS status;
2351   ULONG_PTR conhost_pid;
2352   MSG msg;
2353
2354   if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL)
2355     return 0;
2356
2357   status = pNtQueryInformationProcess(GetCurrentProcess(),
2358                                       ProcessConsoleHostProcess,
2359                                       &conhost_pid,
2360                                       sizeof(conhost_pid),
2361                                       NULL);
2362
2363   if (!NT_SUCCESS(status)) {
2364     /* We couldn't retrieve our console host process, probably because this
2365      * is a 32-bit process running on 64-bit Windows. Fall back to receiving
2366      * console events from the input stream only. */
2367     return 0;
2368   }
2369
2370   /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */
2371   conhost_pid &= ~(ULONG_PTR)0x3;
2372
2373   uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL);
2374   if (uv__tty_console_resized == NULL)
2375     return 0;
2376   if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread,
2377                         NULL,
2378                         WT_EXECUTELONGFUNCTION) == 0)
2379     return 0;
2380
2381   if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT,
2382                         EVENT_CONSOLE_LAYOUT,
2383                         NULL,
2384                         uv__tty_console_resize_event,
2385                         (DWORD)conhost_pid,
2386                         0,
2387                         WINEVENT_OUTOFCONTEXT))
2388     return 0;
2389
2390   while (GetMessage(&msg, NULL, 0, 0)) {
2391     TranslateMessage(&msg);
2392     DispatchMessage(&msg);
2393   }
2394   return 0;
2395 }
2396
2397 static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
2398                                                   DWORD event,
2399                                                   HWND hwnd,
2400                                                   LONG idObject,
2401                                                   LONG idChild,
2402                                                   DWORD dwEventThread,
2403                                                   DWORD dwmsEventTime) {
2404   SetEvent(uv__tty_console_resized);
2405 }
2406
2407 static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
2408   for (;;) {
2409     /* Make sure to not overwhelm the system with resize events */
2410     Sleep(33);
2411     WaitForSingleObject(uv__tty_console_resized, INFINITE);
2412     uv__tty_console_signal_resize();
2413     ResetEvent(uv__tty_console_resized);
2414   }
2415   return 0;
2416 }
2417
2418 static void uv__tty_console_signal_resize(void) {
2419   CONSOLE_SCREEN_BUFFER_INFO sb_info;
2420   int width, height;
2421
2422   if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
2423     return;
2424
2425   width = sb_info.dwSize.X;
2426   height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
2427
2428   uv_mutex_lock(&uv__tty_console_resize_mutex);
2429   assert(uv__tty_console_width != -1 && uv__tty_console_height != -1);
2430   if (width != uv__tty_console_width || height != uv__tty_console_height) {
2431     uv__tty_console_width = width;
2432     uv__tty_console_height = height;
2433     uv_mutex_unlock(&uv__tty_console_resize_mutex);
2434     uv__signal_dispatch(SIGWINCH);
2435   } else {
2436     uv_mutex_unlock(&uv__tty_console_resize_mutex);
2437   }
2438 }
2439
2440 void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) {
2441   uv_sem_wait(&uv_tty_output_lock);
2442   uv__need_check_vterm_state = FALSE;
2443   uv__vterm_state = state;
2444   uv_sem_post(&uv_tty_output_lock);
2445 }
2446
2447 int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) {
2448   uv_sem_wait(&uv_tty_output_lock);
2449   *state = uv__vterm_state;
2450   uv_sem_post(&uv_tty_output_lock);
2451   return 0;
2452 }