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