Home | History | Annotate | Line # | Download | only in test
test-tty-duplicate-key.c revision 1.1.1.1
      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(buf->base != NULL);
     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(nread == 0);
     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) {
    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(handle != INVALID_HANDLE_VALUE);
    154   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
    155   ASSERT(ttyin_fd >= 0);
    156   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
    157 
    158   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
    159   ASSERT(r == 0);
    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(r == 0);
    165 
    166   expect_str = ESC"[[A";
    167   expect_nread = strlen(expect_str);
    168 
    169   /* Turn on raw mode. */
    170   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
    171   ASSERT(r == 0);
    172 
    173   /*
    174    * Send F1 keystrokes. Test of issue cause by #2114 that vt100 fn key
    175    * duplicate.
    176    */
    177   make_key_event_records(VK_F1, 0, TRUE, records);
    178   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    179   ASSERT(written == ARRAY_SIZE(records));
    180 
    181   uv_run(loop, UV_RUN_DEFAULT);
    182 
    183   MAKE_VALGRIND_HAPPY();
    184   return 0;
    185 }
    186 
    187 TEST_IMPL(tty_duplicate_alt_modifier_key) {
    188   int r;
    189   int ttyin_fd;
    190   uv_tty_t tty_in;
    191   uv_loop_t* loop;
    192   HANDLE handle;
    193   INPUT_RECORD records[2];
    194   INPUT_RECORD alt_records[2];
    195   DWORD written;
    196 
    197   loop = uv_default_loop();
    198 
    199   /* Make sure we have an FD that refers to a tty */
    200   handle = CreateFileA("conin$",
    201                        GENERIC_READ | GENERIC_WRITE,
    202                        FILE_SHARE_READ | FILE_SHARE_WRITE,
    203                        NULL,
    204                        OPEN_EXISTING,
    205                        FILE_ATTRIBUTE_NORMAL,
    206                        NULL);
    207   ASSERT(handle != INVALID_HANDLE_VALUE);
    208   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
    209   ASSERT(ttyin_fd >= 0);
    210   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
    211 
    212   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
    213   ASSERT(r == 0);
    214   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
    215   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
    216 
    217   r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
    218   ASSERT(r == 0);
    219 
    220   expect_str = ESC"a"ESC"a";
    221   expect_nread = strlen(expect_str);
    222 
    223   /* Turn on raw mode. */
    224   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
    225   ASSERT(r == 0);
    226 
    227   /* Emulate transmission of M-a at normal console */
    228   make_key_event_records(VK_MENU, 0, TRUE, alt_records);
    229   WriteConsoleInputW(handle, &alt_records[0], 1, &written);
    230   ASSERT(written == 1);
    231   make_key_event_records(L'A', LEFT_ALT_PRESSED, FALSE, records);
    232   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    233   ASSERT(written == 2);
    234   WriteConsoleInputW(handle, &alt_records[1], 1, &written);
    235   ASSERT(written == 1);
    236 
    237   /* Emulate transmission of M-a at WSL(#2111) */
    238   make_key_event_records(VK_MENU, 0, TRUE, alt_records);
    239   WriteConsoleInputW(handle, &alt_records[0], 1, &written);
    240   ASSERT(written == 1);
    241   make_key_event_records(L'A', LEFT_ALT_PRESSED, TRUE, records);
    242   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    243   ASSERT(written == 2);
    244   WriteConsoleInputW(handle, &alt_records[1], 1, &written);
    245   ASSERT(written == 1);
    246 
    247   uv_run(loop, UV_RUN_DEFAULT);
    248 
    249   MAKE_VALGRIND_HAPPY();
    250   return 0;
    251 }
    252 
    253 TEST_IMPL(tty_composing_character) {
    254   int r;
    255   int ttyin_fd;
    256   uv_tty_t tty_in;
    257   uv_loop_t* loop;
    258   HANDLE handle;
    259   INPUT_RECORD records[2];
    260   INPUT_RECORD alt_records[2];
    261   DWORD written;
    262 
    263   loop = uv_default_loop();
    264 
    265   /* Make sure we have an FD that refers to a tty */
    266   handle = CreateFileA("conin$",
    267                        GENERIC_READ | GENERIC_WRITE,
    268                        FILE_SHARE_READ | FILE_SHARE_WRITE,
    269                        NULL,
    270                        OPEN_EXISTING,
    271                        FILE_ATTRIBUTE_NORMAL,
    272                        NULL);
    273   ASSERT(handle != INVALID_HANDLE_VALUE);
    274   ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
    275   ASSERT(ttyin_fd >= 0);
    276   ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
    277 
    278   r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1);  /* Readable. */
    279   ASSERT(r == 0);
    280   ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
    281   ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
    282 
    283   r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
    284   ASSERT(r == 0);
    285 
    286   expect_str = EUR_UTF8;
    287   expect_nread = strlen(expect_str);
    288 
    289   /* Turn on raw mode. */
    290   r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
    291   ASSERT(r == 0);
    292 
    293   /* Emulate EUR inputs by LEFT ALT+NUMPAD ASCII KeyComos */
    294   make_key_event_records(VK_MENU, 0, FALSE, alt_records);
    295   alt_records[1].Event.KeyEvent.uChar.UnicodeChar = EUR_UNICODE;
    296   WriteConsoleInputW(handle, &alt_records[0], 1, &written);
    297   make_key_event_records(VK_NUMPAD0, LEFT_ALT_PRESSED, FALSE, records);
    298   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    299   ASSERT(written == ARRAY_SIZE(records));
    300   make_key_event_records(VK_NUMPAD1, LEFT_ALT_PRESSED, FALSE, records);
    301   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    302   ASSERT(written == ARRAY_SIZE(records));
    303   make_key_event_records(VK_NUMPAD2, LEFT_ALT_PRESSED, FALSE, records);
    304   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    305   ASSERT(written == ARRAY_SIZE(records));
    306   make_key_event_records(VK_NUMPAD8, LEFT_ALT_PRESSED, FALSE, records);
    307   WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
    308   ASSERT(written == ARRAY_SIZE(records));
    309   WriteConsoleInputW(handle, &alt_records[1], 1, &written);
    310 
    311   uv_run(loop, UV_RUN_DEFAULT);
    312 
    313   MAKE_VALGRIND_HAPPY();
    314   return 0;
    315 }
    316 
    317 #else
    318 
    319 typedef int file_has_no_tests;  /* ISO C forbids an empty translation unit. */
    320 
    321 #endif  /* ifndef _WIN32 */
    322