|
160
|
1 /* Copyright libuv project 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 #ifdef _WIN32
|
|
|
23
|
|
|
24 #include "uv.h"
|
|
|
25 #include "task.h"
|
|
|
26
|
|
|
27 #include <errno.h>
|
|
|
28 #include <io.h>
|
|
|
29 #include <string.h>
|
|
|
30 #include <windows.h>
|
|
|
31
|
|
|
32 #define ESC "\x1b"
|
|
|
33 #define EUR_UTF8 "\xe2\x82\xac"
|
|
|
34 #define EUR_UNICODE 0x20AC
|
|
|
35
|
|
|
36
|
|
|
37 const char* expect_str = NULL;
|
|
|
38 ssize_t expect_nread = 0;
|
|
|
39
|
|
|
40 static void dump_str(const char* str, ssize_t len) {
|
|
|
41 ssize_t i;
|
|
|
42 for (i = 0; i < len; i++) {
|
|
|
43 fprintf(stderr, "%#02x ", *(str + i));
|
|
|
44 }
|
|
|
45 }
|
|
|
46
|
|
|
47 static void print_err_msg(const char* expect, ssize_t expect_len,
|
|
|
48 const char* found, ssize_t found_len) {
|
|
|
49 fprintf(stderr, "expect ");
|
|
|
50 dump_str(expect, expect_len);
|
|
|
51 fprintf(stderr, ", but found ");
|
|
|
52 dump_str(found, found_len);
|
|
|
53 fprintf(stderr, "\n");
|
|
|
54 }
|
|
|
55
|
|
|
56 static void tty_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
|
|
|
57 buf->base = malloc(size);
|
|
|
58 ASSERT_NOT_NULL(buf->base);
|
|
|
59 buf->len = size;
|
|
|
60 }
|
|
|
61
|
|
|
62 static void tty_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) {
|
|
|
63 if (nread > 0) {
|
|
|
64 if (nread != expect_nread) {
|
|
|
65 fprintf(stderr, "expected nread %ld, but found %ld\n",
|
|
|
66 (long)expect_nread, (long)nread);
|
|
|
67 print_err_msg(expect_str, expect_nread, buf->base, nread);
|
|
|
68 ASSERT(FALSE);
|
|
|
69 }
|
|
|
70 if (strncmp(buf->base, expect_str, nread) != 0) {
|
|
|
71 print_err_msg(expect_str, expect_nread, buf->base, nread);
|
|
|
72 ASSERT(FALSE);
|
|
|
73 }
|
|
|
74 uv_close((uv_handle_t*) tty_in, NULL);
|
|
|
75 } else {
|
|
|
76 ASSERT_OK(nread);
|
|
|
77 }
|
|
|
78 }
|
|
|
79
|
|
|
80 static void make_key_event_records(WORD virt_key, DWORD ctr_key_state,
|
|
|
81 BOOL is_wsl, INPUT_RECORD* records) {
|
|
|
82 # define KEV(I) records[(I)].Event.KeyEvent
|
|
|
83 BYTE kb_state[256] = {0};
|
|
|
84 WCHAR buf[2];
|
|
|
85 int ret;
|
|
|
86
|
|
|
87 records[0].EventType = records[1].EventType = KEY_EVENT;
|
|
|
88 KEV(0).bKeyDown = TRUE;
|
|
|
89 KEV(1).bKeyDown = FALSE;
|
|
|
90 KEV(0).wVirtualKeyCode = KEV(1).wVirtualKeyCode = virt_key;
|
|
|
91 KEV(0).wRepeatCount = KEV(1).wRepeatCount = 1;
|
|
|
92 KEV(0).wVirtualScanCode = KEV(1).wVirtualScanCode =
|
|
|
93 MapVirtualKeyW(virt_key, MAPVK_VK_TO_VSC);
|
|
|
94 KEV(0).dwControlKeyState = KEV(1).dwControlKeyState = ctr_key_state;
|
|
|
95 if (ctr_key_state & LEFT_ALT_PRESSED) {
|
|
|
96 kb_state[VK_LMENU] = 0x01;
|
|
|
97 }
|
|
|
98 if (ctr_key_state & RIGHT_ALT_PRESSED) {
|
|
|
99 kb_state[VK_RMENU] = 0x01;
|
|
|
100 }
|
|
|
101 if (ctr_key_state & LEFT_CTRL_PRESSED) {
|
|
|
102 kb_state[VK_LCONTROL] = 0x01;
|
|
|
103 }
|
|
|
104 if (ctr_key_state & RIGHT_CTRL_PRESSED) {
|
|
|
105 kb_state[VK_RCONTROL] = 0x01;
|
|
|
106 }
|
|
|
107 if (ctr_key_state & SHIFT_PRESSED) {
|
|
|
108 kb_state[VK_SHIFT] = 0x01;
|
|
|
109 }
|
|
|
110 ret = ToUnicode(virt_key, KEV(0).wVirtualScanCode, kb_state, buf, 2, 0);
|
|
|
111 if (ret == 1) {
|
|
|
112 if(!is_wsl &&
|
|
|
113 ((ctr_key_state & LEFT_ALT_PRESSED) ||
|
|
|
114 (ctr_key_state & RIGHT_ALT_PRESSED))) {
|
|
|
115 /*
|
|
|
116 * If ALT key is pressed, the UnicodeChar value of the keyup event is
|
|
|
117 * set to 0 on nomal console. Emulate this behavior.
|
|
|
118 * See https://github.com/Microsoft/console/issues/320
|
|
|
119 */
|
|
|
120 KEV(0).uChar.UnicodeChar = buf[0];
|
|
|
121 KEV(1).uChar.UnicodeChar = 0;
|
|
|
122 } else{
|
|
|
123 /*
|
|
|
124 * In WSL UnicodeChar is normally set. This behavior cause #2111.
|
|
|
125 */
|
|
|
126 KEV(0).uChar.UnicodeChar = KEV(1).uChar.UnicodeChar = buf[0];
|
|
|
127 }
|
|
|
128 } else {
|
|
|
129 KEV(0).uChar.UnicodeChar = KEV(1).uChar.UnicodeChar = 0;
|
|
|
130 }
|
|
|
131 # undef KEV
|
|
|
132 }
|
|
|
133
|
|
|
134 TEST_IMPL(tty_duplicate_vt100_fn_key_libuv) {
|
|
|
135 int r;
|
|
|
136 int ttyin_fd;
|
|
|
137 uv_tty_t tty_in;
|
|
|
138 uv_loop_t* loop;
|
|
|
139 HANDLE handle;
|
|
|
140 INPUT_RECORD records[2];
|
|
|
141 DWORD written;
|
|
|
142
|
|
|
143 loop = uv_default_loop();
|
|
|
144
|
|
|
145 /* Make sure we have an FD that refers to a tty */
|
|
|
146 handle = CreateFileA("conin$",
|
|
|
147 GENERIC_READ | GENERIC_WRITE,
|
|
|
148 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
149 NULL,
|
|
|
150 OPEN_EXISTING,
|
|
|
151 FILE_ATTRIBUTE_NORMAL,
|
|
|
152 NULL);
|
|
|
153 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE);
|
|
|
154 ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
|
|
|
155 ASSERT_GE(ttyin_fd, 0);
|
|
|
156 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd));
|
|
|
157
|
|
|
158 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
|
|
|
159 ASSERT_OK(r);
|
|
|
160 ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
|
|
|
161 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
|
|
|
162
|
|
|
163 r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
|
|
|
164 ASSERT_OK(r);
|
|
|
165
|
|
|
166 /*
|
|
|
167 * libuv has chosen to emit ESC[[A, but other terminals, and even
|
|
|
168 * Windows itself use a different escape sequence, see the test below.
|
|
|
169 */
|
|
|
170 expect_str = ESC"[[A";
|
|
|
171 expect_nread = strlen(expect_str);
|
|
|
172
|
|
|
173 /* Turn on raw mode. */
|
|
|
174 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
|
|
|
175 ASSERT_OK(r);
|
|
|
176
|
|
|
177 /*
|
|
|
178 * Send F1 keystrokes. Test of issue cause by #2114 that vt100 fn key
|
|
|
179 * duplicate.
|
|
|
180 */
|
|
|
181 make_key_event_records(VK_F1, 0, TRUE, records);
|
|
|
182 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
183 ASSERT_EQ(written, ARRAY_SIZE(records));
|
|
|
184
|
|
|
185 uv_run(loop, UV_RUN_DEFAULT);
|
|
|
186
|
|
|
187 MAKE_VALGRIND_HAPPY(loop);
|
|
|
188 return 0;
|
|
|
189 }
|
|
|
190
|
|
|
191 TEST_IMPL(tty_duplicate_vt100_fn_key_winvt) {
|
|
|
192 int r;
|
|
|
193 int ttyin_fd;
|
|
|
194 uv_tty_t tty_in;
|
|
|
195 uv_loop_t* loop;
|
|
|
196 HANDLE handle;
|
|
|
197 INPUT_RECORD records[2];
|
|
|
198 DWORD written;
|
|
|
199
|
|
|
200 loop = uv_default_loop();
|
|
|
201
|
|
|
202 /* Make sure we have an FD that refers to a tty */
|
|
|
203 handle = CreateFileA("conin$",
|
|
|
204 GENERIC_READ | GENERIC_WRITE,
|
|
|
205 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
206 NULL,
|
|
|
207 OPEN_EXISTING,
|
|
|
208 FILE_ATTRIBUTE_NORMAL,
|
|
|
209 NULL);
|
|
|
210 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE);
|
|
|
211 ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
|
|
|
212 ASSERT_GE(ttyin_fd, 0);
|
|
|
213 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd));
|
|
|
214
|
|
|
215 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
|
|
|
216 ASSERT_OK(r);
|
|
|
217 ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
|
|
|
218 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
|
|
|
219
|
|
|
220 r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
|
|
|
221 ASSERT_OK(r);
|
|
|
222
|
|
|
223 /*
|
|
|
224 * Some keys, like F1, get are assigned a different value by Windows
|
|
|
225 * in ENABLE_VIRTUAL_TERMINAL_INPUT mode vs. libuv in the test above.
|
|
|
226 */
|
|
|
227 expect_str = ESC"OP";
|
|
|
228 expect_nread = strlen(expect_str);
|
|
|
229
|
|
|
230 /* Turn on raw mode. */
|
|
|
231 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW_VT);
|
|
|
232 ASSERT_OK(r);
|
|
|
233
|
|
|
234 /*
|
|
|
235 * Send F1 keystroke.
|
|
|
236 */
|
|
|
237 make_key_event_records(VK_F1, 0, TRUE, records);
|
|
|
238 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
239 ASSERT_EQ(written, ARRAY_SIZE(records));
|
|
|
240
|
|
|
241 uv_run(loop, UV_RUN_DEFAULT);
|
|
|
242
|
|
|
243 MAKE_VALGRIND_HAPPY(loop);
|
|
|
244 return 0;
|
|
|
245 }
|
|
|
246
|
|
|
247 TEST_IMPL(tty_duplicate_alt_modifier_key) {
|
|
|
248 int r;
|
|
|
249 int ttyin_fd;
|
|
|
250 uv_tty_t tty_in;
|
|
|
251 uv_loop_t* loop;
|
|
|
252 HANDLE handle;
|
|
|
253 INPUT_RECORD records[2];
|
|
|
254 INPUT_RECORD alt_records[2];
|
|
|
255 DWORD written;
|
|
|
256
|
|
|
257 loop = uv_default_loop();
|
|
|
258
|
|
|
259 /* Make sure we have an FD that refers to a tty */
|
|
|
260 handle = CreateFileA("conin$",
|
|
|
261 GENERIC_READ | GENERIC_WRITE,
|
|
|
262 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
263 NULL,
|
|
|
264 OPEN_EXISTING,
|
|
|
265 FILE_ATTRIBUTE_NORMAL,
|
|
|
266 NULL);
|
|
|
267 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE);
|
|
|
268 ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
|
|
|
269 ASSERT_GE(ttyin_fd, 0);
|
|
|
270 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd));
|
|
|
271
|
|
|
272 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
|
|
|
273 ASSERT_OK(r);
|
|
|
274 ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
|
|
|
275 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
|
|
|
276
|
|
|
277 r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
|
|
|
278 ASSERT_OK(r);
|
|
|
279
|
|
|
280 expect_str = ESC"a"ESC"a";
|
|
|
281 expect_nread = strlen(expect_str);
|
|
|
282
|
|
|
283 /* Turn on raw mode. */
|
|
|
284 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
|
|
|
285 ASSERT_OK(r);
|
|
|
286
|
|
|
287 /* Emulate transmission of M-a at normal console */
|
|
|
288 make_key_event_records(VK_MENU, 0, TRUE, alt_records);
|
|
|
289 WriteConsoleInputW(handle, &alt_records[0], 1, &written);
|
|
|
290 ASSERT_EQ(1, written);
|
|
|
291 make_key_event_records(L'A', LEFT_ALT_PRESSED, FALSE, records);
|
|
|
292 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
293 ASSERT_EQ(2, written);
|
|
|
294 WriteConsoleInputW(handle, &alt_records[1], 1, &written);
|
|
|
295 ASSERT_EQ(1, written);
|
|
|
296
|
|
|
297 /* Emulate transmission of M-a at WSL(#2111) */
|
|
|
298 make_key_event_records(VK_MENU, 0, TRUE, alt_records);
|
|
|
299 WriteConsoleInputW(handle, &alt_records[0], 1, &written);
|
|
|
300 ASSERT_EQ(1, written);
|
|
|
301 make_key_event_records(L'A', LEFT_ALT_PRESSED, TRUE, records);
|
|
|
302 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
303 ASSERT_EQ(2, written);
|
|
|
304 WriteConsoleInputW(handle, &alt_records[1], 1, &written);
|
|
|
305 ASSERT_EQ(1, written);
|
|
|
306
|
|
|
307 uv_run(loop, UV_RUN_DEFAULT);
|
|
|
308
|
|
|
309 MAKE_VALGRIND_HAPPY(loop);
|
|
|
310 return 0;
|
|
|
311 }
|
|
|
312
|
|
|
313 TEST_IMPL(tty_composing_character) {
|
|
|
314 int r;
|
|
|
315 int ttyin_fd;
|
|
|
316 uv_tty_t tty_in;
|
|
|
317 uv_loop_t* loop;
|
|
|
318 HANDLE handle;
|
|
|
319 INPUT_RECORD records[2];
|
|
|
320 INPUT_RECORD alt_records[2];
|
|
|
321 DWORD written;
|
|
|
322
|
|
|
323 loop = uv_default_loop();
|
|
|
324
|
|
|
325 /* Make sure we have an FD that refers to a tty */
|
|
|
326 handle = CreateFileA("conin$",
|
|
|
327 GENERIC_READ | GENERIC_WRITE,
|
|
|
328 FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
|
329 NULL,
|
|
|
330 OPEN_EXISTING,
|
|
|
331 FILE_ATTRIBUTE_NORMAL,
|
|
|
332 NULL);
|
|
|
333 ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE);
|
|
|
334 ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
|
|
|
335 ASSERT_GE(ttyin_fd, 0);
|
|
|
336 ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd));
|
|
|
337
|
|
|
338 r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
|
|
|
339 ASSERT_OK(r);
|
|
|
340 ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
|
|
|
341 ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
|
|
|
342
|
|
|
343 r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
|
|
|
344 ASSERT_OK(r);
|
|
|
345
|
|
|
346 expect_str = EUR_UTF8;
|
|
|
347 expect_nread = strlen(expect_str);
|
|
|
348
|
|
|
349 /* Turn on raw mode. */
|
|
|
350 r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
|
|
|
351 ASSERT_OK(r);
|
|
|
352
|
|
|
353 /* Emulate EUR inputs by LEFT ALT+NUMPAD ASCII KeyComos */
|
|
|
354 make_key_event_records(VK_MENU, 0, FALSE, alt_records);
|
|
|
355 alt_records[1].Event.KeyEvent.uChar.UnicodeChar = EUR_UNICODE;
|
|
|
356 WriteConsoleInputW(handle, &alt_records[0], 1, &written);
|
|
|
357 make_key_event_records(VK_NUMPAD0, LEFT_ALT_PRESSED, FALSE, records);
|
|
|
358 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
359 ASSERT_EQ(written, ARRAY_SIZE(records));
|
|
|
360 make_key_event_records(VK_NUMPAD1, LEFT_ALT_PRESSED, FALSE, records);
|
|
|
361 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
362 ASSERT_EQ(written, ARRAY_SIZE(records));
|
|
|
363 make_key_event_records(VK_NUMPAD2, LEFT_ALT_PRESSED, FALSE, records);
|
|
|
364 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
365 ASSERT_EQ(written, ARRAY_SIZE(records));
|
|
|
366 make_key_event_records(VK_NUMPAD8, LEFT_ALT_PRESSED, FALSE, records);
|
|
|
367 WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
|
|
|
368 ASSERT_EQ(written, ARRAY_SIZE(records));
|
|
|
369 WriteConsoleInputW(handle, &alt_records[1], 1, &written);
|
|
|
370
|
|
|
371 uv_run(loop, UV_RUN_DEFAULT);
|
|
|
372
|
|
|
373 MAKE_VALGRIND_HAPPY(loop);
|
|
|
374 return 0;
|
|
|
375 }
|
|
|
376
|
|
|
377 #else
|
|
|
378
|
|
|
379 typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */
|
|
|
380
|
|
|
381 #endif /* ifndef _WIN32 */
|