comparison third_party/libuv/src/win/tty.c @ 160:948de3f54cea

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