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 } 2461