1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
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
27 #if defined(_MSC_VER) && _MSC_VER < 1600
28 # include "uv/stdint-msvc2008.h"
33 #ifndef COMMON_LVB_REVERSE_VIDEO
34 # define COMMON_LVB_REVERSE_VIDEO 0x4000
39 #include "handle-inl.h"
40 #include "stream-inl.h"
44 # define InterlockedOr _InterlockedOr
47 #define UNICODE_REPLACEMENT_CHARACTER (0xfffd)
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
60 #define MAX_INPUT_BUFFER_LENGTH 8192
61 #define MAX_CONSOLE_CHAR 8192
63 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
64 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
67 #define CURSOR_SIZE_SMALL 25
68 #define CURSOR_SIZE_LARGE 100
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);
78 static const uv_buf_t uv_null_buf_ = { 0, NULL };
80 enum uv__read_console_status_e {
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;
93 * The console virtual window.
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.
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.
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.
114 * Since all tty i/o happens on the same console, this window is shared
115 * between all stdout/stderr handles.
118 static int uv_tty_virtual_offset = -1;
119 static int uv_tty_virtual_height = -1;
120 static int uv_tty_virtual_width = -1;
122 /* The console window size
123 * We keep this separate from uv_tty_virtual_*. We use those values to only
124 * handle signalling SIGWINCH
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;
133 static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param);
134 static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
140 DWORD dwmsEventTime);
141 static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param);
142 static void uv__tty_console_signal_resize(void);
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;
150 static WORD uv_tty_default_text_attributes =
151 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
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;
159 static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info;
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);
166 void uv_console_init(void) {
167 if (uv_sem_init(&uv_tty_output_lock, 1))
169 uv__tty_console_handle = CreateFileW(L"CONOUT$",
170 GENERIC_READ | GENERIC_WRITE,
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,
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;
190 int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
192 DWORD NumberOfEvents;
194 CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info;
195 CONSOLE_CURSOR_INFO cursor_info;
199 handle = (HANDLE) uv__get_osfhandle(fd);
200 if (handle == INVALID_HANDLE_VALUE)
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
210 if (!DuplicateHandle(INVALID_HANDLE_VALUE,
212 INVALID_HANDLE_VALUE,
216 DUPLICATE_SAME_ACCESS))
217 return uv_translate_sys_error(GetLastError());
221 readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents);
223 /* Obtain the screen buffer info with the output handle. */
224 if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) {
225 return uv_translate_sys_error(GetLastError());
228 /* Obtain the cursor info with the output handle. */
229 if (!GetConsoleCursorInfo(handle, &cursor_info)) {
230 return uv_translate_sys_error(GetLastError());
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);
237 if (uv__need_check_vterm_state)
238 uv__determine_vterm_state(handle);
240 /* Remember the original console text attributes and cursor info. */
241 uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info);
243 uv_tty_update_virtual_window(&screen_buffer_info);
245 uv_sem_post(&uv_tty_output_lock);
249 uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY);
250 uv_connection_init((uv_stream_t*) tty);
252 tty->handle = handle;
254 tty->reqs_pending = 0;
255 tty->flags |= UV_HANDLE_BOUND;
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;
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);
271 /* TTY output specific fields. */
272 tty->flags |= UV_HANDLE_WRITABLE;
274 /* Init utf8-to-utf16 conversion state. */
275 tty->tty.wr.utf8_bytes_left = 0;
276 tty->tty.wr.utf8_codepoint = 0;
278 /* Initialize eol conversion state */
279 tty->tty.wr.previous_eol = 0;
281 /* Init ANSI parser state. */
282 tty->tty.wr.ansi_parser_state = ANSI_NORMAL;
289 /* Set the default console text attributes based on how the console was
290 * configured when libuv started.
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;
297 /* Only do this once.
298 Assumption: Caller has acquired uv_tty_output_lock. */
302 /* Save raw win32 attributes. */
303 uv_tty_default_text_attributes = screen_buffer_info->wAttributes;
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;
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;
316 if (uv_tty_default_text_attributes & FOREGROUND_RED)
317 uv_tty_default_fg_color |= 1;
319 if (uv_tty_default_text_attributes & FOREGROUND_GREEN)
320 uv_tty_default_fg_color |= 2;
322 if (uv_tty_default_text_attributes & FOREGROUND_BLUE)
323 uv_tty_default_fg_color |= 4;
325 if (uv_tty_default_text_attributes & BACKGROUND_RED)
326 uv_tty_default_bg_color |= 1;
328 if (uv_tty_default_text_attributes & BACKGROUND_GREEN)
329 uv_tty_default_bg_color |= 2;
331 if (uv_tty_default_text_attributes & BACKGROUND_BLUE)
332 uv_tty_default_bg_color |= 4;
334 if (uv_tty_default_text_attributes & FOREGROUND_INTENSITY)
335 uv_tty_default_fg_bright = 1;
337 if (uv_tty_default_text_attributes & BACKGROUND_INTENSITY)
338 uv_tty_default_bg_bright = 1;
340 if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO)
341 uv_tty_default_inverse = 1;
343 /* Save the cursor size and the cursor state. */
344 uv_tty_default_cursor_info = *cursor_info;
350 int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
352 unsigned char was_reading;
353 uv_alloc_cb alloc_cb;
357 if (!(tty->flags & UV_HANDLE_TTY_READABLE)) {
361 if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
366 case UV_TTY_MODE_NORMAL:
367 flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
369 case UV_TTY_MODE_RAW:
370 flags = ENABLE_WINDOW_INPUT;
378 /* If currently reading, stop, and restart reading. */
379 if (tty->flags & UV_HANDLE_READING) {
381 alloc_cb = tty->alloc_cb;
382 read_cb = tty->read_cb;
383 err = uv_tty_read_stop(tty);
385 return uv_translate_sys_error(err);
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);
399 uv_sem_post(&uv_tty_output_lock);
402 tty->flags &= ~UV_HANDLE_TTY_RAW;
403 tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
405 /* If we just stopped reading, restart. */
407 err = uv_tty_read_start(tty, alloc_cb, read_cb);
409 return uv_translate_sys_error(err);
417 int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
418 CONSOLE_SCREEN_BUFFER_INFO info;
420 if (!GetConsoleScreenBufferInfo(tty->handle, &info)) {
421 return uv_translate_sys_error(GetLastError());
424 uv_sem_wait(&uv_tty_output_lock);
425 uv_tty_update_virtual_window(&info);
426 uv_sem_post(&uv_tty_output_lock);
428 *width = uv_tty_virtual_width;
429 *height = uv_tty_virtual_height;
435 static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) {
443 req = (uv_req_t*) data;
444 handle = (uv_tty_t*) req->data;
447 UnregisterWait(handle->tty.rd.read_raw_wait);
448 handle->tty.rd.read_raw_wait = NULL;
450 SET_REQ_SUCCESS(req);
451 POST_COMPLETION_FOR_REQ(loop, req);
455 static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) {
459 assert(handle->flags & UV_HANDLE_READING);
460 assert(!(handle->flags & UV_HANDLE_READ_PENDING));
462 assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
464 handle->tty.rd.read_line_buffer = uv_null_buf_;
466 req = &handle->read_req;
467 memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
469 r = RegisterWaitForSingleObject(&handle->tty.rd.read_raw_wait,
471 uv_tty_post_raw_read,
474 WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE);
476 handle->tty.rd.read_raw_wait = NULL;
477 SET_REQ_ERROR(req, GetLastError());
478 uv_insert_pending_req(loop, (uv_req_t*)req);
481 handle->flags |= UV_HANDLE_READ_PENDING;
482 handle->reqs_pending++;
486 static DWORD CALLBACK uv_tty_line_read_thread(void* data) {
490 DWORD bytes, read_bytes;
491 WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3];
492 DWORD chars, read_chars;
495 BOOL read_console_success;
499 req = (uv_req_t*) data;
500 handle = (uv_tty_t*) req->data;
503 assert(handle->tty.rd.read_line_buffer.base != NULL);
504 assert(handle->tty.rd.read_line_buffer.len > 0);
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;
510 bytes = MAX_INPUT_BUFFER_LENGTH;
513 /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8
514 * codeunits to encode. */
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);
526 read_console_success = ReadConsoleW(handle->handle,
532 if (read_console_success) {
533 read_bytes = WideCharToMultiByte(CP_UTF8,
537 handle->tty.rd.read_line_buffer.base,
541 SET_REQ_SUCCESS(req);
542 req->u.io.overlapped.InternalHigh = read_bytes;
544 SET_REQ_ERROR(req, GetLastError());
547 status = InterlockedExchange(&uv__read_console_status, COMPLETED);
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,
559 FILE_ATTRIBUTE_NORMAL,
561 if (active_screen_buffer != INVALID_HANDLE_VALUE) {
562 pos = uv__saved_screen_state.dwCursorPosition;
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
568 if (pos.Y == uv__saved_screen_state.dwSize.Y - 1)
571 SetConsoleCursorPosition(active_screen_buffer, pos);
572 CloseHandle(active_screen_buffer);
575 uv_sem_post(&uv_tty_output_lock);
577 POST_COMPLETION_FOR_REQ(loop, req);
582 static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
586 assert(handle->flags & UV_HANDLE_READING);
587 assert(!(handle->flags & UV_HANDLE_READ_PENDING));
588 assert(handle->handle && handle->handle != INVALID_HANDLE_VALUE);
590 req = &handle->read_req;
591 memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
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,
599 &handle->tty.rd.read_line_buffer);
602 assert(handle->tty.rd.read_line_buffer.base != NULL);
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
607 uv__restore_screen_state = FALSE;
608 uv__read_console_status = NOT_STARTED;
609 r = QueueUserWorkItem(uv_tty_line_read_thread,
611 WT_EXECUTELONGFUNCTION);
613 SET_REQ_ERROR(req, GetLastError());
614 uv_insert_pending_req(loop, (uv_req_t*)req);
617 handle->flags |= UV_HANDLE_READ_PENDING;
618 handle->reqs_pending++;
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);
626 uv_tty_queue_read_line(loop, handle);
631 static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl,
633 #define VK_CASE(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \
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; \
642 *len = sizeof ctrl_str; \
643 return "\033" ctrl_str; \
645 *len = sizeof normal_str; \
646 return "\033" normal_str; \
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@" )
697 void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
699 /* Shortcut for handle->tty.rd.last_input_record.Event.KeyEvent. */
700 #define KEV handle->tty.rd.last_input_record.Event.KeyEvent
702 DWORD records_left, records_read;
706 assert(handle->type == UV_TTY);
707 assert(handle->flags & UV_HANDLE_TTY_READABLE);
708 handle->flags &= ~UV_HANDLE_READ_PENDING;
710 if (!(handle->flags & UV_HANDLE_READING) ||
711 !(handle->flags & UV_HANDLE_TTY_RAW)) {
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)),
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()),
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. */
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,
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()),
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();
764 /* Ignore other events that are not key events. */
765 if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) {
769 /* Ignore keyup events, unless the left alt key was held and a valid
770 * unicode character was emitted. */
772 (KEV.wVirtualKeyCode != VK_MENU ||
773 KEV.uChar.UnicodeChar == 0)) {
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.
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)) {
805 if (KEV.uChar.UnicodeChar != 0) {
806 int prefix_len, char_len;
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;
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';
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,
837 &handle->tty.rd.last_key[prefix_len],
838 sizeof handle->tty.rd.last_key,
842 /* Single UTF-16 character */
843 char_len = WideCharToMultiByte(CP_UTF8,
845 &KEV.uChar.UnicodeChar,
847 &handle->tty.rd.last_key[prefix_len],
848 sizeof handle->tty.rd.last_key,
853 /* Whatever happened, the last character wasn't a high surrogate. */
854 handle->tty.rd.last_utf16_high_surrogate = 0;
856 /* If the utf16 character(s) couldn't be converted something must be
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()),
867 handle->tty.rd.last_key_len = (unsigned char) (prefix_len + char_len);
868 handle->tty.rd.last_key_offset = 0;
872 /* Function key pressed */
874 size_t prefix_len, vt100_len;
876 vt100 = get_vt100_fn_key(KEV.wVirtualKeyCode,
877 !!(KEV.dwControlKeyState & SHIFT_PRESSED),
878 !!(KEV.dwControlKeyState & (
880 RIGHT_CTRL_PRESSED)),
883 /* If we were unable to map to a vt100 sequence, just ignore. */
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';
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);
900 handle->tty.rd.last_key_len = (unsigned char) (prefix_len + vt100_len);
901 handle->tty.rd.last_key_offset = 0;
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 */
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);
915 assert(buf.base != NULL);
918 buf.base[buf_used++] = handle->tty.rd.last_key[handle->tty.rd.last_key_offset++];
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);
930 /* Apply dwRepeat from the last input record. */
931 if (--KEV.wRepeatCount > 0) {
932 handle->tty.rd.last_key_offset = 0;
936 handle->tty.rd.last_key_len = 0;
941 /* Send the buffer back to the user */
943 handle->read_cb((uv_stream_t*) handle, buf_used, &buf);
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);
953 DECREASE_PENDING_REQ_COUNT(handle);
960 void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle,
964 assert(handle->type == UV_TTY);
965 assert(handle->flags & UV_HANDLE_TTY_READABLE);
967 buf = handle->tty.rd.read_line_buffer;
969 handle->flags &= ~UV_HANDLE_READ_PENDING;
970 handle->tty.rd.read_line_buffer = uv_null_buf_;
972 if (!REQ_SUCCESS(req)) {
973 /* Read was not successful */
974 if (handle->flags & UV_HANDLE_READING) {
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)),
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);
989 handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING;
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);
998 DECREASE_PENDING_REQ_COUNT(handle);
1002 void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle,
1004 assert(handle->type == UV_TTY);
1005 assert(handle->flags & UV_HANDLE_TTY_READABLE);
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);
1013 uv_process_tty_read_line_req(loop, handle, req);
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;
1022 if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
1023 return ERROR_INVALID_PARAMETER;
1026 handle->flags |= UV_HANDLE_READING;
1027 INCREASE_ACTIVE_COUNT(loop, handle);
1028 handle->read_cb = read_cb;
1029 handle->alloc_cb = alloc_cb;
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) {
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++;
1048 uv_tty_queue_read(loop, handle);
1054 int uv_tty_read_stop(uv_tty_t* handle) {
1055 INPUT_RECORD record;
1058 handle->flags &= ~UV_HANDLE_READING;
1059 DECREASE_ACTIVE_COUNT(handle->loop, handle);
1061 if (!(handle->flags & UV_HANDLE_READ_PENDING))
1064 if (handle->flags & UV_HANDLE_TTY_RAW) {
1065 /* Cancel raw read. Write some bullshit event to force the console wait to
1067 memset(&record, 0, sizeof record);
1068 record.EventType = FOCUS_EVENT;
1069 if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) {
1070 return GetLastError();
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);
1078 handle->flags |= UV_HANDLE_CANCELLATION_PENDING;
1084 static int uv__cancel_read_console(uv_tty_t* handle) {
1085 HANDLE active_screen_buffer = INVALID_HANDLE_VALUE;
1086 INPUT_RECORD record;
1091 assert(!(handle->flags & UV_HANDLE_CANCELLATION_PENDING));
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);
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,
1112 FILE_ATTRIBUTE_NORMAL,
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);
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();
1133 if (active_screen_buffer != INVALID_HANDLE_VALUE)
1134 CloseHandle(active_screen_buffer);
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;
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;
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;
1157 if (uv_tty_virtual_offset < 0) {
1158 uv_tty_virtual_offset = 0;
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) {
1168 uv_tty_update_virtual_window(info);
1170 /* Adjust y position */
1172 y = info->dwCursorPosition.Y + y;
1174 y = uv_tty_virtual_offset + y;
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;
1185 x = info->dwCursorPosition.X + x;
1190 } else if (x >= uv_tty_virtual_width) {
1191 x = uv_tty_virtual_width - 1;
1194 result.X = (unsigned short) x;
1195 result.Y = (unsigned short) y;
1200 static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length,
1204 if (*error != ERROR_SUCCESS) {
1208 if (!WriteConsoleW(handle->handle,
1213 *error = GetLastError();
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;
1226 if (*error != ERROR_SUCCESS) {
1231 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
1232 *error = GetLastError();
1235 pos = uv_tty_make_real_coord(handle, &info, x, x_relative, y, y_relative);
1237 if (!SetConsoleCursorPosition(handle->handle, pos)) {
1238 if (GetLastError() == ERROR_INVALID_PARAMETER) {
1239 /* The console may be resized - retry */
1242 *error = GetLastError();
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;
1257 if (*error != ERROR_SUCCESS) {
1261 /* Reset original text attributes. */
1262 if (!SetConsoleTextAttribute(handle->handle, char_attrs)) {
1263 *error = GetLastError();
1267 /* Move the cursor position to (0, 0). */
1268 if (!SetConsoleCursorPosition(handle->handle, origin)) {
1269 *error = GetLastError();
1273 /* Clear the screen buffer. */
1275 if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) {
1276 *error = GetLastError();
1280 count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y;
1282 if (!(FillConsoleOutputCharacterW(handle->handle,
1287 FillConsoleOutputAttribute(handle->handle,
1292 if (GetLastError() == ERROR_INVALID_PARAMETER) {
1293 /* The console may be resized - retry */
1296 *error = GetLastError();
1301 /* Move the virtual window up to the top. */
1302 uv_tty_virtual_offset = 0;
1303 uv_tty_update_virtual_window(&screen_buffer_info);
1305 /* Reset the cursor size and the cursor state. */
1306 if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) {
1307 *error = GetLastError();
1315 static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen,
1317 CONSOLE_SCREEN_BUFFER_INFO info;
1319 DWORD count, written;
1322 int x1r, x2r, y1r, y2r;
1324 if (*error != ERROR_SUCCESS) {
1329 /* Clear from current position */
1333 /* Clear from column 0 */
1339 /* Clear to current position */
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. */
1349 if (!entire_screen) {
1350 /* Stay on our own row */
1354 /* Apply columns direction to row */
1362 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
1363 *error = GetLastError();
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;
1372 if (!(FillConsoleOutputCharacterW(handle->handle,
1377 FillConsoleOutputAttribute(handle->handle,
1382 if (GetLastError() == ERROR_INVALID_PARAMETER) {
1383 /* The console may be resized - retry */
1386 *error = GetLastError();
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; \
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;
1407 CONSOLE_SCREEN_BUFFER_INFO info;
1409 char fg_color = -1, bg_color = -1;
1410 char fg_bright = -1, bg_bright = -1;
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;
1422 for (i = 0; i < argc; i++) {
1423 short arg = argv[i];
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;
1433 } else if (arg == 1) {
1434 /* Foreground bright on */
1437 } else if (arg == 2) {
1438 /* Both bright off */
1442 } else if (arg == 5) {
1443 /* Background bright on */
1446 } else if (arg == 7) {
1450 } else if (arg == 21 || arg == 22) {
1451 /* Foreground bright off */
1454 } else if (arg == 25) {
1455 /* Background bright off */
1458 } else if (arg == 27) {
1462 } else if (arg >= 30 && arg <= 37) {
1463 /* Set foreground color */
1464 fg_color = arg - 30;
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;
1471 } else if (arg >= 40 && arg <= 47) {
1472 /* Set background color */
1473 bg_color = arg - 40;
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;
1480 } else if (arg >= 90 && arg <= 97) {
1481 /* Set bold foreground color */
1483 fg_color = arg - 90;
1485 } else if (arg >= 100 && arg <= 107) {
1486 /* Set bold background color */
1488 bg_color = arg - 100;
1493 if (fg_color == -1 && bg_color == -1 && fg_bright == -1 &&
1494 bg_bright == -1 && inverse == -1) {
1495 /* Nothing changed */
1499 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
1500 *error = GetLastError();
1504 if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
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;
1515 if (fg_bright != -1) {
1517 info.wAttributes |= FOREGROUND_INTENSITY;
1519 info.wAttributes &= ~FOREGROUND_INTENSITY;
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;
1530 if (bg_bright != -1) {
1532 info.wAttributes |= BACKGROUND_INTENSITY;
1534 info.wAttributes &= ~BACKGROUND_INTENSITY;
1538 if (inverse != -1) {
1540 info.wAttributes |= COMMON_LVB_REVERSE_VIDEO;
1542 info.wAttributes &= ~COMMON_LVB_REVERSE_VIDEO;
1546 if ((info.wAttributes & COMMON_LVB_REVERSE_VIDEO) > 0) {
1550 if (!SetConsoleTextAttribute(handle->handle, info.wAttributes)) {
1551 *error = GetLastError();
1559 static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes,
1561 CONSOLE_SCREEN_BUFFER_INFO info;
1563 if (*error != ERROR_SUCCESS) {
1567 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
1568 *error = GetLastError();
1572 uv_tty_update_virtual_window(&info);
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;
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;
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;
1593 if (*error != ERROR_SUCCESS) {
1597 if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) {
1598 if (uv_tty_move_caret(handle,
1599 handle->tty.wr.saved_position.X,
1601 handle->tty.wr.saved_position.Y,
1608 if (restore_attributes &&
1609 (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) {
1610 if (!GetConsoleScreenBufferInfo(handle->handle, &info)) {
1611 *error = GetLastError();
1615 new_attributes = info.wAttributes;
1616 new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
1617 new_attributes |= handle->tty.wr.saved_attributes;
1619 if (!SetConsoleTextAttribute(handle->handle, new_attributes)) {
1620 *error = GetLastError();
1628 static int uv_tty_set_cursor_visibility(uv_tty_t* handle,
1631 CONSOLE_CURSOR_INFO cursor_info;
1633 if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
1634 *error = GetLastError();
1638 cursor_info.bVisible = visible;
1640 if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
1641 *error = GetLastError();
1648 static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) {
1649 CONSOLE_CURSOR_INFO cursor_info;
1651 if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) {
1652 *error = GetLastError();
1657 cursor_info.dwSize = uv_tty_default_cursor_info.dwSize;
1658 } else if (style <= 2) {
1659 cursor_info.dwSize = CURSOR_SIZE_LARGE;
1661 cursor_info.dwSize = CURSOR_SIZE_SMALL;
1664 if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) {
1665 *error = GetLastError();
1673 static int uv_tty_write_bufs(uv_tty_t* handle,
1674 const uv_buf_t bufs[],
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;
1683 #define FLUSH_TEXT() \
1685 if (utf16_buf_used > 0) { \
1686 uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \
1687 utf16_buf_used = 0; \
1691 #define ENSURE_BUFFER_SPACE(wchars_needed) \
1692 if (wchars_needed > ARRAY_SIZE(utf16_buf) - utf16_buf_used) { \
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;
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;
1706 uv_sem_wait(&uv_tty_output_lock);
1708 for (i = 0; i < nbufs; i++) {
1709 uv_buf_t buf = bufs[i];
1712 for (j = 0; j < buf.len; j++) {
1713 unsigned char c = buf.base[j];
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 */
1726 first_zero_bit = (sizeof(int) * 8) - 1 - __builtin_clz(not_c);
1728 if (first_zero_bit == 7) {
1729 /* Ascii - pass right through */
1730 utf8_codepoint = (unsigned int) c;
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);
1738 /* Invalid continuation */
1739 utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
1743 /* 0xff -- invalid */
1744 utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
1747 } else if ((c & 0xc0) == 0x80) {
1748 /* Valid continuation of utf-8 multibyte sequence */
1750 utf8_codepoint <<= 6;
1751 utf8_codepoint |= ((unsigned int) c & 0x3f);
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
1762 /* Maybe we need to parse more bytes to find a character. */
1763 if (utf8_bytes_left != 0) {
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) {
1773 ansi_parser_state = ANSI_ESCAPE_SEEN;
1777 ansi_parser_state = ANSI_CSI;
1778 handle->tty.wr.ansi_csi_argc = 0;
1782 } else if (ansi_parser_state == ANSI_ESCAPE_SEEN) {
1783 switch (utf8_codepoint) {
1785 ansi_parser_state = ANSI_CSI;
1786 handle->tty.wr.ansi_csi_argc = 0;
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;
1799 /* Ignore double escape. */
1803 /* Full console reset. */
1805 uv_tty_reset(handle, error);
1806 ansi_parser_state = ANSI_NORMAL;
1810 /* Save the cursor position and text attributes. */
1812 uv_tty_save_state(handle, 1, error);
1813 ansi_parser_state = ANSI_NORMAL;
1817 /* Restore the cursor position and text attributes */
1819 uv_tty_restore_state(handle, 1, error);
1820 ansi_parser_state = ANSI_NORMAL;
1824 if (utf8_codepoint >= '@' && utf8_codepoint <= '_') {
1825 /* Single-char control. */
1826 ansi_parser_state = ANSI_NORMAL;
1829 /* Invalid - proceed as normal, */
1830 ansi_parser_state = ANSI_NORMAL;
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;
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 <= '~') {
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) {
1852 uv_tty_set_cursor_shape(handle, style, error);
1856 /* Sequence ended - go back to normal state. */
1857 ansi_parser_state = ANSI_NORMAL;
1860 /* Unexpected character, but sequence hasn't ended yet. Ignore the rest
1861 * of the sequence. */
1862 ansi_parser_state = ANSI_IGNORE;
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. */
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;
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';
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];
1889 /* Check for overflow. */
1890 if (value > UINT16_MAX) {
1891 ansi_parser_state = ANSI_IGNORE;
1895 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] =
1896 (unsigned short) value + (utf8_codepoint - '0');
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;
1907 /* If ANSI_IN_ARG is not set, add another argument and default
1910 /* Check for too many arguments */
1911 if (handle->tty.wr.ansi_csi_argc >=
1913 ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) {
1914 ansi_parser_state = ANSI_IGNORE;
1918 handle->tty.wr.ansi_csi_argc++;
1919 handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0;
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;
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;
1940 } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') {
1942 if (ansi_parser_state & ANSI_EXTENSION) {
1943 /* Sequence is `ESC [ ? args command`. */
1944 switch (utf8_codepoint) {
1946 /* Hide the cursor */
1947 if (handle->tty.wr.ansi_csi_argc == 1 &&
1948 handle->tty.wr.ansi_csi_argv[0] == 25) {
1950 uv_tty_set_cursor_visibility(handle, 0, error);
1955 /* Show the cursor */
1956 if (handle->tty.wr.ansi_csi_argc == 1 &&
1957 handle->tty.wr.ansi_csi_argv[0] == 25) {
1959 uv_tty_set_cursor_visibility(handle, 1, error);
1965 /* Sequence is `ESC [ args command`. */
1967 switch (utf8_codepoint) {
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);
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);
1985 /* cursor forward */
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);
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);
2001 /* cursor next line */
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);
2009 /* cursor previous line */
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);
2017 /* cursor horizontal move absolute */
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);
2027 /* cursor move absolute */
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);
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);
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);
2061 uv_tty_set_style(handle, error);
2065 /* Save the cursor position. */
2067 uv_tty_save_state(handle, 0, error);
2071 /* Restore the cursor position */
2073 uv_tty_restore_state(handle, 0, error);
2078 /* Sequence ended - go back to normal state. */
2079 ansi_parser_state = ANSI_NORMAL;
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;
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;
2100 ansi_parser_state &= ~ANSI_BACKSLASH_SEEN;
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;
2116 ansi_parser_state &= ~ANSI_ESCAPE_SEEN;
2121 /* Inconsistent state */
2125 if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) {
2126 /* EOL conversion - emit \r\n when we see \n. */
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
2139 /* \r without \n; print \r as-is. */
2140 ENSURE_BUFFER_SPACE(1);
2141 utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint;
2144 previous_eol = (char) utf8_codepoint;
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;
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);
2161 /* Flush remaining characters */
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;
2170 uv_sem_post(&uv_tty_output_lock);
2172 if (*error == STATUS_SUCCESS) {
2182 int uv_tty_write(uv_loop_t* loop,
2185 const uv_buf_t bufs[],
2190 UV_REQ_INIT(req, UV_WRITE);
2191 req->handle = (uv_stream_t*) handle;
2194 handle->reqs_pending++;
2195 handle->stream.conn.write_reqs_pending++;
2196 REGISTER_HANDLE_REQ(loop, handle, req);
2198 req->u.io.queued_bytes = 0;
2200 if (!uv_tty_write_bufs(handle, bufs, nbufs, &error)) {
2201 SET_REQ_SUCCESS(req);
2203 SET_REQ_ERROR(req, error);
2206 uv_insert_pending_req(loop, (uv_req_t*) req);
2212 int uv__tty_try_write(uv_tty_t* handle,
2213 const uv_buf_t bufs[],
2214 unsigned int nbufs) {
2217 if (handle->stream.conn.write_reqs_pending > 0)
2220 if (uv_tty_write_bufs(handle, bufs, nbufs, &error))
2221 return uv_translate_sys_error(error);
2223 return uv__count_bufs(bufs, nbufs);
2227 void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
2231 handle->write_queue_size -= req->u.io.queued_bytes;
2232 UNREGISTER_HANDLE_REQ(loop, handle, req);
2235 err = GET_REQ_ERROR(req);
2236 req->cb(req, uv_translate_sys_error(err));
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);
2245 DECREASE_PENDING_REQ_COUNT(handle);
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);
2254 if (handle->u.fd == -1)
2255 CloseHandle(handle->handle);
2257 close(handle->u.fd);
2260 handle->handle = INVALID_HANDLE_VALUE;
2261 handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
2262 uv__handle_closing(handle);
2264 if (handle->reqs_pending == 0) {
2265 uv_want_endgame(handle->loop, (uv_handle_t*) handle);
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);
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);
2281 handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0);
2285 handle->stream.conn.shutdown_req = NULL;
2287 DECREASE_PENDING_REQ_COUNT(handle);
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);
2298 assert(!(handle->flags & UV_HANDLE_CLOSED));
2299 uv__handle_close(handle);
2305 * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working
2306 * TODO: find a way to remove it
2308 void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
2309 uv_req_t* raw_req) {
2315 * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working
2316 * TODO: find a way to remove it
2318 void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
2319 uv_connect_t* req) {
2324 int uv_tty_reset_mode(void) {
2325 /* Not necessary to do anything. */
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.
2333 static void uv__determine_vterm_state(HANDLE handle) {
2336 uv__need_check_vterm_state = FALSE;
2337 if (!GetConsoleMode(handle, &dwMode)) {
2341 dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
2342 if (!SetConsoleMode(handle, dwMode)) {
2346 uv__vterm_state = UV_TTY_SUPPORTED;
2349 static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) {
2351 ULONG_PTR conhost_pid;
2354 if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL)
2357 status = pNtQueryInformationProcess(GetCurrentProcess(),
2358 ProcessConsoleHostProcess,
2360 sizeof(conhost_pid),
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. */
2370 /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */
2371 conhost_pid &= ~(ULONG_PTR)0x3;
2373 uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL);
2374 if (uv__tty_console_resized == NULL)
2376 if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread,
2378 WT_EXECUTELONGFUNCTION) == 0)
2381 if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT,
2382 EVENT_CONSOLE_LAYOUT,
2384 uv__tty_console_resize_event,
2387 WINEVENT_OUTOFCONTEXT))
2390 while (GetMessage(&msg, NULL, 0, 0)) {
2391 TranslateMessage(&msg);
2392 DispatchMessage(&msg);
2397 static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook,
2402 DWORD dwEventThread,
2403 DWORD dwmsEventTime) {
2404 SetEvent(uv__tty_console_resized);
2407 static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
2409 /* Make sure to not overwhelm the system with resize events */
2411 WaitForSingleObject(uv__tty_console_resized, INFINITE);
2412 uv__tty_console_signal_resize();
2413 ResetEvent(uv__tty_console_resized);
2418 static void uv__tty_console_signal_resize(void) {
2419 CONSOLE_SCREEN_BUFFER_INFO sb_info;
2422 if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
2425 width = sb_info.dwSize.X;
2426 height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
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);
2436 uv_mutex_unlock(&uv__tty_console_resize_mutex);
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);
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);