xref: /netbsd-src/external/mit/libuv/dist/test/test-tty-duplicate-key.c (revision 5f2f42719cd62ff11fd913b40b7ce19f07c4fd25)
10e552da7Schristos /* Copyright libuv project contributors. All rights reserved.
20e552da7Schristos  *
30e552da7Schristos  * Permission is hereby granted, free of charge, to any person obtaining a copy
40e552da7Schristos  * of this software and associated documentation files (the "Software"), to
50e552da7Schristos  * deal in the Software without restriction, including without limitation the
60e552da7Schristos  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
70e552da7Schristos  * sell copies of the Software, and to permit persons to whom the Software is
80e552da7Schristos  * furnished to do so, subject to the following conditions:
90e552da7Schristos  *
100e552da7Schristos  * The above copyright notice and this permission notice shall be included in
110e552da7Schristos  * all copies or substantial portions of the Software.
120e552da7Schristos  *
130e552da7Schristos  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140e552da7Schristos  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
150e552da7Schristos  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
160e552da7Schristos  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
170e552da7Schristos  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
180e552da7Schristos  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
190e552da7Schristos  * IN THE SOFTWARE.
200e552da7Schristos  */
210e552da7Schristos 
220e552da7Schristos #ifdef _WIN32
230e552da7Schristos 
240e552da7Schristos #include "uv.h"
250e552da7Schristos #include "task.h"
260e552da7Schristos 
270e552da7Schristos #include <errno.h>
280e552da7Schristos #include <io.h>
290e552da7Schristos #include <string.h>
300e552da7Schristos #include <windows.h>
310e552da7Schristos 
320e552da7Schristos #define ESC "\x1b"
330e552da7Schristos #define EUR_UTF8 "\xe2\x82\xac"
340e552da7Schristos #define EUR_UNICODE 0x20AC
350e552da7Schristos 
360e552da7Schristos 
370e552da7Schristos const char* expect_str = NULL;
380e552da7Schristos ssize_t expect_nread = 0;
390e552da7Schristos 
dump_str(const char * str,ssize_t len)400e552da7Schristos static void dump_str(const char* str, ssize_t len) {
410e552da7Schristos   ssize_t i;
420e552da7Schristos   for (i = 0; i < len; i++) {
430e552da7Schristos     fprintf(stderr, "%#02x ", *(str + i));
440e552da7Schristos   }
450e552da7Schristos }
460e552da7Schristos 
print_err_msg(const char * expect,ssize_t expect_len,const char * found,ssize_t found_len)470e552da7Schristos static void print_err_msg(const char* expect, ssize_t expect_len,
480e552da7Schristos                           const char* found, ssize_t found_len) {
490e552da7Schristos   fprintf(stderr, "expect ");
500e552da7Schristos   dump_str(expect, expect_len);
510e552da7Schristos   fprintf(stderr, ", but found ");
520e552da7Schristos   dump_str(found, found_len);
530e552da7Schristos   fprintf(stderr, "\n");
540e552da7Schristos }
550e552da7Schristos 
tty_alloc(uv_handle_t * handle,size_t size,uv_buf_t * buf)560e552da7Schristos static void tty_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
570e552da7Schristos   buf->base = malloc(size);
58*5f2f4271Schristos   ASSERT_NOT_NULL(buf->base);
590e552da7Schristos   buf->len = size;
600e552da7Schristos }
610e552da7Schristos 
tty_read(uv_stream_t * tty_in,ssize_t nread,const uv_buf_t * buf)620e552da7Schristos static void tty_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) {
630e552da7Schristos   if (nread > 0) {
640e552da7Schristos     if (nread != expect_nread) {
650e552da7Schristos       fprintf(stderr, "expected nread %ld, but found %ld\n",
660e552da7Schristos               (long)expect_nread, (long)nread);
670e552da7Schristos       print_err_msg(expect_str, expect_nread, buf->base, nread);
680e552da7Schristos       ASSERT(FALSE);
690e552da7Schristos     }
700e552da7Schristos     if (strncmp(buf->base, expect_str, nread) != 0) {
710e552da7Schristos       print_err_msg(expect_str, expect_nread, buf->base, nread);
720e552da7Schristos       ASSERT(FALSE);
730e552da7Schristos     }
740e552da7Schristos     uv_close((uv_handle_t*) tty_in, NULL);
750e552da7Schristos   } else {
760e552da7Schristos     ASSERT(nread == 0);
770e552da7Schristos   }
780e552da7Schristos }
790e552da7Schristos 
make_key_event_records(WORD virt_key,DWORD ctr_key_state,BOOL is_wsl,INPUT_RECORD * records)800e552da7Schristos static void make_key_event_records(WORD virt_key, DWORD ctr_key_state,
810e552da7Schristos                                    BOOL is_wsl, INPUT_RECORD* records) {
820e552da7Schristos # define KEV(I) records[(I)].Event.KeyEvent
830e552da7Schristos   BYTE kb_state[256] = {0};
840e552da7Schristos   WCHAR buf[2];
850e552da7Schristos   int ret;
860e552da7Schristos 
870e552da7Schristos   records[0].EventType = records[1].EventType = KEY_EVENT;
880e552da7Schristos   KEV(0).bKeyDown = TRUE;
890e552da7Schristos   KEV(1).bKeyDown = FALSE;
900e552da7Schristos   KEV(0).wVirtualKeyCode = KEV(1).wVirtualKeyCode = virt_key;
910e552da7Schristos   KEV(0).wRepeatCount = KEV(1).wRepeatCount = 1;
920e552da7Schristos   KEV(0).wVirtualScanCode = KEV(1).wVirtualScanCode =
930e552da7Schristos     MapVirtualKeyW(virt_key, MAPVK_VK_TO_VSC);
940e552da7Schristos   KEV(0).dwControlKeyState = KEV(1).dwControlKeyState = ctr_key_state;
950e552da7Schristos   if (ctr_key_state & LEFT_ALT_PRESSED) {
960e552da7Schristos     kb_state[VK_LMENU] = 0x01;
970e552da7Schristos   }
980e552da7Schristos   if (ctr_key_state & RIGHT_ALT_PRESSED) {
990e552da7Schristos     kb_state[VK_RMENU] = 0x01;
1000e552da7Schristos   }
1010e552da7Schristos   if (ctr_key_state & LEFT_CTRL_PRESSED) {
1020e552da7Schristos     kb_state[VK_LCONTROL] = 0x01;
1030e552da7Schristos   }
1040e552da7Schristos   if (ctr_key_state & RIGHT_CTRL_PRESSED) {
1050e552da7Schristos     kb_state[VK_RCONTROL] = 0x01;
1060e552da7Schristos   }
1070e552da7Schristos   if (ctr_key_state & SHIFT_PRESSED) {
1080e552da7Schristos     kb_state[VK_SHIFT] = 0x01;
1090e552da7Schristos   }
1100e552da7Schristos   ret = ToUnicode(virt_key, KEV(0).wVirtualScanCode, kb_state, buf, 2, 0);
1110e552da7Schristos   if (ret == 1) {
1120e552da7Schristos     if(!is_wsl &&
1130e552da7Schristos         ((ctr_key_state & LEFT_ALT_PRESSED) ||
1140e552da7Schristos          (ctr_key_state & RIGHT_ALT_PRESSED))) {
1150e552da7Schristos       /*
1160e552da7Schristos        * If ALT key is pressed, the UnicodeChar value of the keyup event is
1170e552da7Schristos        * set to 0 on nomal console. Emulate this behavior.
1180e552da7Schristos        * See https://github.com/Microsoft/console/issues/320
1190e552da7Schristos        */
1200e552da7Schristos       KEV(0).uChar.UnicodeChar = buf[0];
1210e552da7Schristos       KEV(1).uChar.UnicodeChar = 0;
1220e552da7Schristos     } else{
1230e552da7Schristos       /*
1240e552da7Schristos        * In WSL UnicodeChar is normally set. This behavior cause #2111.
1250e552da7Schristos        */
1260e552da7Schristos       KEV(0).uChar.UnicodeChar = KEV(1).uChar.UnicodeChar = buf[0];
1270e552da7Schristos     }
1280e552da7Schristos   } else {
1290e552da7Schristos     KEV(0).uChar.UnicodeChar = KEV(1).uChar.UnicodeChar = 0;
1300e552da7Schristos   }
1310e552da7Schristos # undef KEV
1320e552da7Schristos }
1330e552da7Schristos 
TEST_IMPL(tty_duplicate_vt100_fn_key)1340e552da7Schristos TEST_IMPL(tty_duplicate_vt100_fn_key) {
1350e552da7Schristos   int r;
1360e552da7Schristos   int ttyin_fd;
1370e552da7Schristos   uv_tty_t tty_in;
1380e552da7Schristos   uv_loop_t* loop;
1390e552da7Schristos   HANDLE handle;
1400e552da7Schristos   INPUT_RECORD records[2];
1410e552da7Schristos   DWORD written;
1420e552da7Schristos 
1430e552da7Schristos   loop = uv_default_loop();
1440e552da7Schristos 
1450e552da7Schristos   /* Make sure we have an FD that refers to a tty */
1460e552da7Schristos   handle = CreateFileA("conin$",
1470e552da7Schristos                        GENERIC_READ | GENERIC_WRITE,
1480e552da7Schristos                        FILE_SHARE_READ | FILE_SHARE_WRITE,
1490e552da7Schristos                        NULL,
1500e552da7Schristos                        OPEN_EXISTING,
1510e552da7Schristos                        FILE_ATTRIBUTE_NORMAL,
1520e552da7Schristos                        NULL);
1530e552da7Schristos   ASSERT(handle != INVALID_HANDLE_VALUE);
1540e552da7Schristos   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
1550e552da7Schristos   ASSERT(ttyin_fd >= 0);
1560e552da7Schristos   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
1570e552da7Schristos 
1580e552da7Schristos   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
1590e552da7Schristos   ASSERT(r == 0);
1600e552da7Schristos   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
1610e552da7Schristos   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
1620e552da7Schristos 
1630e552da7Schristos   r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
1640e552da7Schristos   ASSERT(r == 0);
1650e552da7Schristos 
1660e552da7Schristos   expect_str = ESC"[[A";
1670e552da7Schristos   expect_nread = strlen(expect_str);
1680e552da7Schristos 
1690e552da7Schristos   /* Turn on raw mode. */
1700e552da7Schristos   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
1710e552da7Schristos   ASSERT(r == 0);
1720e552da7Schristos 
1730e552da7Schristos   /*
1740e552da7Schristos    * Send F1 keystrokes. Test of issue cause by #2114 that vt100 fn key
1750e552da7Schristos    * duplicate.
1760e552da7Schristos    */
1770e552da7Schristos   make_key_event_records(VK_F1, 0, TRUE, records);
1780e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
1790e552da7Schristos   ASSERT(written == ARRAY_SIZE(records));
1800e552da7Schristos 
1810e552da7Schristos   uv_run(loop, UV_RUN_DEFAULT);
1820e552da7Schristos 
1830e552da7Schristos   MAKE_VALGRIND_HAPPY();
1840e552da7Schristos   return 0;
1850e552da7Schristos }
1860e552da7Schristos 
TEST_IMPL(tty_duplicate_alt_modifier_key)1870e552da7Schristos TEST_IMPL(tty_duplicate_alt_modifier_key) {
1880e552da7Schristos   int r;
1890e552da7Schristos   int ttyin_fd;
1900e552da7Schristos   uv_tty_t tty_in;
1910e552da7Schristos   uv_loop_t* loop;
1920e552da7Schristos   HANDLE handle;
1930e552da7Schristos   INPUT_RECORD records[2];
1940e552da7Schristos   INPUT_RECORD alt_records[2];
1950e552da7Schristos   DWORD written;
1960e552da7Schristos 
1970e552da7Schristos   loop = uv_default_loop();
1980e552da7Schristos 
1990e552da7Schristos   /* Make sure we have an FD that refers to a tty */
2000e552da7Schristos   handle = CreateFileA("conin$",
2010e552da7Schristos                        GENERIC_READ | GENERIC_WRITE,
2020e552da7Schristos                        FILE_SHARE_READ | FILE_SHARE_WRITE,
2030e552da7Schristos                        NULL,
2040e552da7Schristos                        OPEN_EXISTING,
2050e552da7Schristos                        FILE_ATTRIBUTE_NORMAL,
2060e552da7Schristos                        NULL);
2070e552da7Schristos   ASSERT(handle != INVALID_HANDLE_VALUE);
2080e552da7Schristos   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
2090e552da7Schristos   ASSERT(ttyin_fd >= 0);
2100e552da7Schristos   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
2110e552da7Schristos 
2120e552da7Schristos   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
2130e552da7Schristos   ASSERT(r == 0);
2140e552da7Schristos   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
2150e552da7Schristos   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
2160e552da7Schristos 
2170e552da7Schristos   r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
2180e552da7Schristos   ASSERT(r == 0);
2190e552da7Schristos 
2200e552da7Schristos   expect_str = ESC"a"ESC"a";
2210e552da7Schristos   expect_nread = strlen(expect_str);
2220e552da7Schristos 
2230e552da7Schristos   /* Turn on raw mode. */
2240e552da7Schristos   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
2250e552da7Schristos   ASSERT(r == 0);
2260e552da7Schristos 
2270e552da7Schristos   /* Emulate transmission of M-a at normal console */
2280e552da7Schristos   make_key_event_records(VK_MENU, 0, TRUE, alt_records);
2290e552da7Schristos   WriteConsoleInputW(handle, &alt_records[0], 1, &written);
2300e552da7Schristos   ASSERT(written == 1);
2310e552da7Schristos   make_key_event_records(L'A', LEFT_ALT_PRESSED, FALSE, records);
2320e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
2330e552da7Schristos   ASSERT(written == 2);
2340e552da7Schristos   WriteConsoleInputW(handle, &alt_records[1], 1, &written);
2350e552da7Schristos   ASSERT(written == 1);
2360e552da7Schristos 
2370e552da7Schristos   /* Emulate transmission of M-a at WSL(#2111) */
2380e552da7Schristos   make_key_event_records(VK_MENU, 0, TRUE, alt_records);
2390e552da7Schristos   WriteConsoleInputW(handle, &alt_records[0], 1, &written);
2400e552da7Schristos   ASSERT(written == 1);
2410e552da7Schristos   make_key_event_records(L'A', LEFT_ALT_PRESSED, TRUE, records);
2420e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
2430e552da7Schristos   ASSERT(written == 2);
2440e552da7Schristos   WriteConsoleInputW(handle, &alt_records[1], 1, &written);
2450e552da7Schristos   ASSERT(written == 1);
2460e552da7Schristos 
2470e552da7Schristos   uv_run(loop, UV_RUN_DEFAULT);
2480e552da7Schristos 
2490e552da7Schristos   MAKE_VALGRIND_HAPPY();
2500e552da7Schristos   return 0;
2510e552da7Schristos }
2520e552da7Schristos 
TEST_IMPL(tty_composing_character)2530e552da7Schristos TEST_IMPL(tty_composing_character) {
2540e552da7Schristos   int r;
2550e552da7Schristos   int ttyin_fd;
2560e552da7Schristos   uv_tty_t tty_in;
2570e552da7Schristos   uv_loop_t* loop;
2580e552da7Schristos   HANDLE handle;
2590e552da7Schristos   INPUT_RECORD records[2];
2600e552da7Schristos   INPUT_RECORD alt_records[2];
2610e552da7Schristos   DWORD written;
2620e552da7Schristos 
2630e552da7Schristos   loop = uv_default_loop();
2640e552da7Schristos 
2650e552da7Schristos   /* Make sure we have an FD that refers to a tty */
2660e552da7Schristos   handle = CreateFileA("conin$",
2670e552da7Schristos                        GENERIC_READ | GENERIC_WRITE,
2680e552da7Schristos                        FILE_SHARE_READ | FILE_SHARE_WRITE,
2690e552da7Schristos                        NULL,
2700e552da7Schristos                        OPEN_EXISTING,
2710e552da7Schristos                        FILE_ATTRIBUTE_NORMAL,
2720e552da7Schristos                        NULL);
2730e552da7Schristos   ASSERT(handle != INVALID_HANDLE_VALUE);
2740e552da7Schristos   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
2750e552da7Schristos   ASSERT(ttyin_fd >= 0);
2760e552da7Schristos   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
2770e552da7Schristos 
2780e552da7Schristos   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
2790e552da7Schristos   ASSERT(r == 0);
2800e552da7Schristos   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
2810e552da7Schristos   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
2820e552da7Schristos 
2830e552da7Schristos   r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
2840e552da7Schristos   ASSERT(r == 0);
2850e552da7Schristos 
2860e552da7Schristos   expect_str = EUR_UTF8;
2870e552da7Schristos   expect_nread = strlen(expect_str);
2880e552da7Schristos 
2890e552da7Schristos   /* Turn on raw mode. */
2900e552da7Schristos   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
2910e552da7Schristos   ASSERT(r == 0);
2920e552da7Schristos 
2930e552da7Schristos   /* Emulate EUR inputs by LEFT ALT+NUMPAD ASCII KeyComos */
2940e552da7Schristos   make_key_event_records(VK_MENU, 0, FALSE, alt_records);
2950e552da7Schristos   alt_records[1].Event.KeyEvent.uChar.UnicodeChar = EUR_UNICODE;
2960e552da7Schristos   WriteConsoleInputW(handle, &alt_records[0], 1, &written);
2970e552da7Schristos   make_key_event_records(VK_NUMPAD0, LEFT_ALT_PRESSED, FALSE, records);
2980e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
2990e552da7Schristos   ASSERT(written == ARRAY_SIZE(records));
3000e552da7Schristos   make_key_event_records(VK_NUMPAD1, LEFT_ALT_PRESSED, FALSE, records);
3010e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
3020e552da7Schristos   ASSERT(written == ARRAY_SIZE(records));
3030e552da7Schristos   make_key_event_records(VK_NUMPAD2, LEFT_ALT_PRESSED, FALSE, records);
3040e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
3050e552da7Schristos   ASSERT(written == ARRAY_SIZE(records));
3060e552da7Schristos   make_key_event_records(VK_NUMPAD8, LEFT_ALT_PRESSED, FALSE, records);
3070e552da7Schristos   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
3080e552da7Schristos   ASSERT(written == ARRAY_SIZE(records));
3090e552da7Schristos   WriteConsoleInputW(handle, &alt_records[1], 1, &written);
3100e552da7Schristos 
3110e552da7Schristos   uv_run(loop, UV_RUN_DEFAULT);
3120e552da7Schristos 
3130e552da7Schristos   MAKE_VALGRIND_HAPPY();
3140e552da7Schristos   return 0;
3150e552da7Schristos }
3160e552da7Schristos 
3170e552da7Schristos #else
3180e552da7Schristos 
3190e552da7Schristos typedef int file_has_no_tests;  /* ISO C forbids an empty translation unit. */
3200e552da7Schristos 
3210e552da7Schristos #endif  /* ifndef _WIN32 */
322