Mercurial
comparison third_party/libuv/test/test-tty-duplicate-key.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 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 */ |