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