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