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