msxml3: Fold in reset_output_buffer.
[wine/zf.git] / programs / conhost / tests / tty.c
blob7cc37fa7fe1807a21f9b83150e01a907fff4f9ff
1 /*
2 * 2020 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "wine/test.h"
21 #include <windows.h>
23 static HRESULT (WINAPI *pCreatePseudoConsole)(COORD,HANDLE,HANDLE,DWORD,HPCON*);
24 static void (WINAPI *pClosePseudoConsole)(HPCON);
26 static char console_output[4096];
27 static unsigned int console_output_count;
28 static HANDLE console_pipe;
29 static HANDLE child_pipe;
31 #define fetch_console_output() fetch_console_output_(__LINE__)
32 static void fetch_console_output_(unsigned int line)
34 OVERLAPPED o;
35 DWORD count;
36 BOOL ret;
38 if (console_output_count == sizeof(console_output)) return;
40 memset(&o, 0, sizeof(o));
41 o.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
42 ret = ReadFile(console_pipe, console_output + console_output_count,
43 sizeof(console_output) - console_output_count, NULL, &o);
44 if (!ret)
46 ok_(__FILE__,line)(GetLastError() == ERROR_IO_PENDING, "read failed: %u\n", GetLastError());
47 if (GetLastError() != ERROR_IO_PENDING) return;
48 WaitForSingleObject(o.hEvent, 5000);
50 ret = GetOverlappedResult(console_pipe, &o, &count, FALSE);
51 if (!ret && GetLastError() == ERROR_IO_INCOMPLETE)
52 CancelIoEx(console_pipe, &o);
54 ok_(__FILE__,line)(ret, "Read file failed: %u\n", GetLastError());
55 CloseHandle(o.hEvent);
56 if (ret) console_output_count += count;
59 #define expect_empty_output() expect_empty_output_(__LINE__)
60 static void expect_empty_output_(unsigned int line)
62 DWORD avail;
63 BOOL ret;
65 ret = PeekNamedPipe(console_pipe, NULL, 0, NULL, &avail, NULL);
66 ok_(__FILE__,line)(ret, "PeekNamedPipe failed: %u\n", GetLastError());
67 ok_(__FILE__,line)(!avail, "avail = %u\n", avail);
68 if (avail) fetch_console_output_(line);
69 ok_(__FILE__,line)(!console_output_count, "expected empty buffer, got %s\n",
70 wine_dbgstr_an(console_output, console_output_count));
71 console_output_count = 0;
74 #define expect_output_sequence(a) expect_output_sequence_(__LINE__,0,a)
75 #define expect_output_sequence_ctx(a,b) expect_output_sequence_(__LINE__,a,b)
76 static void expect_output_sequence_(unsigned int line, unsigned ctx, const char *expect)
78 size_t len = strlen(expect);
79 if (console_output_count < len) fetch_console_output_(line);
80 if (len <= console_output_count && !memcmp(console_output, expect, len))
82 console_output_count -= len;
83 memmove(console_output, console_output + len, console_output_count);
85 else ok_(__FILE__,line)(0, "%x: expected %s got %s\n", ctx, wine_dbgstr_a(expect),
86 wine_dbgstr_an(console_output, console_output_count));
89 #define skip_sequence(a) skip_sequence_(__LINE__,a)
90 static BOOL skip_sequence_(unsigned int line, const char *expect)
92 size_t len = strlen(expect);
93 DWORD avail;
94 BOOL r;
96 r = PeekNamedPipe(console_pipe, NULL, 0, NULL, &avail, NULL);
97 if (!console_output_count && r && !avail)
99 Sleep(50);
100 r = PeekNamedPipe(console_pipe, NULL, 0, NULL, &avail, NULL);
102 if (r && avail) fetch_console_output_(line);
104 if (len > console_output_count || memcmp(console_output, expect, len)) return FALSE;
105 console_output_count -= len;
106 memmove(console_output, console_output + len, console_output_count);
107 return TRUE;
110 #define skip_byte(a) skip_byte_(__LINE__,a)
111 static BOOL skip_byte_(unsigned int line, char ch)
113 if (!console_output_count || console_output[0] != ch) return FALSE;
114 console_output_count--;
115 memmove(console_output, console_output + 1, console_output_count);
116 return TRUE;
119 #define expect_hide_cursor() expect_hide_cursor_(__LINE__)
120 static void expect_hide_cursor_(unsigned int line)
122 if (!console_output_count) fetch_console_output_(line);
123 ok_(__FILE__,line)(skip_sequence_(line, "\x1b[?25l") || broken(skip_sequence_(line, "\x1b[25l")),
124 "expected hide cursor escape\n");
127 #define skip_hide_cursor() skip_hide_cursor_(__LINE__)
128 static BOOL skip_hide_cursor_(unsigned int line)
130 if (!console_output_count) fetch_console_output_(line);
131 return skip_sequence_(line, "\x1b[25l") || broken(skip_sequence_(line, "\x1b[?25l"));
134 #define expect_erase_line(a) expect_erase_line_(__LINE__,a)
135 static BOOL expect_erase_line_(unsigned line, unsigned int cnt)
137 char buf[16];
138 if (skip_sequence("\x1b[K")) return FALSE;
139 ok(broken(1), "expected erase line\n");
140 sprintf(buf, "\x1b[%uX", cnt);
141 expect_output_sequence_(line, cnt, buf); /* erase the rest of the line */
142 sprintf(buf, "\x1b[%uC", cnt);
143 expect_output_sequence_(line, cnt, buf); /* move cursor to the end of the line */
144 return TRUE;
147 enum req_type
149 REQ_CREATE_SCREEN_BUFFER,
150 REQ_FILL_CHAR,
151 REQ_GET_INPUT,
152 REQ_READ_CONSOLE,
153 REQ_READ_CONSOLE_A,
154 REQ_READ_CONSOLE_FILE,
155 REQ_SCROLL,
156 REQ_SET_ACTIVE,
157 REQ_SET_CURSOR,
158 REQ_SET_INPUT_CP,
159 REQ_SET_INPUT_MODE,
160 REQ_SET_OUTPUT_MODE,
161 REQ_SET_TITLE,
162 REQ_WRITE_CHARACTERS,
163 REQ_WRITE_CONSOLE,
164 REQ_WRITE_OUTPUT,
167 struct pseudoconsole_req
169 enum req_type type;
170 union
172 WCHAR string[1];
173 COORD coord;
174 HANDLE handle;
175 DWORD mode;
176 int cp;
177 size_t size;
178 struct
180 COORD coord;
181 unsigned int len;
182 WCHAR buf[1];
183 } write_characters;
184 struct
186 COORD size;
187 COORD coord;
188 SMALL_RECT region;
189 CHAR_INFO buf[1];
190 } write_output;
191 struct
193 SMALL_RECT rect;
194 COORD dst;
195 CHAR_INFO fill;
196 } scroll;
197 struct
199 WCHAR ch;
200 DWORD count;
201 COORD coord;
202 } fill;
203 } u;
206 static void child_string_request(enum req_type type, const WCHAR *title)
208 char buf[4096];
209 struct pseudoconsole_req *req = (void *)buf;
210 size_t len = lstrlenW(title) + 1;
211 DWORD count;
212 BOOL ret;
214 req->type = type;
215 memcpy(req->u.string, title, len * sizeof(WCHAR));
216 ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.string[len]),
217 &count, NULL);
218 ok(ret, "WriteFile failed: %u\n", GetLastError());
221 static void child_write_characters(const WCHAR *buf, unsigned int x, unsigned int y)
223 char req_buf[4096];
224 struct pseudoconsole_req *req = (void *)req_buf;
225 size_t len = lstrlenW(buf);
226 DWORD count;
227 BOOL ret;
229 req->type = REQ_WRITE_CHARACTERS;
230 req->u.write_characters.coord.X = x;
231 req->u.write_characters.coord.Y = y;
232 req->u.write_characters.len = len;
233 memcpy(req->u.write_characters.buf, buf, len * sizeof(WCHAR));
234 ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.write_characters.buf[len + 1]),
235 &count, NULL);
236 ok(ret, "WriteFile failed: %u\n", GetLastError());
239 static void child_set_cursor(const unsigned int x, unsigned int y)
241 struct pseudoconsole_req req;
242 DWORD count;
243 BOOL ret;
245 req.type = REQ_SET_CURSOR;
246 req.u.coord.X = x;
247 req.u.coord.Y = y;
248 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
249 ok(ret, "WriteFile failed: %u\n", GetLastError());
252 static HANDLE child_create_screen_buffer(void)
254 struct pseudoconsole_req req;
255 HANDLE handle;
256 DWORD count;
257 BOOL ret;
259 req.type = REQ_CREATE_SCREEN_BUFFER;
260 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
261 ok(ret, "WriteFile failed: %u\n", GetLastError());
262 ret = ReadFile(child_pipe, &handle, sizeof(handle), &count, NULL);
263 ok(ret, "ReadFile failed: %u\n", GetLastError());
264 return handle;
267 static void child_set_active(HANDLE handle)
269 struct pseudoconsole_req req;
270 DWORD count;
271 BOOL ret;
273 req.type = REQ_SET_ACTIVE;
274 req.u.handle = handle;
275 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
276 ok(ret, "WriteFile failed: %u\n", GetLastError());
279 #define child_write_output(a,b,c,d,e,f,g,h,j,k,l,m,n) child_write_output_(__LINE__,a,b,c,d,e,f,g,h,j,k,l,m,n)
280 static void child_write_output_(unsigned int line, CHAR_INFO *buf, unsigned int size_x, unsigned int size_y,
281 unsigned int coord_x, unsigned int coord_y, unsigned int left,
282 unsigned int top, unsigned int right, unsigned int bottom, unsigned int out_left,
283 unsigned int out_top, unsigned int out_right, unsigned int out_bottom)
285 char req_buf[4096];
286 struct pseudoconsole_req *req = (void *)req_buf;
287 SMALL_RECT region;
288 DWORD count;
289 BOOL ret;
291 req->type = REQ_WRITE_OUTPUT;
292 req->u.write_output.size.X = size_x;
293 req->u.write_output.size.Y = size_y;
294 req->u.write_output.coord.X = coord_x;
295 req->u.write_output.coord.Y = coord_y;
296 req->u.write_output.region.Left = left;
297 req->u.write_output.region.Top = top;
298 req->u.write_output.region.Right = right;
299 req->u.write_output.region.Bottom = bottom;
300 memcpy(req->u.write_output.buf, buf, size_x * size_y * sizeof(*buf));
301 ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.write_output.buf[size_x * size_y]), &count, NULL);
302 ok_(__FILE__,line)(ret, "WriteFile failed: %u\n", GetLastError());
303 ret = ReadFile(child_pipe, &region, sizeof(region), &count, NULL);
304 ok_(__FILE__,line)(ret, "WriteFile failed: %u\n", GetLastError());
305 ok_(__FILE__,line)(region.Left == out_left, "Left = %u\n", region.Left);
306 ok_(__FILE__,line)(region.Top == out_top, "Top = %u\n", region.Top);
307 ok_(__FILE__,line)(region.Right == out_right, "Right = %u\n", region.Right);
308 ok_(__FILE__,line)(region.Bottom == out_bottom, "Bottom = %u\n", region.Bottom);
311 static void child_scroll(unsigned int src_left, unsigned int src_top, unsigned int src_right,
312 unsigned int src_bottom, unsigned int dst_x, unsigned int dst_y, WCHAR fill)
314 struct pseudoconsole_req req;
315 DWORD count;
316 BOOL ret;
318 req.type = REQ_SCROLL;
319 req.u.scroll.rect.Left = src_left;
320 req.u.scroll.rect.Top = src_top;
321 req.u.scroll.rect.Right = src_right;
322 req.u.scroll.rect.Bottom = src_bottom;
323 req.u.scroll.dst.X = dst_x;
324 req.u.scroll.dst.Y = dst_y;
325 req.u.scroll.fill.Char.UnicodeChar = fill;
326 req.u.scroll.fill.Attributes = 0;
327 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
328 ok(ret, "WriteFile failed: %u\n", GetLastError());
331 static void child_fill_character(WCHAR ch, DWORD count, int x, int y)
333 struct pseudoconsole_req req;
334 BOOL ret;
336 req.type = REQ_FILL_CHAR;
337 req.u.fill.ch = ch;
338 req.u.fill.count = count;
339 req.u.fill.coord.X = x;
340 req.u.fill.coord.Y = y;
341 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
342 ok(ret, "WriteFile failed: %u\n", GetLastError());
345 static void child_set_input_mode(HANDLE pipe, DWORD mode)
347 struct pseudoconsole_req req;
348 DWORD count;
349 BOOL ret;
351 req.type = REQ_SET_INPUT_MODE;
352 req.u.mode = mode;
353 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
354 ok(ret, "WriteFile failed: %u\n", GetLastError());
357 static void child_set_output_mode(DWORD mode)
359 struct pseudoconsole_req req;
360 DWORD count;
361 BOOL ret;
363 req.type = REQ_SET_OUTPUT_MODE;
364 req.u.mode = mode;
365 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
366 ok(ret, "WriteFile failed: %u\n", GetLastError());
369 static void child_set_input_cp(int cp)
371 struct pseudoconsole_req req;
372 DWORD count;
373 BOOL ret;
375 req.type = REQ_SET_INPUT_CP;
376 req.u.cp = cp;
377 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
378 ok(ret, "WriteFile failed: %u\n", GetLastError());
381 static void child_read_console(HANDLE pipe, size_t size)
383 struct pseudoconsole_req req;
384 DWORD count;
385 BOOL ret;
387 req.type = REQ_READ_CONSOLE;
388 req.u.size = size;
389 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
390 ok(ret, "WriteFile failed: %u\n", GetLastError());
393 static void child_read_console_a(HANDLE pipe, size_t size)
395 struct pseudoconsole_req req;
396 DWORD count;
397 BOOL ret;
399 req.type = REQ_READ_CONSOLE_A;
400 req.u.size = size;
401 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
402 ok(ret, "WriteFile failed: %u\n", GetLastError());
405 static void child_read_console_file(HANDLE pipe, size_t size)
407 struct pseudoconsole_req req;
408 DWORD count;
409 BOOL ret;
411 req.type = REQ_READ_CONSOLE_FILE;
412 req.u.size = size;
413 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
414 ok(ret, "WriteFile failed: %u\n", GetLastError());
417 #define child_expect_read_result(a,b) child_expect_read_result_(__LINE__,a,b)
418 static void child_expect_read_result_(unsigned int line, HANDLE pipe, const WCHAR *expect)
420 size_t exlen = wcslen(expect);
421 WCHAR buf[4096];
422 DWORD count;
423 BOOL ret;
425 ret = ReadFile(pipe, buf, sizeof(buf), &count, NULL);
426 ok_(__FILE__,line)(ret, "ReadFile failed: %u\n", GetLastError());
427 ok_(__FILE__,line)(count == exlen * sizeof(WCHAR), "got %u, expected %u\n",
428 count, exlen * sizeof(WCHAR));
429 buf[count / sizeof(WCHAR)] = 0;
430 ok_(__FILE__,line)(!memcmp(expect, buf, count), "unexpected data %s\n", wine_dbgstr_w(buf));
433 #define child_expect_read_result_a(a,b) child_expect_read_result_a_(__LINE__,a,b)
434 static void child_expect_read_result_a_(unsigned int line, HANDLE pipe, const char *expect)
436 size_t exlen = strlen(expect);
437 char buf[4096];
438 DWORD count;
439 BOOL ret;
441 ret = ReadFile(pipe, buf, sizeof(buf), &count, NULL);
442 ok_(__FILE__,line)(ret, "ReadFile failed: %u\n", GetLastError());
443 todo_wine_if(exlen && expect[exlen - 1] == '\xcc')
444 ok_(__FILE__,line)(count == exlen, "got %u, expected %u\n", count, exlen);
445 buf[count] = 0;
446 ok_(__FILE__,line)(!memcmp(expect, buf, count), "unexpected data %s\n", wine_dbgstr_a(buf));
449 static void expect_input(unsigned int event_type, INPUT_RECORD *record)
451 struct pseudoconsole_req req = { REQ_GET_INPUT };
452 INPUT_RECORD input;
453 DWORD read;
454 BOOL ret;
456 ret = WriteFile(child_pipe, &req, sizeof(req), &read, NULL);
457 ok(ret, "WriteFile failed: %u\n", GetLastError());
459 ret = ReadFile(child_pipe, &input, sizeof(input), &read, NULL);
460 ok(ret, "ReadFile failed: %u\n", GetLastError());
462 ok(input.EventType == event_type, "EventType = %u, expected %u\n", input.EventType, event_type);
463 if (record) *record = input;
466 static BOOL get_key_input(unsigned int vt, INPUT_RECORD *record)
468 static INPUT_RECORD prev_record;
469 static BOOL have_prev_record;
471 if (!have_prev_record)
473 expect_input(KEY_EVENT, &prev_record);
474 have_prev_record = TRUE;
477 if (vt && prev_record.Event.KeyEvent.wVirtualKeyCode != vt) return FALSE;
478 *record = prev_record;
479 have_prev_record = FALSE;
480 return TRUE;
483 #define expect_key_input(a,b,c,d) expect_key_input_(__LINE__,0,a,b,c,d)
484 static void expect_key_input_(unsigned int line, unsigned int ctx, WCHAR ch, unsigned int vk,
485 BOOL down, unsigned int ctrl_state)
487 unsigned int vs = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC);
488 INPUT_RECORD record;
490 get_key_input(0, &record);
491 ok_(__FILE__,line)(record.Event.KeyEvent.bKeyDown == down, "%x: bKeyDown = %x\n",
492 ctx, record.Event.KeyEvent.bKeyDown);
493 ok_(__FILE__,line)(record.Event.KeyEvent.wRepeatCount == 1, "%x: wRepeatCount = %x\n",
494 ctx, record.Event.KeyEvent.wRepeatCount);
495 ok_(__FILE__,line)(record.Event.KeyEvent.uChar.UnicodeChar == ch, "%x: UnicodeChar = %x\n",
496 ctx, record.Event.KeyEvent.uChar.UnicodeChar);
497 ok_(__FILE__,line)(record.Event.KeyEvent.wVirtualKeyCode == vk,
498 "%x: wVirtualKeyCode = %x, expected %x\n", ctx,
499 record.Event.KeyEvent.wVirtualKeyCode, vk);
500 ok_(__FILE__,line)(record.Event.KeyEvent.wVirtualScanCode == vs,
501 "%x: wVirtualScanCode = %x expected %x\n", ctx,
502 record.Event.KeyEvent.wVirtualScanCode, vs);
503 ok_(__FILE__,line)(record.Event.KeyEvent.dwControlKeyState == ctrl_state,
504 "%x: dwControlKeyState = %x\n", ctx, record.Event.KeyEvent.dwControlKeyState);
507 #define get_input_key_vt() get_input_key_vt_(__LINE__)
508 static unsigned int get_input_key_vt_(unsigned int line)
510 INPUT_RECORD record;
512 get_key_input(0, &record);
513 ok_(__FILE__,line)(record.Event.KeyEvent.wRepeatCount == 1, "wRepeatCount = %x\n",
514 record.Event.KeyEvent.wRepeatCount);
515 return record.Event.KeyEvent.wVirtualKeyCode;
518 #define expect_key_pressed(a,b,c) expect_key_pressed_(__LINE__,0,a,b,c)
519 #define expect_key_pressed_ctx(a,b,c,d) expect_key_pressed_(__LINE__,a,b,c,d)
520 static void expect_key_pressed_(unsigned int line, unsigned int ctx, WCHAR ch, unsigned int vk,
521 unsigned int ctrl_state)
523 if (ctrl_state & SHIFT_PRESSED)
524 expect_key_input_(line, ctx, 0, VK_SHIFT, TRUE, SHIFT_PRESSED);
525 if (ctrl_state & LEFT_ALT_PRESSED)
526 expect_key_input_(line, ctx, 0, VK_MENU, TRUE,
527 LEFT_ALT_PRESSED | (ctrl_state & SHIFT_PRESSED));
528 if (ctrl_state & LEFT_CTRL_PRESSED)
529 expect_key_input_(line, ctx, 0, VK_CONTROL, TRUE,
530 LEFT_CTRL_PRESSED | (ctrl_state & (SHIFT_PRESSED | LEFT_ALT_PRESSED)));
531 expect_key_input_(line, ctx, ch, vk, TRUE, ctrl_state);
532 expect_key_input_(line, ctx, ch, vk, FALSE, ctrl_state);
533 if (ctrl_state & LEFT_CTRL_PRESSED)
534 expect_key_input_(line, ctx, 0, VK_CONTROL, FALSE,
535 ctrl_state & (SHIFT_PRESSED | LEFT_ALT_PRESSED));
536 if (ctrl_state & LEFT_ALT_PRESSED)
537 expect_key_input_(line, ctx, 0, VK_MENU, FALSE, ctrl_state & SHIFT_PRESSED);
538 if (ctrl_state & SHIFT_PRESSED)
539 expect_key_input_(line, ctx, 0, VK_SHIFT, FALSE, 0);
542 #define expect_char_key(a) expect_char_key_(__LINE__,a)
543 static void expect_char_key_(unsigned int line, WCHAR ch)
545 unsigned int ctrl = 0, vk;
546 vk = VkKeyScanW(ch);
547 if (vk == ~0) vk = 0;
548 if (vk & 0x0100) ctrl |= SHIFT_PRESSED;
549 if (vk & 0x0200) ctrl |= LEFT_CTRL_PRESSED;
550 vk &= 0xff;
551 expect_key_pressed_(line, ch, ch, vk, ctrl);
554 static void test_write_console(void)
556 child_string_request(REQ_WRITE_CONSOLE, L"abc");
557 skip_hide_cursor();
558 expect_output_sequence("abc");
559 skip_sequence("\x1b[?25h"); /* show cursor */
561 child_string_request(REQ_WRITE_CONSOLE, L"\tt");
562 skip_hide_cursor();
563 if (!skip_sequence("\x1b[3C")) expect_output_sequence(" ");
564 expect_output_sequence("t");
565 skip_sequence("\x1b[?25h"); /* show cursor */
566 expect_empty_output();
568 child_string_request(REQ_WRITE_CONSOLE, L"x\rr");
569 expect_hide_cursor();
570 expect_output_sequence("\rr abc tx");
571 if (!skip_sequence("\x1b[9D"))
572 expect_output_sequence("\x1b[4;2H"); /* set cursor */
573 expect_output_sequence("\x1b[?25h"); /* show cursor */
574 expect_empty_output();
576 child_string_request(REQ_WRITE_CONSOLE, L"yz\r\n");
577 skip_hide_cursor();
578 expect_output_sequence("yz\r\n");
579 skip_sequence("\x1b[?25h"); /* show cursor */
580 expect_empty_output();
582 child_string_request(REQ_WRITE_CONSOLE, L"abc\r\n123\r\ncde\r");
583 skip_hide_cursor();
584 expect_output_sequence("abc\r\n123\r\ncde\r");
585 skip_sequence("\x1b[?25h"); /* show cursor */
586 expect_empty_output();
588 child_set_cursor(0, 39);
589 expect_hide_cursor();
590 expect_output_sequence("\x1b[40;1H"); /* set cursor */
591 expect_output_sequence("\x1b[?25h"); /* show cursor */
592 expect_empty_output();
594 child_string_request(REQ_WRITE_CONSOLE, L"yz\r\n");
595 skip_hide_cursor();
596 expect_output_sequence("yz\r");
597 if (skip_sequence("\x1b[?25h")) /* show cursor */
598 expect_output_sequence("\x1b[?25l"); /* hide cursor */
599 expect_output_sequence("\n"); /* next line */
600 if (skip_sequence("\x1b[30X")) /* erase the line */
602 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
603 expect_output_sequence("\r");
605 skip_sequence("\x1b[?25h"); /* show cursor */
606 expect_empty_output();
608 child_string_request(REQ_WRITE_CONSOLE, L"");
609 expect_empty_output();
611 child_string_request(REQ_WRITE_CONSOLE, L"ab\n");
612 skip_hide_cursor();
613 expect_output_sequence("ab");
614 if (skip_sequence("\x1b[?25h")) /* show cursor */
615 expect_output_sequence("\x1b[?25l"); /* hide cursor */
616 expect_output_sequence("\r\n"); /* next line */
617 if (skip_sequence("\x1b[30X")) /* erase the line */
619 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
620 expect_output_sequence("\r");
622 skip_sequence("\x1b[?25h"); /* show cursor */
623 expect_empty_output();
625 child_set_cursor(28, 10);
626 expect_hide_cursor();
627 expect_output_sequence("\x1b[11;29H"); /* set cursor */
628 expect_output_sequence("\x1b[?25h"); /* show cursor */
629 expect_empty_output();
631 child_string_request(REQ_WRITE_CONSOLE, L"xy");
632 skip_hide_cursor();
633 expect_output_sequence("xy");
634 if (!skip_sequence("\b")) expect_output_sequence("\r\n");
635 skip_sequence("\x1b[?25h"); /* show cursor */
636 expect_empty_output();
638 child_set_cursor(28, 10);
639 fetch_console_output();
640 if (!skip_sequence("\b"))
642 expect_hide_cursor();
643 expect_output_sequence("\x1b[11;29H"); /* set cursor */
644 expect_output_sequence("\x1b[?25h"); /* show cursor */
646 expect_empty_output();
648 child_string_request(REQ_WRITE_CONSOLE, L"abc");
649 skip_hide_cursor();
650 expect_output_sequence("\r ab");
651 expect_output_sequence("\r\nc");
652 if (expect_erase_line(29))
653 expect_output_sequence("\x1b[12;2H"); /* set cursor */
654 skip_sequence("\x1b[?25h"); /* show cursor */
655 expect_empty_output();
657 child_set_cursor(28, 39);
658 expect_hide_cursor();
659 expect_output_sequence("\x1b[40;29H"); /* set cursor */
660 expect_output_sequence("\x1b[?25h"); /* show cursor */
661 expect_empty_output();
663 child_string_request(REQ_WRITE_CONSOLE, L"abc");
664 skip_hide_cursor();
665 expect_output_sequence("ab");
666 skip_sequence("\x1b[40;29H"); /* set cursor */
667 if (skip_sequence("\x1b[?25h")) /* show cursor */
668 expect_output_sequence("\x1b[?25l"); /* hide cursor */
669 else
670 skip_sequence("\b");
671 expect_output_sequence("\r\nc");
672 if (skip_sequence("\x1b[29X")) /* erase the line */
674 expect_output_sequence("\x1b[29C"); /* move cursor to the end of the line */
675 expect_output_sequence("\x1b[40;2H"); /* set cursor */
677 skip_sequence("\x1b[?25h"); /* show cursor */
678 expect_empty_output();
680 child_set_cursor(28, 39);
681 skip_hide_cursor();
682 if (!skip_sequence("\x1b[27C"))
683 expect_output_sequence("\x1b[40;29H"); /* set cursor */
684 skip_sequence("\x1b[?25h"); /* show cursor */
685 expect_empty_output();
687 child_string_request(REQ_WRITE_CONSOLE, L"XY");
688 skip_hide_cursor();
689 expect_output_sequence("XY");
690 skip_sequence("\x1b[40;29H"); /* set cursor */
691 if (skip_sequence("\x1b[?25h")) /* show cursor */
692 expect_output_sequence("\x1b[?25l"); /* hide cursor */
693 if (!skip_sequence("\b"))
695 expect_output_sequence("\r\n");
696 expect_output_sequence("\x1b[30X"); /* erase the line */
697 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
698 expect_output_sequence("\r"); /* set cursor */
700 skip_sequence("\x1b[?25h"); /* show cursor */
701 expect_empty_output();
703 child_string_request(REQ_WRITE_CONSOLE, L"\n");
704 skip_hide_cursor();
705 if (!skip_sequence("\r\n"))
707 expect_output_sequence("\n");
708 expect_output_sequence("\x1b[30X"); /* erase the line */
709 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
710 expect_output_sequence("\r"); /* set cursor */
712 skip_sequence("\x1b[?25h"); /* show cursor */
713 expect_empty_output();
715 child_set_output_mode(ENABLE_PROCESSED_OUTPUT);
717 child_set_cursor(28, 11);
718 expect_hide_cursor();
719 expect_output_sequence("\x1b[12;29H"); /* set cursor */
720 skip_sequence("\x1b[?25h"); /* show cursor */
722 child_string_request(REQ_WRITE_CONSOLE, L"xyz1234");
723 skip_hide_cursor();
724 expect_output_sequence("43\b");
725 skip_sequence("\x1b[?25h"); /* show cursor */
726 expect_empty_output();
728 child_set_cursor(28, 11);
729 skip_hide_cursor();
730 expect_output_sequence("\b"); /* backspace */
731 skip_sequence("\x1b[?25h"); /* show cursor */
733 child_string_request(REQ_WRITE_CONSOLE, L"xyz123");
734 expect_hide_cursor();
735 expect_output_sequence("23");
736 if (!skip_sequence("\x1b[2D"))
737 expect_output_sequence("\x1b[12;29H");/* set cursor */
738 expect_output_sequence("\x1b[?25h"); /* show cursor */
739 expect_empty_output();
741 child_set_cursor(28, 11);
742 child_string_request(REQ_WRITE_CONSOLE, L"abcdef\n\r123456789012345678901234567890xyz");
743 expect_hide_cursor();
744 if (skip_sequence("\x1b[?25h")) expect_hide_cursor();
745 expect_output_sequence("\r ef\r\n");
746 expect_output_sequence("xyz456789012345678901234567890");
747 if (!skip_sequence("\x1b[27D"))
748 expect_output_sequence("\x1b[13;4H"); /* set cursor */
749 expect_output_sequence("\x1b[?25h"); /* show cursor */
750 expect_empty_output();
752 child_set_cursor(28, 11);
753 expect_hide_cursor();
754 expect_output_sequence("\x1b[12;29H"); /* set cursor */
755 expect_output_sequence("\x1b[?25h"); /* show cursor */
757 child_string_request(REQ_WRITE_CONSOLE, L"AB\r\n");
758 skip_hide_cursor();
759 expect_output_sequence("AB\r\n");
760 skip_sequence("\x1b[?25h"); /* show cursor */
761 expect_empty_output();
763 child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
766 static void test_tty_output(void)
768 CHAR_INFO char_info_buf[2048], char_info;
769 HANDLE sb, sb2;
770 unsigned int i;
772 /* simple write chars */
773 child_write_characters(L"child", 3, 4);
774 expect_hide_cursor();
775 expect_output_sequence("\x1b[5;4H"); /* set cursor */
776 expect_output_sequence("child");
777 expect_output_sequence("\x1b[H"); /* set cursor */
778 expect_output_sequence("\x1b[?25h"); /* show cursor */
779 expect_empty_output();
781 /* wrapped write chars */
782 child_write_characters(L"bound", 28, 6);
783 expect_hide_cursor();
784 expect_output_sequence("\x1b[7;1H"); /* set cursor */
785 expect_output_sequence(" bo\r\nund");
786 expect_erase_line(27);
787 expect_output_sequence("\x1b[H"); /* set cursor */
788 expect_output_sequence("\x1b[?25h"); /* show cursor */
789 expect_empty_output();
791 /* fill line 4 with a few simple writes */
792 child_write_characters(L"xxx", 13, 4);
793 expect_hide_cursor();
794 expect_output_sequence("\x1b[5;14H"); /* set cursor */
795 expect_output_sequence("xxx");
796 expect_output_sequence("\x1b[H"); /* set cursor */
797 expect_output_sequence("\x1b[?25h"); /* show cursor */
798 expect_empty_output();
800 /* write one char at the end of row */
801 child_write_characters(L"y", 29, 4);
802 expect_hide_cursor();
803 expect_output_sequence("\x1b[5;30H"); /* set cursor */
804 expect_output_sequence("y");
805 expect_output_sequence("\x1b[H"); /* set cursor */
806 expect_output_sequence("\x1b[?25h"); /* show cursor */
807 expect_empty_output();
809 /* wrapped write chars */
810 child_write_characters(L"zz", 29, 4);
811 expect_hide_cursor();
812 expect_output_sequence("\x1b[5;1H"); /* set cursor */
813 expect_output_sequence(" child xxx z");
814 expect_output_sequence("\r\nz");
815 expect_erase_line(29);
816 expect_output_sequence("\x1b[H"); /* set cursor */
817 expect_output_sequence("\x1b[?25h"); /* show cursor */
818 expect_empty_output();
820 /* trailing spaces */
821 child_write_characters(L"child ", 3, 4);
822 expect_hide_cursor();
823 expect_output_sequence("\x1b[5;4H"); /* set cursor */
824 expect_output_sequence("child ");
825 expect_output_sequence("\x1b[H"); /* set cursor */
826 expect_output_sequence("\x1b[?25h"); /* show cursor */
827 expect_empty_output();
829 child_set_cursor(2, 3);
830 expect_hide_cursor();
831 expect_output_sequence("\x1b[4;3H"); /* set cursor */
832 expect_output_sequence("\x1b[?25h"); /* show cursor */
833 expect_empty_output();
835 child_string_request(REQ_SET_TITLE, L"new title");
836 fetch_console_output();
837 skip_sequence("\x1b[?25l"); /* hide cursor */
838 expect_output_sequence("\x1b]0;new title\x07"); /* set title */
839 skip_sequence("\x1b[?25h"); /* show cursor */
840 expect_empty_output();
842 for (i = 0; i < ARRAY_SIZE(char_info_buf); i++)
844 char_info_buf[i].Char.UnicodeChar = '0' + i % 10;
845 char_info_buf[i].Attributes = 0;
848 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 1, 2,
849 /* region */ 3, 7, 5, 9, /* out region */ 3, 7, 5, 9);
850 expect_hide_cursor();
851 expect_output_sequence("\x1b[30m"); /* foreground black */
852 expect_output_sequence("\x1b[8;4H"); /* set cursor */
853 expect_output_sequence("567");
854 expect_output_sequence("\x1b[9;4H"); /* set cursor */
855 expect_output_sequence("234");
856 expect_output_sequence("\x1b[10;4H"); /* set cursor */
857 expect_output_sequence("901");
858 expect_output_sequence("\x1b[4;3H"); /* set cursor */
859 expect_output_sequence("\x1b[?25h"); /* show cursor */
860 expect_empty_output();
862 child_write_output(char_info_buf, /* size */ 2, 3, /* coord */ 1, 2,
863 /* region */ 3, 8, 15, 19, /* out region */ 3, 8, 3, 8);
864 expect_hide_cursor();
865 if (skip_sequence("\x1b[m")) /* default attr */
866 expect_output_sequence("\x1b[30m");/* foreground black */
867 expect_output_sequence("\x1b[9;4H"); /* set cursor */
868 expect_output_sequence("5");
869 expect_output_sequence("\x1b[4;3H"); /* set cursor */
870 expect_output_sequence("\x1b[?25h"); /* show cursor */
871 expect_empty_output();
873 child_write_output(char_info_buf, /* size */ 3, 4, /* coord */ 1, 2,
874 /* region */ 3, 8, 15, 19, /* out region */ 3, 8, 4, 9);
875 expect_hide_cursor();
876 if (skip_sequence("\x1b[m")) /* default attr */
877 expect_output_sequence("\x1b[30m");/* foreground black */
878 expect_output_sequence("\x1b[9;4H"); /* set cursor */
879 expect_output_sequence("78");
880 expect_output_sequence("\x1b[10;4H"); /* set cursor */
881 expect_output_sequence("01");
882 expect_output_sequence("\x1b[4;3H"); /* set cursor */
883 expect_output_sequence("\x1b[?25h"); /* show cursor */
884 expect_empty_output();
886 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 2, 3,
887 /* region */ 28, 38, 31, 60, /* out region */ 28, 38, 29, 39);
888 expect_hide_cursor();
889 if (skip_sequence("\x1b[m")) /* default attr */
890 expect_output_sequence("\x1b[30m");/* foreground black */
891 expect_output_sequence("\x1b[39;29H"); /* set cursor */
892 expect_output_sequence("34");
893 expect_output_sequence("\x1b[40;29H"); /* set cursor */
894 expect_output_sequence("01");
895 expect_output_sequence("\x1b[4;3H"); /* set cursor */
896 expect_output_sequence("\x1b[?25h"); /* show cursor */
897 expect_empty_output();
899 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 1, 2,
900 /* region */ 0, 7, 5, 9, /* out region */ 0, 7, 5, 9);
901 expect_hide_cursor();
902 if (skip_sequence("\x1b[m")) /* default attr */
903 expect_output_sequence("\x1b[30m");/* foreground black */
904 expect_output_sequence("\x1b[8;1H"); /* set cursor */
905 expect_output_sequence("567890\r\n");
906 expect_output_sequence("234567\r\n");
907 expect_output_sequence("901234");
908 expect_output_sequence("\x1b[4;3H"); /* set cursor */
909 expect_output_sequence("\x1b[?25h"); /* show cursor */
910 expect_empty_output();
912 child_scroll(/* scroll rect */ 0, 7, 2, 8, /* destination */ 2, 8, /* fill */ 'x');
913 expect_hide_cursor();
914 if (skip_sequence("\x1b[m")) /* default attr */
915 expect_output_sequence("\x1b[30m");/* foreground black */
916 expect_output_sequence("\x1b[8;1H"); /* set cursor */
917 expect_output_sequence("xxx89\r\n");
918 expect_output_sequence("xx567\r\n");
919 expect_output_sequence("90234");
920 expect_output_sequence("\x1b[4;3H"); /* set cursor */
921 expect_output_sequence("\x1b[?25h"); /* show cursor */
922 expect_empty_output();
924 child_write_characters(L"xxx", 3, 10);
925 expect_hide_cursor();
926 expect_output_sequence("\x1b[m"); /* default attributes */
927 expect_output_sequence("\x1b[11;4H"); /* set cursor */
928 expect_output_sequence("xxx");
929 expect_output_sequence("\x1b[4;3H"); /* set cursor */
930 expect_output_sequence("\x1b[?25h"); /* show cursor */
931 expect_empty_output();
933 /* test attributes */
934 for (i = 0; i < 0x100 - 0xff; i++)
936 unsigned int expect;
937 char expect_buf[16];
938 char_info.Char.UnicodeChar = 'a';
939 char_info.Attributes = i;
940 child_write_output(&char_info, /* size */ 1, 1, /* coord */ 0, 0,
941 /* region */ 12, 3, 12, 3, /* out region */ 12, 3, 12, 3);
942 expect_hide_cursor();
943 if (i != 0x190 && i && ((i & 0xff) != 8)) expect_output_sequence_ctx(i, "\x1b[m");
944 if ((i & 0x0f) != 7)
946 expect = 30;
947 if (i & FOREGROUND_BLUE) expect += 4;
948 if (i & FOREGROUND_GREEN) expect += 2;
949 if (i & FOREGROUND_RED) expect += 1;
950 if (i & FOREGROUND_INTENSITY) expect += 60;
951 sprintf(expect_buf, "\x1b[%um", expect);
952 expect_output_sequence_ctx(i, expect_buf);
954 if (i & 0xf0)
956 expect = 40;
957 if (i & BACKGROUND_BLUE) expect += 4;
958 if (i & BACKGROUND_GREEN) expect += 2;
959 if (i & BACKGROUND_RED) expect += 1;
960 if (i & BACKGROUND_INTENSITY) expect += 60;
961 sprintf(expect_buf, "\x1b[%um", expect);
962 expect_output_sequence_ctx(i, expect_buf);
964 if (!skip_sequence("\x1b[10C"))
965 expect_output_sequence_ctx(i, "\x1b[4;13H"); /* set cursor */
966 expect_output_sequence("a");
967 if (!skip_sequence("\x1b[11D"))
968 expect_output_sequence("\x1b[4;3H"); /* set cursor */
969 expect_output_sequence("\x1b[?25h"); /* show cursor */
970 expect_empty_output();
973 char_info_buf[0].Attributes = FOREGROUND_GREEN;
974 char_info_buf[1].Attributes = FOREGROUND_GREEN | BACKGROUND_RED;
975 char_info_buf[2].Attributes = BACKGROUND_RED;
976 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 0, 0,
977 /* region */ 7, 0, 9, 0, /* out region */ 7, 0, 9, 0);
978 expect_hide_cursor();
979 skip_sequence("\x1b[m"); /* default attr */
980 expect_output_sequence("\x1b[32m"); /* foreground black */
981 expect_output_sequence("\x1b[1;8H"); /* set cursor */
982 expect_output_sequence("0");
983 expect_output_sequence("\x1b[41m"); /* background red */
984 expect_output_sequence("1");
985 expect_output_sequence("\x1b[30m"); /* foreground black */
986 expect_output_sequence("2");
987 expect_output_sequence("\x1b[4;3H"); /* set cursor */
988 expect_output_sequence("\x1b[?25h"); /* show cursor */
989 expect_empty_output();
991 child_fill_character('i', 5, 15, 16);
992 expect_hide_cursor();
993 expect_output_sequence("\x1b[m"); /* default attributes */
994 expect_output_sequence("\x1b[17;16H"); /* set cursor */
995 expect_output_sequence("iiiii");
996 expect_output_sequence("\x1b[4;3H"); /* set cursor */
997 expect_output_sequence("\x1b[?25h"); /* show cursor */
998 expect_empty_output();
1000 test_write_console();
1002 sb = child_create_screen_buffer();
1003 child_set_active(sb);
1004 expect_hide_cursor();
1005 expect_output_sequence("\x1b[H"); /* set cursor */
1006 for (i = 0; i < 40; i++)
1008 expect_erase_line(30);
1009 if (i != 39) expect_output_sequence("\r\n");
1011 expect_output_sequence("\x1b[H"); /* set cursor */
1012 expect_output_sequence("\x1b[?25h"); /* show cursor */
1013 expect_empty_output();
1015 child_write_characters(L"new sb", 0, 0);
1016 skip_hide_cursor();
1017 expect_output_sequence("new sb");
1018 ok(skip_sequence("\x1b[H") || skip_sequence("\r"), "expected set cursor\n");
1019 skip_sequence("\x1b[?25h"); /* show cursor */
1020 expect_empty_output();
1022 sb2 = child_create_screen_buffer();
1023 child_set_active(sb2);
1024 expect_hide_cursor();
1025 for (i = 0; i < 40; i++)
1027 expect_erase_line(30);
1028 if (i != 39) expect_output_sequence("\r\n");
1030 expect_output_sequence("\x1b[H"); /* set cursor */
1031 expect_output_sequence("\x1b[?25h"); /* show cursor */
1032 expect_empty_output();
1034 child_set_active(sb);
1035 expect_hide_cursor();
1036 expect_output_sequence("new sb");
1037 expect_erase_line(24);
1038 expect_output_sequence("\r\n");
1039 for (i = 1; i < 40; i++)
1041 expect_erase_line(30);
1042 if (i != 39) expect_output_sequence("\r\n");
1044 expect_output_sequence("\x1b[H"); /* set cursor */
1045 expect_output_sequence("\x1b[?25h"); /* show cursor */
1046 expect_empty_output();
1049 static void write_console_pipe(const char *buf)
1051 DWORD written;
1052 BOOL res;
1053 res = WriteFile(console_pipe, buf, strlen(buf), &written, NULL);
1054 ok(res, "WriteFile failed: %u\n", GetLastError());
1057 static void test_read_console(void)
1059 child_set_input_mode(child_pipe, ENABLE_PROCESSED_INPUT);
1061 child_read_console(child_pipe, 100);
1062 write_console_pipe("abc");
1063 expect_empty_output();
1064 child_expect_read_result(child_pipe, L"abc");
1065 expect_empty_output();
1067 child_read_console(child_pipe, 1);
1068 write_console_pipe("xyz");
1069 child_expect_read_result(child_pipe, L"x");
1070 child_read_console(child_pipe, 100);
1071 child_expect_read_result(child_pipe, L"yz");
1072 expect_empty_output();
1074 child_set_input_cp(932);
1076 child_read_console_a(child_pipe, 2);
1077 write_console_pipe("\xe3\x81\x81");
1078 child_expect_read_result_a(child_pipe, "\x82\x9f");
1079 expect_empty_output();
1081 child_read_console_a(child_pipe, 1);
1082 write_console_pipe("\xe3\x81\x81""a");
1083 child_expect_read_result_a(child_pipe, "\x82\xcc");
1084 child_read_console_a(child_pipe, 1);
1085 child_expect_read_result_a(child_pipe, "a");
1086 expect_empty_output();
1088 child_read_console_a(child_pipe, 2);
1089 write_console_pipe("a\xe3\x81\x81""b");
1090 child_expect_read_result_a(child_pipe, "a\x82\xcc");
1091 child_read_console_a(child_pipe, 1);
1092 child_expect_read_result_a(child_pipe, "b");
1093 expect_empty_output();
1095 child_read_console_file(child_pipe, 2);
1096 write_console_pipe("\xe3\x81\x81");
1097 child_expect_read_result_a(child_pipe, "\x82\x9f");
1098 expect_empty_output();
1100 child_read_console_file(child_pipe, 1);
1101 write_console_pipe("\xe3\x81\x81""a");
1102 child_expect_read_result_a(child_pipe, "\x82\xcc");
1103 child_read_console_file(child_pipe, 1);
1104 child_expect_read_result_a(child_pipe, "a");
1105 expect_empty_output();
1107 child_read_console_file(child_pipe, 2);
1108 write_console_pipe("a\xe3\x81\x81""b");
1109 child_expect_read_result_a(child_pipe, "a\x82\xcc");
1110 child_read_console_file(child_pipe, 1);
1111 child_expect_read_result_a(child_pipe, "b");
1112 expect_empty_output();
1114 child_set_input_cp(437);
1116 child_set_input_mode(child_pipe, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
1117 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
1118 ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION);
1120 child_read_console(child_pipe, 100);
1121 write_console_pipe("xyz");
1122 skip_hide_cursor();
1123 expect_output_sequence("xyz");
1124 skip_sequence("\x1b[?25h"); /* show cursor */
1125 write_console_pipe("ab\r\n");
1126 child_expect_read_result(child_pipe, L"xyzab\r\n");
1127 skip_hide_cursor();
1128 expect_output_sequence("ab\r\n");
1129 skip_sequence("\x1b[?25h"); /* show cursor */
1130 expect_key_input('\r', VK_RETURN, 0, FALSE);
1131 expect_key_pressed('\n', VK_RETURN, LEFT_CTRL_PRESSED);
1132 expect_empty_output();
1135 static void test_tty_input(void)
1137 INPUT_RECORD ir;
1138 unsigned int i;
1139 char buf[8];
1141 static const struct
1143 const char *str;
1144 WCHAR ch;
1145 unsigned int vk;
1146 unsigned int ctrl;
1147 } escape_test[] = {
1148 { "\x1b[A", 0, VK_UP, 0 },
1149 { "\x1b[B", 0, VK_DOWN, 0 },
1150 { "\x1b[C", 0, VK_RIGHT, 0 },
1151 { "\x1b[D", 0, VK_LEFT, 0 },
1152 { "\x1b[H", 0, VK_HOME, 0 },
1153 { "\x1b[F", 0, VK_END, 0 },
1154 { "\x1b[2~", 0, VK_INSERT, 0 },
1155 { "\x1b[3~", 0, VK_DELETE, 0 },
1156 { "\x1b[5~", 0, VK_PRIOR, 0 },
1157 { "\x1b[6~", 0, VK_NEXT, 0 },
1158 { "\x1b[15~", 0, VK_F5, 0 },
1159 { "\x1b[17~", 0, VK_F6, 0 },
1160 { "\x1b[18~", 0, VK_F7, 0 },
1161 { "\x1b[19~", 0, VK_F8, 0 },
1162 { "\x1b[20~", 0, VK_F9, 0 },
1163 { "\x1b[21~", 0, VK_F10, 0 },
1164 /* 0x10 */
1165 { "\x1b[23~", 0, VK_F11, 0 },
1166 { "\x1b[24~", 0, VK_F12, 0 },
1167 { "\x1bOP", 0, VK_F1, 0 },
1168 { "\x1bOQ", 0, VK_F2, 0 },
1169 { "\x1bOR", 0, VK_F3, 0 },
1170 { "\x1bOS", 0, VK_F4, 0 },
1171 { "\x1b[1;1A", 0, VK_UP, 0 },
1172 { "\x1b[1;2A", 0, VK_UP, SHIFT_PRESSED },
1173 { "\x1b[1;3A", 0, VK_UP, LEFT_ALT_PRESSED },
1174 { "\x1b[1;4A", 0, VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED },
1175 { "\x1b[1;5A", 0, VK_UP, LEFT_CTRL_PRESSED },
1176 { "\x1b[1;6A", 0, VK_UP, SHIFT_PRESSED | LEFT_CTRL_PRESSED },
1177 { "\x1b[1;7A", 0, VK_UP, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1178 { "\x1b[1;8A", 0, VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1179 { "\x1b[1;9A", 0, VK_UP, 0 },
1180 { "\x1b[1;10A", 0, VK_UP, SHIFT_PRESSED },
1181 /* 0x20 */
1182 { "\x1b[1;11A", 0, VK_UP, LEFT_ALT_PRESSED },
1183 { "\x1b[1;12A", 0, VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED },
1184 { "\x1b[1;13A", 0, VK_UP, LEFT_CTRL_PRESSED },
1185 { "\x1b[1;14A", 0, VK_UP, SHIFT_PRESSED | LEFT_CTRL_PRESSED },
1186 { "\x1b[1;15A", 0, VK_UP, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1187 { "\x1b[1;16A", 0, VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1188 { "\x1b[1;2P", 0, VK_F1, SHIFT_PRESSED },
1189 { "\x1b[2;3~", 0, VK_INSERT, LEFT_ALT_PRESSED },
1190 { "\x1b[2;3;5;6~", 0, VK_INSERT, 0 },
1191 { "\x1b[6;2;3;5;1~", 0, VK_NEXT, 0 },
1192 { "\xe4\xb8\x80", 0x4e00, 0, 0 },
1193 { "\x1b\x1b", 0x1b, VK_ESCAPE, LEFT_ALT_PRESSED },
1194 { "\x1b""1", '1', '1', LEFT_ALT_PRESSED },
1195 { "\x1b""x", 'x', 'X', LEFT_ALT_PRESSED },
1196 { "\x1b""[", '[', VK_OEM_4, LEFT_ALT_PRESSED },
1197 { "\x7f", '\b', VK_BACK, 0 },
1200 write_console_pipe("x");
1201 if (!get_input_key_vt())
1203 skip("Skipping tests on settings that don't have VT mapping for 'x'\n");
1204 get_input_key_vt();
1205 return;
1207 get_input_key_vt();
1209 write_console_pipe("aBCd");
1210 expect_char_key('a');
1211 expect_char_key('B');
1212 expect_char_key('C');
1213 expect_char_key('d');
1215 for (i = 1; i < 0x7f; i++)
1217 if (i == 3 || i == '\n' || i == 0x1b || i == 0x1f) continue;
1218 buf[0] = i;
1219 buf[1] = 0;
1220 write_console_pipe(buf);
1221 if (i == 8)
1222 expect_key_pressed('\b', 'H', LEFT_CTRL_PRESSED);
1223 else if (i == 0x7f)
1224 expect_char_key(8);
1225 else
1226 expect_char_key(i);
1229 write_console_pipe("\r\n");
1230 expect_key_pressed('\r', VK_RETURN, 0);
1231 expect_key_pressed('\n', VK_RETURN, LEFT_CTRL_PRESSED);
1233 write_console_pipe("\xc4\x85");
1234 if (get_key_input(VK_MENU, &ir))
1236 expect_key_input(0x105, 'A', TRUE, LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
1237 expect_key_input(0x105, 'A', FALSE, LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
1238 expect_key_input(0, VK_MENU, FALSE, ENHANCED_KEY);
1240 else
1242 expect_key_input(0x105, 0, TRUE, 0);
1243 expect_key_input(0x105, 0, FALSE, 0);
1246 for (i = 0; i < ARRAY_SIZE(escape_test); i++)
1248 write_console_pipe(escape_test[i].str);
1249 expect_key_pressed_ctx(i, escape_test[i].ch, escape_test[i].vk, escape_test[i].ctrl);
1252 for (i = 0x80; i < 0x100; i += 11)
1254 buf[0] = i;
1255 buf[1] = 0;
1256 write_console_pipe(buf);
1257 expect_empty_output();
1261 static void child_process(HANDLE pipe)
1263 HANDLE output, input;
1264 DWORD size, count;
1265 char buf[4096];
1266 BOOL ret;
1268 output = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1269 ok(output != INVALID_HANDLE_VALUE, "could not open console output\n");
1271 input = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1272 ok(output != INVALID_HANDLE_VALUE, "could not open console output\n");
1274 while(ReadFile(pipe, buf, sizeof(buf), &size, NULL))
1276 const struct pseudoconsole_req *req = (void *)buf;
1277 switch (req->type)
1279 case REQ_CREATE_SCREEN_BUFFER:
1281 HANDLE handle;
1282 SetLastError(0xdeadbeef);
1283 handle = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
1284 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1285 CONSOLE_TEXTMODE_BUFFER, NULL);
1286 ok(handle != INVALID_HANDLE_VALUE, "CreateConsoleScreenBuffer failed: %u\n", GetLastError());
1287 ret = WriteFile(pipe, &handle, sizeof(handle), &count, NULL);
1288 ok(ret, "WriteFile failed: %u\n", GetLastError());
1289 break;
1292 case REQ_GET_INPUT:
1294 INPUT_RECORD record;
1295 ret = ReadConsoleInputW(input, &record, 1, &count);
1296 ok(ret, "ReadConsoleInputW failed: %u\n", GetLastError());
1297 ok(count == 1, "count = %u\n", count);
1298 ret = WriteFile(pipe, &record, sizeof(record), &count, NULL);
1299 ok(ret, "WriteFile failed: %u\n", GetLastError());
1300 break;
1303 case REQ_READ_CONSOLE:
1304 ret = ReadConsoleW(input, buf, req->u.size, &count, NULL );
1305 ok(ret, "ReadConsoleW failed: %u\n", GetLastError());
1306 ret = WriteFile(pipe, buf, count * sizeof(WCHAR), NULL, NULL);
1307 ok(ret, "WriteFile failed: %u\n", GetLastError());
1308 break;
1310 case REQ_READ_CONSOLE_A:
1311 count = req->u.size;
1312 memset(buf, 0xcc, sizeof(buf));
1313 ret = ReadConsoleA(input, buf, count, &count, NULL );
1314 ok(ret, "ReadConsoleA failed: %u\n", GetLastError());
1315 ret = WriteFile(pipe, buf, count, NULL, NULL);
1316 ok(ret, "WriteFile failed: %u\n", GetLastError());
1317 break;
1319 case REQ_READ_CONSOLE_FILE:
1320 count = req->u.size;
1321 memset(buf, 0xcc, sizeof(buf));
1322 ret = ReadFile(input, buf, count, &count, NULL );
1323 ok(ret, "ReadFile failed: %u\n", GetLastError());
1324 ret = WriteFile(pipe, buf, count, NULL, NULL);
1325 ok(ret, "WriteFile failed: %u\n", GetLastError());
1326 break;
1328 case REQ_SCROLL:
1329 ret = ScrollConsoleScreenBufferW(output, &req->u.scroll.rect, NULL, req->u.scroll.dst, &req->u.scroll.fill);
1330 ok(ret, "ScrollConsoleScreenBuffer failed: %u\n", GetLastError());
1331 break;
1333 case REQ_FILL_CHAR:
1334 ret = FillConsoleOutputCharacterW(output, req->u.fill.ch, req->u.fill.count, req->u.fill.coord, &count);
1335 ok(ret, "FillConsoleOutputCharacter failed: %u\n", GetLastError());
1336 ok(count == req->u.fill.count, "count = %u, expected %u\n", count, req->u.fill.count);
1337 break;
1339 case REQ_SET_ACTIVE:
1340 output = req->u.handle;
1341 ret = SetConsoleActiveScreenBuffer(output);
1342 ok(ret, "SetConsoleActiveScreenBuffer failed: %u\n", GetLastError());
1343 break;
1345 case REQ_SET_CURSOR:
1346 ret = SetConsoleCursorPosition(output, req->u.coord);
1347 ok(ret, "SetConsoleCursorPosition failed: %u\n", GetLastError());
1348 break;
1350 case REQ_SET_INPUT_CP:
1351 ret = SetConsoleCP(req->u.cp);
1352 ok(ret, "SetConsoleCP failed: %u\n", GetLastError());
1353 break;
1355 case REQ_SET_INPUT_MODE:
1356 ret = SetConsoleMode(input, req->u.mode);
1357 ok(ret, "SetConsoleMode failed: %u\n", GetLastError());
1358 break;
1360 case REQ_SET_OUTPUT_MODE:
1361 ret = SetConsoleMode(output, req->u.mode);
1362 ok(ret, "SetConsoleMode failed: %u\n", GetLastError());
1363 break;
1365 case REQ_SET_TITLE:
1366 ret = SetConsoleTitleW(req->u.string);
1367 ok(ret, "SetConsoleTitleW failed: %u\n", GetLastError());
1368 break;
1370 case REQ_WRITE_CHARACTERS:
1371 ret = WriteConsoleOutputCharacterW(output, req->u.write_characters.buf,
1372 req->u.write_characters.len,
1373 req->u.write_characters.coord, &count);
1374 ok(ret, "WriteConsoleOutputCharacterW failed: %u\n", GetLastError());
1375 break;
1377 case REQ_WRITE_CONSOLE:
1378 ret = WriteConsoleW(output, req->u.string, lstrlenW(req->u.string), NULL, NULL);
1379 ok(ret, "SetConsoleTitleW failed: %u\n", GetLastError());
1380 break;
1382 case REQ_WRITE_OUTPUT:
1384 SMALL_RECT region = req->u.write_output.region;
1385 ret = WriteConsoleOutputW(output, req->u.write_output.buf, req->u.write_output.size, req->u.write_output.coord, &region);
1386 ok(ret, "WriteConsoleOutput failed: %u\n", GetLastError());
1387 ret = WriteFile(pipe, &region, sizeof(region), &count, NULL);
1388 ok(ret, "WriteFile failed: %u\n", GetLastError());
1389 break;
1392 default:
1393 ok(0, "unexpected request type %u\n", req->type);
1396 ok(GetLastError() == ERROR_BROKEN_PIPE, "ReadFile failed: %u\n", GetLastError());
1397 CloseHandle(output);
1398 CloseHandle(input);
1401 static HANDLE run_child(HANDLE console, HANDLE pipe)
1403 STARTUPINFOEXA startup = {{ sizeof(startup) }};
1404 char **argv, cmdline[MAX_PATH];
1405 PROCESS_INFORMATION info;
1406 SIZE_T size;
1407 BOOL ret;
1409 InitializeProcThreadAttributeList(NULL, 1, 0, &size);
1410 startup.lpAttributeList = HeapAlloc(GetProcessHeap(), 0, size);
1411 InitializeProcThreadAttributeList(startup.lpAttributeList, 1, 0, &size);
1412 UpdateProcThreadAttribute(startup.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, console,
1413 sizeof(console), NULL, NULL);
1415 winetest_get_mainargs(&argv);
1416 sprintf(cmdline, "\"%s\" %s child %p", argv[0], argv[1], pipe);
1417 ret = CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
1418 &startup.StartupInfo, &info);
1419 ok(ret, "CreateProcessW failed: %u\n", GetLastError());
1421 CloseHandle(info.hThread);
1422 HeapFree(GetProcessHeap(), 0, startup.lpAttributeList);
1423 return info.hProcess;
1426 static HPCON create_pseudo_console(HANDLE *console_pipe_end, HANDLE *child_process)
1428 SECURITY_ATTRIBUTES sec_attr = { sizeof(sec_attr), NULL, TRUE };
1429 HANDLE child_pipe_end;
1430 COORD size = { 30, 40 };
1431 DWORD read_mode;
1432 HPCON console;
1433 HRESULT hres;
1434 BOOL r;
1436 console_pipe = CreateNamedPipeW(L"\\\\.\\pipe\\pseudoconsoleconn", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
1437 PIPE_WAIT | PIPE_TYPE_BYTE, 1, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL);
1438 ok(console_pipe != INVALID_HANDLE_VALUE, "CreateNamedPipeW failed: %u\n", GetLastError());
1440 *console_pipe_end = CreateFileW(L"\\\\.\\pipe\\pseudoconsoleconn", GENERIC_READ | GENERIC_WRITE,
1441 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
1442 ok(*console_pipe_end != INVALID_HANDLE_VALUE, "CreateFile failed: %u\n", GetLastError());
1444 child_pipe = CreateNamedPipeW(L"\\\\.\\pipe\\pseudoconsoleserver", PIPE_ACCESS_DUPLEX,
1445 PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 5000, 6000,
1446 NMPWAIT_USE_DEFAULT_WAIT, NULL);
1447 ok(child_pipe != INVALID_HANDLE_VALUE, "CreateNamedPipeW failed: %u\n", GetLastError());
1449 child_pipe_end = CreateFileW(L"\\\\.\\pipe\\pseudoconsoleserver", GENERIC_READ | GENERIC_WRITE, 0,
1450 &sec_attr, OPEN_EXISTING, 0, NULL);
1451 ok(child_pipe_end != INVALID_HANDLE_VALUE, "CreateFile failed: %u\n", GetLastError());
1453 read_mode = PIPE_READMODE_MESSAGE;
1454 r = SetNamedPipeHandleState(child_pipe_end, &read_mode, NULL, NULL);
1455 ok(r, "SetNamedPipeHandleState failed: %u\n", GetLastError());
1457 hres = pCreatePseudoConsole(size, *console_pipe_end, *console_pipe_end, 0, &console);
1458 ok(hres == S_OK, "CreatePseudoConsole failed: %08x\n", hres);
1460 *child_process = run_child(console, child_pipe_end);
1461 CloseHandle(child_pipe_end);
1462 return console;
1465 static void test_pseudoconsole(void)
1467 HANDLE console_pipe_end, child_process;
1468 BOOL broken_version;
1469 HPCON console;
1471 console = create_pseudo_console(&console_pipe_end, &child_process);
1473 child_string_request(REQ_SET_TITLE, L"test title");
1474 expect_output_sequence("\x1b[2J"); /* erase display */
1475 skip_hide_cursor();
1476 expect_output_sequence("\x1b[m"); /* default attributes */
1477 expect_output_sequence("\x1b[H"); /* set cursor */
1478 skip_sequence("\x1b[H"); /* some windows versions emit it twice */
1479 expect_output_sequence("\x1b]0;test title"); /* set title */
1480 broken_version = skip_byte(0); /* some win versions emit nullbyte */
1481 expect_output_sequence("\x07");
1482 skip_sequence("\x1b[?25h"); /* show cursor */
1483 expect_empty_output();
1485 if (!broken_version)
1487 test_tty_output();
1488 test_read_console();
1489 test_tty_input();
1491 else win_skip("Skipping tty output tests on broken Windows version\n");
1493 CloseHandle(child_pipe);
1494 wait_child_process(child_process);
1495 CloseHandle(child_process);
1497 /* native sometimes clears the screen here */
1498 if (skip_sequence("\x1b[25l"))
1500 unsigned int i;
1501 skip_sequence("\x1b[H");
1502 for (i = 0; i < 40; i++)
1504 expect_output_sequence("\x1b[K");
1505 if (i != 39) expect_output_sequence("\r\n");
1507 skip_sequence("\x1b[H\x1b[?25h");
1509 expect_empty_output();
1511 pClosePseudoConsole(console);
1512 CloseHandle(console_pipe_end);
1513 CloseHandle(console_pipe);
1516 START_TEST(tty)
1518 HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
1519 char **argv;
1520 int argc;
1522 argc = winetest_get_mainargs(&argv);
1523 if (argc > 3)
1525 HANDLE pipe;
1526 DWORD mode;
1527 sscanf(argv[3], "%p", &pipe);
1528 /* if std output is console, silence debug output so it does not interfere with tests */
1529 if (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode))
1530 winetest_debug = 0;
1531 child_process(pipe);
1532 return;
1535 pCreatePseudoConsole = (void *)GetProcAddress(kernel32, "CreatePseudoConsole");
1536 pClosePseudoConsole = (void *)GetProcAddress(kernel32, "ClosePseudoConsole");
1537 if (!pCreatePseudoConsole)
1539 win_skip("CreatePseudoConsole is not available\n");
1540 return;
1543 test_pseudoconsole();