Home | History | Annotate | Line # | Download | only in test
test-tty-duplicate-key.c revision 1.1.1.3
      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 */
    382