test-tty-duplicate-key.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. #ifdef _WIN32
  22. #include "uv.h"
  23. #include "task.h"
  24. #include <errno.h>
  25. #include <io.h>
  26. #include <string.h>
  27. #include <windows.h>
  28. #define ESC "\x1b"
  29. #define EUR_UTF8 "\xe2\x82\xac"
  30. #define EUR_UNICODE 0x20AC
  31. const char* expect_str = NULL;
  32. ssize_t expect_nread = 0;
  33. static void dump_str(const char* str, ssize_t len) {
  34. ssize_t i;
  35. for (i = 0; i < len; i++) {
  36. fprintf(stderr, "%#02x ", *(str + i));
  37. }
  38. }
  39. static void print_err_msg(const char* expect, ssize_t expect_len,
  40. const char* found, ssize_t found_len) {
  41. fprintf(stderr, "expect ");
  42. dump_str(expect, expect_len);
  43. fprintf(stderr, ", but found ");
  44. dump_str(found, found_len);
  45. fprintf(stderr, "\n");
  46. }
  47. static void tty_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
  48. buf->base = malloc(size);
  49. ASSERT(buf->base != NULL);
  50. buf->len = size;
  51. }
  52. static void tty_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) {
  53. if (nread > 0) {
  54. if (nread != expect_nread) {
  55. fprintf(stderr, "expected nread %ld, but found %ld\n",
  56. (long)expect_nread, (long)nread);
  57. print_err_msg(expect_str, expect_nread, buf->base, nread);
  58. ASSERT(FALSE);
  59. }
  60. if (strncmp(buf->base, expect_str, nread) != 0) {
  61. print_err_msg(expect_str, expect_nread, buf->base, nread);
  62. ASSERT(FALSE);
  63. }
  64. uv_close((uv_handle_t*) tty_in, NULL);
  65. } else {
  66. ASSERT(nread == 0);
  67. }
  68. }
  69. static void make_key_event_records(WORD virt_key, DWORD ctr_key_state,
  70. BOOL is_wsl, INPUT_RECORD* records) {
  71. # define KEV(I) records[(I)].Event.KeyEvent
  72. BYTE kb_state[256] = {0};
  73. WCHAR buf[2];
  74. int ret;
  75. records[0].EventType = records[1].EventType = KEY_EVENT;
  76. KEV(0).bKeyDown = TRUE;
  77. KEV(1).bKeyDown = FALSE;
  78. KEV(0).wVirtualKeyCode = KEV(1).wVirtualKeyCode = virt_key;
  79. KEV(0).wRepeatCount = KEV(1).wRepeatCount = 1;
  80. KEV(0).wVirtualScanCode = KEV(1).wVirtualScanCode =
  81. MapVirtualKeyW(virt_key, MAPVK_VK_TO_VSC);
  82. KEV(0).dwControlKeyState = KEV(1).dwControlKeyState = ctr_key_state;
  83. if (ctr_key_state & LEFT_ALT_PRESSED) {
  84. kb_state[VK_LMENU] = 0x01;
  85. }
  86. if (ctr_key_state & RIGHT_ALT_PRESSED) {
  87. kb_state[VK_RMENU] = 0x01;
  88. }
  89. if (ctr_key_state & LEFT_CTRL_PRESSED) {
  90. kb_state[VK_LCONTROL] = 0x01;
  91. }
  92. if (ctr_key_state & RIGHT_CTRL_PRESSED) {
  93. kb_state[VK_RCONTROL] = 0x01;
  94. }
  95. if (ctr_key_state & SHIFT_PRESSED) {
  96. kb_state[VK_SHIFT] = 0x01;
  97. }
  98. ret = ToUnicode(virt_key, KEV(0).wVirtualScanCode, kb_state, buf, 2, 0);
  99. if (ret == 1) {
  100. if(!is_wsl &&
  101. ((ctr_key_state & LEFT_ALT_PRESSED) ||
  102. (ctr_key_state & RIGHT_ALT_PRESSED))) {
  103. /*
  104. * If ALT key is pressed, the UnicodeChar value of the keyup event is
  105. * set to 0 on nomal console. Emulate this behavior.
  106. * See https://github.com/Microsoft/console/issues/320
  107. */
  108. KEV(0).uChar.UnicodeChar = buf[0];
  109. KEV(1).uChar.UnicodeChar = 0;
  110. } else{
  111. /*
  112. * In WSL UnicodeChar is normally set. This behavior cause #2111.
  113. */
  114. KEV(0).uChar.UnicodeChar = KEV(1).uChar.UnicodeChar = buf[0];
  115. }
  116. } else {
  117. KEV(0).uChar.UnicodeChar = KEV(1).uChar.UnicodeChar = 0;
  118. }
  119. # undef KEV
  120. }
  121. TEST_IMPL(tty_duplicate_vt100_fn_key) {
  122. int r;
  123. int ttyin_fd;
  124. uv_tty_t tty_in;
  125. uv_loop_t* loop;
  126. HANDLE handle;
  127. INPUT_RECORD records[2];
  128. DWORD written;
  129. loop = uv_default_loop();
  130. /* Make sure we have an FD that refers to a tty */
  131. handle = CreateFileA("conin$",
  132. GENERIC_READ | GENERIC_WRITE,
  133. FILE_SHARE_READ | FILE_SHARE_WRITE,
  134. NULL,
  135. OPEN_EXISTING,
  136. FILE_ATTRIBUTE_NORMAL,
  137. NULL);
  138. ASSERT(handle != INVALID_HANDLE_VALUE);
  139. ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
  140. ASSERT(ttyin_fd >= 0);
  141. ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
  142. r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
  143. ASSERT(r == 0);
  144. ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
  145. ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
  146. r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
  147. ASSERT(r == 0);
  148. expect_str = ESC"[[A";
  149. expect_nread = strlen(expect_str);
  150. /* Turn on raw mode. */
  151. r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
  152. ASSERT(r == 0);
  153. /*
  154. * Send F1 keystrokes. Test of issue cause by #2114 that vt100 fn key
  155. * duplicate.
  156. */
  157. make_key_event_records(VK_F1, 0, TRUE, records);
  158. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  159. ASSERT(written == ARRAY_SIZE(records));
  160. uv_run(loop, UV_RUN_DEFAULT);
  161. MAKE_VALGRIND_HAPPY();
  162. return 0;
  163. }
  164. TEST_IMPL(tty_duplicate_alt_modifier_key) {
  165. int r;
  166. int ttyin_fd;
  167. uv_tty_t tty_in;
  168. uv_loop_t* loop;
  169. HANDLE handle;
  170. INPUT_RECORD records[2];
  171. INPUT_RECORD alt_records[2];
  172. DWORD written;
  173. loop = uv_default_loop();
  174. /* Make sure we have an FD that refers to a tty */
  175. handle = CreateFileA("conin$",
  176. GENERIC_READ | GENERIC_WRITE,
  177. FILE_SHARE_READ | FILE_SHARE_WRITE,
  178. NULL,
  179. OPEN_EXISTING,
  180. FILE_ATTRIBUTE_NORMAL,
  181. NULL);
  182. ASSERT(handle != INVALID_HANDLE_VALUE);
  183. ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
  184. ASSERT(ttyin_fd >= 0);
  185. ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
  186. r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
  187. ASSERT(r == 0);
  188. ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
  189. ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
  190. r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
  191. ASSERT(r == 0);
  192. expect_str = ESC"a"ESC"a";
  193. expect_nread = strlen(expect_str);
  194. /* Turn on raw mode. */
  195. r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
  196. ASSERT(r == 0);
  197. /* Emulate transmission of M-a at normal console */
  198. make_key_event_records(VK_MENU, 0, TRUE, alt_records);
  199. WriteConsoleInputW(handle, &alt_records[0], 1, &written);
  200. ASSERT(written == 1);
  201. make_key_event_records(L'A', LEFT_ALT_PRESSED, FALSE, records);
  202. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  203. ASSERT(written == 2);
  204. WriteConsoleInputW(handle, &alt_records[1], 1, &written);
  205. ASSERT(written == 1);
  206. /* Emulate transmission of M-a at WSL(#2111) */
  207. make_key_event_records(VK_MENU, 0, TRUE, alt_records);
  208. WriteConsoleInputW(handle, &alt_records[0], 1, &written);
  209. ASSERT(written == 1);
  210. make_key_event_records(L'A', LEFT_ALT_PRESSED, TRUE, records);
  211. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  212. ASSERT(written == 2);
  213. WriteConsoleInputW(handle, &alt_records[1], 1, &written);
  214. ASSERT(written == 1);
  215. uv_run(loop, UV_RUN_DEFAULT);
  216. MAKE_VALGRIND_HAPPY();
  217. return 0;
  218. }
  219. TEST_IMPL(tty_composing_character) {
  220. int r;
  221. int ttyin_fd;
  222. uv_tty_t tty_in;
  223. uv_loop_t* loop;
  224. HANDLE handle;
  225. INPUT_RECORD records[2];
  226. INPUT_RECORD alt_records[2];
  227. DWORD written;
  228. loop = uv_default_loop();
  229. /* Make sure we have an FD that refers to a tty */
  230. handle = CreateFileA("conin$",
  231. GENERIC_READ | GENERIC_WRITE,
  232. FILE_SHARE_READ | FILE_SHARE_WRITE,
  233. NULL,
  234. OPEN_EXISTING,
  235. FILE_ATTRIBUTE_NORMAL,
  236. NULL);
  237. ASSERT(handle != INVALID_HANDLE_VALUE);
  238. ttyin_fd = _open_osfhandle((intptr_t) handle, 0);
  239. ASSERT(ttyin_fd >= 0);
  240. ASSERT(UV_TTY == uv_guess_handle(ttyin_fd));
  241. r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */
  242. ASSERT(r == 0);
  243. ASSERT(uv_is_readable((uv_stream_t*) &tty_in));
  244. ASSERT(!uv_is_writable((uv_stream_t*) &tty_in));
  245. r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read);
  246. ASSERT(r == 0);
  247. expect_str = EUR_UTF8;
  248. expect_nread = strlen(expect_str);
  249. /* Turn on raw mode. */
  250. r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW);
  251. ASSERT(r == 0);
  252. /* Emulate EUR inputs by LEFT ALT+NUMPAD ASCII KeyComos */
  253. make_key_event_records(VK_MENU, 0, FALSE, alt_records);
  254. alt_records[1].Event.KeyEvent.uChar.UnicodeChar = EUR_UNICODE;
  255. WriteConsoleInputW(handle, &alt_records[0], 1, &written);
  256. make_key_event_records(VK_NUMPAD0, LEFT_ALT_PRESSED, FALSE, records);
  257. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  258. ASSERT(written == ARRAY_SIZE(records));
  259. make_key_event_records(VK_NUMPAD1, LEFT_ALT_PRESSED, FALSE, records);
  260. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  261. ASSERT(written == ARRAY_SIZE(records));
  262. make_key_event_records(VK_NUMPAD2, LEFT_ALT_PRESSED, FALSE, records);
  263. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  264. ASSERT(written == ARRAY_SIZE(records));
  265. make_key_event_records(VK_NUMPAD8, LEFT_ALT_PRESSED, FALSE, records);
  266. WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written);
  267. ASSERT(written == ARRAY_SIZE(records));
  268. WriteConsoleInputW(handle, &alt_records[1], 1, &written);
  269. uv_run(loop, UV_RUN_DEFAULT);
  270. MAKE_VALGRIND_HAPPY();
  271. return 0;
  272. }
  273. #else
  274. typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */
  275. #endif /* ifndef _WIN32 */