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 | |