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