wined3d: Pass a wined3d_adapter instead of wined3d_gl_info to FFP pipe emulation...
[wine.git] / programs / conhost / tests / tty.c
blob441bc17f04d66f9eba34bedc28acea55065fac05
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: %lu\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: %lu\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: %lu\n", GetLastError());
67 ok_(__FILE__,line)(!avail, "avail = %lu\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_GET_SB_INFO,
153 REQ_READ_CONSOLE,
154 REQ_READ_CONSOLE_A,
155 REQ_READ_CONSOLE_FILE,
156 REQ_READ_CONSOLE_CONTROL,
157 REQ_SCROLL,
158 REQ_SET_ACTIVE,
159 REQ_SET_CURSOR,
160 REQ_SET_INPUT_CP,
161 REQ_SET_INPUT_MODE,
162 REQ_SET_OUTPUT_MODE,
163 REQ_SET_TITLE,
164 REQ_WRITE_CHARACTERS,
165 REQ_WRITE_CONSOLE,
166 REQ_WRITE_OUTPUT,
169 struct pseudoconsole_req
171 enum req_type type;
172 union
174 WCHAR string[1];
175 COORD coord;
176 HANDLE handle;
177 DWORD mode;
178 int cp;
179 size_t size;
180 struct
182 COORD coord;
183 unsigned int len;
184 WCHAR buf[1];
185 } write_characters;
186 struct
188 COORD size;
189 COORD coord;
190 SMALL_RECT region;
191 CHAR_INFO buf[1];
192 } write_output;
193 struct
195 SMALL_RECT rect;
196 COORD dst;
197 CHAR_INFO fill;
198 } scroll;
199 struct
201 WCHAR ch;
202 DWORD count;
203 COORD coord;
204 } fill;
205 struct
207 size_t size;
208 DWORD mask;
209 WCHAR initial[1];
210 } control;
211 } u;
214 static void child_string_request(enum req_type type, const WCHAR *title)
216 char buf[4096];
217 struct pseudoconsole_req *req = (void *)buf;
218 size_t len = lstrlenW(title) + 1;
219 DWORD count;
220 BOOL ret;
222 req->type = type;
223 memcpy(req->u.string, title, len * sizeof(WCHAR));
224 ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.string[len]),
225 &count, NULL);
226 ok(ret, "WriteFile failed: %lu\n", GetLastError());
229 static void child_write_characters(const WCHAR *buf, unsigned int x, unsigned int y)
231 char req_buf[4096];
232 struct pseudoconsole_req *req = (void *)req_buf;
233 size_t len = lstrlenW(buf);
234 DWORD count;
235 BOOL ret;
237 req->type = REQ_WRITE_CHARACTERS;
238 req->u.write_characters.coord.X = x;
239 req->u.write_characters.coord.Y = y;
240 req->u.write_characters.len = len;
241 memcpy(req->u.write_characters.buf, buf, len * sizeof(WCHAR));
242 ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.write_characters.buf[len + 1]),
243 &count, NULL);
244 ok(ret, "WriteFile failed: %lu\n", GetLastError());
247 static void child_set_cursor(const unsigned int x, unsigned int y)
249 struct pseudoconsole_req req;
250 DWORD count;
251 BOOL ret;
253 req.type = REQ_SET_CURSOR;
254 req.u.coord.X = x;
255 req.u.coord.Y = y;
256 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
257 ok(ret, "WriteFile failed: %lu\n", GetLastError());
260 static HANDLE child_create_screen_buffer(void)
262 struct pseudoconsole_req req;
263 HANDLE handle;
264 DWORD count;
265 BOOL ret;
267 req.type = REQ_CREATE_SCREEN_BUFFER;
268 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
269 ok(ret, "WriteFile failed: %lu\n", GetLastError());
270 ret = ReadFile(child_pipe, &handle, sizeof(handle), &count, NULL);
271 ok(ret, "ReadFile failed: %lu\n", GetLastError());
272 return handle;
275 static void child_set_active(HANDLE handle)
277 struct pseudoconsole_req req;
278 DWORD count;
279 BOOL ret;
281 req.type = REQ_SET_ACTIVE;
282 req.u.handle = handle;
283 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
284 ok(ret, "WriteFile failed: %lu\n", GetLastError());
287 #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)
288 static void child_write_output_(unsigned int line, CHAR_INFO *buf, unsigned int size_x, unsigned int size_y,
289 unsigned int coord_x, unsigned int coord_y, unsigned int left,
290 unsigned int top, unsigned int right, unsigned int bottom, unsigned int out_left,
291 unsigned int out_top, unsigned int out_right, unsigned int out_bottom)
293 char req_buf[4096];
294 struct pseudoconsole_req *req = (void *)req_buf;
295 SMALL_RECT region;
296 DWORD count;
297 BOOL ret;
299 req->type = REQ_WRITE_OUTPUT;
300 req->u.write_output.size.X = size_x;
301 req->u.write_output.size.Y = size_y;
302 req->u.write_output.coord.X = coord_x;
303 req->u.write_output.coord.Y = coord_y;
304 req->u.write_output.region.Left = left;
305 req->u.write_output.region.Top = top;
306 req->u.write_output.region.Right = right;
307 req->u.write_output.region.Bottom = bottom;
308 memcpy(req->u.write_output.buf, buf, size_x * size_y * sizeof(*buf));
309 ret = WriteFile(child_pipe, req, FIELD_OFFSET(struct pseudoconsole_req, u.write_output.buf[size_x * size_y]), &count, NULL);
310 ok_(__FILE__,line)(ret, "WriteFile failed: %lu\n", GetLastError());
311 ret = ReadFile(child_pipe, &region, sizeof(region), &count, NULL);
312 ok_(__FILE__,line)(ret, "WriteFile failed: %lu\n", GetLastError());
313 ok_(__FILE__,line)(region.Left == out_left, "Left = %u\n", region.Left);
314 ok_(__FILE__,line)(region.Top == out_top, "Top = %u\n", region.Top);
315 ok_(__FILE__,line)(region.Right == out_right, "Right = %u\n", region.Right);
316 ok_(__FILE__,line)(region.Bottom == out_bottom, "Bottom = %u\n", region.Bottom);
319 static void child_scroll(unsigned int src_left, unsigned int src_top, unsigned int src_right,
320 unsigned int src_bottom, unsigned int dst_x, unsigned int dst_y, WCHAR fill)
322 struct pseudoconsole_req req;
323 DWORD count;
324 BOOL ret;
326 req.type = REQ_SCROLL;
327 req.u.scroll.rect.Left = src_left;
328 req.u.scroll.rect.Top = src_top;
329 req.u.scroll.rect.Right = src_right;
330 req.u.scroll.rect.Bottom = src_bottom;
331 req.u.scroll.dst.X = dst_x;
332 req.u.scroll.dst.Y = dst_y;
333 req.u.scroll.fill.Char.UnicodeChar = fill;
334 req.u.scroll.fill.Attributes = 0;
335 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
336 ok(ret, "WriteFile failed: %lu\n", GetLastError());
339 static void child_fill_character(WCHAR ch, DWORD count, int x, int y)
341 struct pseudoconsole_req req;
342 BOOL ret;
344 req.type = REQ_FILL_CHAR;
345 req.u.fill.ch = ch;
346 req.u.fill.count = count;
347 req.u.fill.coord.X = x;
348 req.u.fill.coord.Y = y;
349 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
350 ok(ret, "WriteFile failed: %lu\n", GetLastError());
353 static void child_set_input_mode(HANDLE pipe, DWORD mode)
355 struct pseudoconsole_req req;
356 DWORD count;
357 BOOL ret;
359 req.type = REQ_SET_INPUT_MODE;
360 req.u.mode = mode;
361 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
362 ok(ret, "WriteFile failed: %lu\n", GetLastError());
365 static void child_set_output_mode(DWORD mode)
367 struct pseudoconsole_req req;
368 DWORD count;
369 BOOL ret;
371 req.type = REQ_SET_OUTPUT_MODE;
372 req.u.mode = mode;
373 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
374 ok(ret, "WriteFile failed: %lu\n", GetLastError());
377 static void child_set_input_cp(int cp)
379 struct pseudoconsole_req req;
380 DWORD count;
381 BOOL ret;
383 req.type = REQ_SET_INPUT_CP;
384 req.u.cp = cp;
385 ret = WriteFile(child_pipe, &req, sizeof(req), &count, NULL);
386 ok(ret, "WriteFile failed: %lu\n", GetLastError());
389 static void child_read_console(HANDLE pipe, size_t size)
391 struct pseudoconsole_req req;
392 DWORD count;
393 BOOL ret;
395 req.type = REQ_READ_CONSOLE;
396 req.u.size = size;
397 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
398 ok(ret, "WriteFile failed: %lu\n", GetLastError());
401 static void child_read_console_a(HANDLE pipe, size_t size)
403 struct pseudoconsole_req req;
404 DWORD count;
405 BOOL ret;
407 req.type = REQ_READ_CONSOLE_A;
408 req.u.size = size;
409 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
410 ok(ret, "WriteFile failed: %lu\n", GetLastError());
413 static void child_read_console_file(HANDLE pipe, size_t size)
415 struct pseudoconsole_req req;
416 DWORD count;
417 BOOL ret;
419 req.type = REQ_READ_CONSOLE_FILE;
420 req.u.size = size;
421 ret = WriteFile(pipe, &req, sizeof(req), &count, NULL);
422 ok(ret, "WriteFile failed: %lu\n", GetLastError());
425 static void child_read_console_control(HANDLE pipe, size_t size, DWORD control, const WCHAR* recall)
427 char tmp[4096];
428 struct pseudoconsole_req *req = (void *)tmp;
429 DWORD count;
430 BOOL ret;
432 req->type = REQ_READ_CONSOLE_CONTROL;
433 req->u.control.size = size;
434 req->u.control.mask = control;
435 wcscpy(req->u.control.initial, recall);
436 ret = WriteFile(pipe, req, sizeof(*req) + wcslen(recall) * sizeof(WCHAR), &count, NULL);
437 ok(ret, "WriteFile failed: %lu\n", GetLastError());
440 #define child_expect_read_result(a,b) child_expect_read_result_(__LINE__,a,b)
441 static void child_expect_read_result_(unsigned int line, HANDLE pipe, const WCHAR *expect)
443 size_t exlen = wcslen(expect);
444 WCHAR buf[4096];
445 DWORD count;
446 BOOL ret;
448 ret = ReadFile(pipe, buf, sizeof(buf), &count, NULL);
449 ok_(__FILE__,line)(ret, "ReadFile failed: %lu\n", GetLastError());
450 ok_(__FILE__,line)(count == exlen * sizeof(WCHAR), "got %lu, expected %Iu\n",
451 count, exlen * sizeof(WCHAR));
452 buf[count / sizeof(WCHAR)] = 0;
453 ok_(__FILE__,line)(!memcmp(expect, buf, count), "unexpected data %s\n", wine_dbgstr_w(buf));
456 #define child_expect_read_control_result(a,b,c) child_expect_read_control_result_(__LINE__,a,b,c)
457 static void child_expect_read_control_result_(unsigned int line, HANDLE pipe, const WCHAR *expect, DWORD state)
459 size_t exlen = wcslen(expect);
460 WCHAR buf[4096];
461 WCHAR *ptr = (void *)((char *)buf + sizeof(DWORD));
462 DWORD count;
463 BOOL ret;
465 ret = ReadFile(pipe, buf, sizeof(buf), &count, NULL);
466 ok_(__FILE__,line)(ret, "ReadFile failed: %lu\n", GetLastError());
467 ok_(__FILE__,line)(count == sizeof(DWORD) + exlen * sizeof(WCHAR), "got %lu, expected %Iu\n",
468 count, sizeof(DWORD) + exlen * sizeof(WCHAR));
469 buf[count / sizeof(WCHAR)] = 0;
470 todo_wine_if(*(DWORD *)buf != state && *(DWORD *)buf == 0)
471 ok_(__FILE__,line)(*(DWORD *)buf == state, "keyboard state: got %lx, expected %lx\n", *(DWORD *)buf, state);
472 ok_(__FILE__,line)(!memcmp(expect, ptr, count - sizeof(DWORD)), "unexpected data %s %s\n", wine_dbgstr_w(ptr), wine_dbgstr_w(expect));
475 #define child_expect_read_result_a(a,b) child_expect_read_result_a_(__LINE__,a,b)
476 static void child_expect_read_result_a_(unsigned int line, HANDLE pipe, const char *expect)
478 size_t exlen = strlen(expect);
479 char buf[4096];
480 DWORD count;
481 BOOL ret;
483 ret = ReadFile(pipe, buf, sizeof(buf), &count, NULL);
484 ok_(__FILE__,line)(ret, "ReadFile failed: %lu\n", GetLastError());
485 todo_wine_if(exlen && expect[exlen - 1] == '\xcc')
486 ok_(__FILE__,line)(count == exlen, "got %lu, expected %Iu\n", count, exlen);
487 buf[count] = 0;
488 ok_(__FILE__,line)(!memcmp(expect, buf, count), "unexpected data %s\n", wine_dbgstr_a(buf));
491 static void expect_input(unsigned int event_type, INPUT_RECORD *record)
493 struct pseudoconsole_req req = { REQ_GET_INPUT };
494 INPUT_RECORD input;
495 DWORD read;
496 BOOL ret;
498 ret = WriteFile(child_pipe, &req, sizeof(req), &read, NULL);
499 ok(ret, "WriteFile failed: %lu\n", GetLastError());
501 ret = ReadFile(child_pipe, &input, sizeof(input), &read, NULL);
502 ok(ret, "ReadFile failed: %lu\n", GetLastError());
504 ok(input.EventType == event_type, "EventType = %u, expected %u\n", input.EventType, event_type);
505 if (record) *record = input;
508 static BOOL get_key_input(unsigned int vt, INPUT_RECORD *record)
510 static INPUT_RECORD prev_record;
511 static BOOL have_prev_record;
513 if (!have_prev_record)
515 expect_input(KEY_EVENT, &prev_record);
516 have_prev_record = TRUE;
519 if (vt && prev_record.Event.KeyEvent.wVirtualKeyCode != vt) return FALSE;
520 *record = prev_record;
521 have_prev_record = FALSE;
522 return TRUE;
525 #define expect_key_input(a,b,c,d) expect_key_input_(__LINE__,0,a,b,c,d)
526 static void expect_key_input_(unsigned int line, unsigned int ctx, WCHAR ch, unsigned int vk,
527 BOOL down, unsigned int ctrl_state)
529 unsigned int vs = MapVirtualKeyW(vk, MAPVK_VK_TO_VSC);
530 INPUT_RECORD record;
532 get_key_input(0, &record);
533 ok_(__FILE__,line)(record.Event.KeyEvent.bKeyDown == down, "%x: bKeyDown = %x\n",
534 ctx, record.Event.KeyEvent.bKeyDown);
535 ok_(__FILE__,line)(record.Event.KeyEvent.wRepeatCount == 1, "%x: wRepeatCount = %x\n",
536 ctx, record.Event.KeyEvent.wRepeatCount);
537 ok_(__FILE__,line)(record.Event.KeyEvent.uChar.UnicodeChar == ch, "%x: UnicodeChar = %x\n",
538 ctx, record.Event.KeyEvent.uChar.UnicodeChar);
539 ok_(__FILE__,line)(record.Event.KeyEvent.wVirtualKeyCode == vk,
540 "%x: wVirtualKeyCode = %x, expected %x\n", ctx,
541 record.Event.KeyEvent.wVirtualKeyCode, vk);
542 ok_(__FILE__,line)(record.Event.KeyEvent.wVirtualScanCode == vs,
543 "%x: wVirtualScanCode = %x expected %x\n", ctx,
544 record.Event.KeyEvent.wVirtualScanCode, vs);
545 ok_(__FILE__,line)(record.Event.KeyEvent.dwControlKeyState == ctrl_state,
546 "%x: dwControlKeyState = %lx expected %x\n", ctx, record.Event.KeyEvent.dwControlKeyState, ctrl_state);
549 #define get_input_key_vt() get_input_key_vt_(__LINE__)
550 static unsigned int get_input_key_vt_(unsigned int line)
552 INPUT_RECORD record;
554 get_key_input(0, &record);
555 ok_(__FILE__,line)(record.Event.KeyEvent.wRepeatCount == 1, "wRepeatCount = %x\n",
556 record.Event.KeyEvent.wRepeatCount);
557 return record.Event.KeyEvent.wVirtualKeyCode;
560 #define expect_key_pressed(a,b,c) expect_key_pressed_(__LINE__,0,a,b,c)
561 #define expect_key_pressed_ctx(a,b,c,d) expect_key_pressed_(__LINE__,a,b,c,d)
562 static void expect_key_pressed_(unsigned int line, unsigned int ctx, WCHAR ch, unsigned int vk,
563 unsigned int ctrl_state)
565 if (ctrl_state & SHIFT_PRESSED)
566 expect_key_input_(line, ctx, 0, VK_SHIFT, TRUE, SHIFT_PRESSED);
567 if (ctrl_state & LEFT_ALT_PRESSED)
568 expect_key_input_(line, ctx, 0, VK_MENU, TRUE,
569 LEFT_ALT_PRESSED | (ctrl_state & SHIFT_PRESSED));
570 if (ctrl_state & RIGHT_ALT_PRESSED)
571 expect_key_input_(line, ctx, 0, VK_MENU, TRUE,
572 RIGHT_ALT_PRESSED | LEFT_CTRL_PRESSED | ENHANCED_KEY | (ctrl_state & SHIFT_PRESSED));
573 else if (ctrl_state & LEFT_CTRL_PRESSED)
574 expect_key_input_(line, ctx, 0, VK_CONTROL, TRUE,
575 LEFT_CTRL_PRESSED | (ctrl_state & (SHIFT_PRESSED | LEFT_ALT_PRESSED)));
576 expect_key_input_(line, ctx, ch, vk, TRUE, ctrl_state);
577 expect_key_input_(line, ctx, ch, vk, FALSE, ctrl_state);
578 if (ctrl_state & RIGHT_ALT_PRESSED)
579 expect_key_input_(line, ctx, 0, VK_MENU, FALSE, ENHANCED_KEY | (ctrl_state & SHIFT_PRESSED));
580 else if (ctrl_state & LEFT_CTRL_PRESSED)
581 expect_key_input_(line, ctx, 0, VK_CONTROL, FALSE,
582 ctrl_state & (SHIFT_PRESSED | LEFT_ALT_PRESSED));
583 if (ctrl_state & LEFT_ALT_PRESSED)
584 expect_key_input_(line, ctx, 0, VK_MENU, FALSE, ctrl_state & SHIFT_PRESSED);
585 if (ctrl_state & SHIFT_PRESSED)
586 expect_key_input_(line, ctx, 0, VK_SHIFT, FALSE, 0);
589 #define expect_char_key(a) expect_char_key_(__LINE__,a,0)
590 #define expect_char_key_ctrl(a,c) expect_char_key_(__LINE__,a,c)
591 static void expect_char_key_(unsigned int line, WCHAR ch, unsigned int ctrl)
593 unsigned int vk;
595 vk = VkKeyScanW(ch);
596 if (vk == ~0) vk = 0;
597 if (vk & 0x0100) ctrl |= SHIFT_PRESSED;
598 /* Some keyboard (like German or French one) report right alt as ctrl+alt */
599 if ((vk & 0x0600) == 0x0600 && !(ctrl & LEFT_ALT_PRESSED)) ctrl |= RIGHT_ALT_PRESSED;
600 if (vk & 0x0200) ctrl |= LEFT_CTRL_PRESSED;
601 vk &= 0xff;
602 expect_key_pressed_(line, ch, ch, vk, ctrl);
605 static void fetch_child_sb_info(CONSOLE_SCREEN_BUFFER_INFO *info)
607 struct pseudoconsole_req req = { REQ_GET_SB_INFO };
608 DWORD read;
609 BOOL ret;
611 ret = WriteFile(child_pipe, &req, sizeof(req), &read, NULL);
612 ok(ret, "WriteFile failed: %lu\n", GetLastError());
614 ret = ReadFile(child_pipe, info, sizeof(*info), &read, NULL);
615 ok(ret, "ReadFile failed: %lu\n", GetLastError());
618 #define test_cursor_pos(a,b) _test_cursor_pos(__LINE__,a,b)
619 static void _test_cursor_pos(unsigned line, int expect_x, int expect_y)
621 CONSOLE_SCREEN_BUFFER_INFO info;
623 fetch_child_sb_info(&info);
625 ok_(__FILE__,line)(info.dwCursorPosition.X == expect_x, "dwCursorPosition.X = %u, expected %u\n",
626 info.dwCursorPosition.X, expect_x);
627 ok_(__FILE__,line)(info.dwCursorPosition.Y == expect_y, "dwCursorPosition.Y = %u, expected %u\n",
628 info.dwCursorPosition.Y, expect_y);
631 static BOOL test_write_console(void)
633 child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
635 child_string_request(REQ_WRITE_CONSOLE, L"abc");
636 skip_hide_cursor();
637 if (skip_sequence("\x1b[H \x1b[32m0\x1b[41m1\x1b[30m2"))
639 /* we get this on w1064v1909 (and more discrepancies afterwards).
640 * This is inconsistent with ulterior w10 versions. Assume it's
641 * immature result, and skip rest of tests.
643 console_output_count = 0;
644 return FALSE;
647 expect_output_sequence("abc");
648 skip_sequence("\x1b[?25h"); /* show cursor */
650 child_string_request(REQ_WRITE_CONSOLE, L"\tt");
651 skip_hide_cursor();
652 if (!skip_sequence("\x1b[3C")) expect_output_sequence(" ");
653 expect_output_sequence("t");
654 skip_sequence("\x1b[?25h"); /* show cursor */
655 expect_empty_output();
657 child_string_request(REQ_WRITE_CONSOLE, L"x\rr");
658 expect_hide_cursor();
659 expect_output_sequence("\rr abc tx");
660 if (!skip_sequence("\x1b[9D"))
661 expect_output_sequence("\x1b[4;2H"); /* set cursor */
662 expect_output_sequence("\x1b[?25h"); /* show cursor */
663 expect_empty_output();
665 child_string_request(REQ_WRITE_CONSOLE, L"yz\r\n");
666 skip_hide_cursor();
667 expect_output_sequence("yz\r\n");
668 skip_sequence("\x1b[?25h"); /* show cursor */
669 expect_empty_output();
671 child_string_request(REQ_WRITE_CONSOLE, L"abc\r\n123\r\ncde\r");
672 skip_hide_cursor();
673 expect_output_sequence("abc\r\n123\r\ncde\r");
674 skip_sequence("\x1b[?25h"); /* show cursor */
675 expect_empty_output();
677 child_set_cursor(0, 39);
678 expect_hide_cursor();
679 expect_output_sequence("\x1b[40;1H"); /* set cursor */
680 expect_output_sequence("\x1b[?25h"); /* show cursor */
681 expect_empty_output();
683 child_string_request(REQ_WRITE_CONSOLE, L"yz\r\n");
684 skip_hide_cursor();
685 expect_output_sequence("yz\r");
686 if (skip_sequence("\x1b[?25h")) /* show cursor */
687 expect_output_sequence("\x1b[?25l"); /* hide cursor */
688 expect_output_sequence("\n"); /* next line */
689 if (skip_sequence("\x1b[30X")) /* erase the line */
691 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
692 expect_output_sequence("\r");
694 skip_sequence("\x1b[?25h"); /* show cursor */
695 expect_empty_output();
697 child_string_request(REQ_WRITE_CONSOLE, L"");
698 expect_empty_output();
700 child_string_request(REQ_WRITE_CONSOLE, L"ab\n");
701 skip_hide_cursor();
702 expect_output_sequence("ab");
703 if (skip_sequence("\x1b[?25h")) /* show cursor */
704 expect_output_sequence("\x1b[?25l"); /* hide cursor */
705 expect_output_sequence("\r\n"); /* next line */
706 if (skip_sequence("\x1b[30X")) /* erase the line */
708 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
709 expect_output_sequence("\r");
711 skip_sequence("\x1b[?25h"); /* show cursor */
712 expect_empty_output();
714 child_set_cursor(28, 10);
715 expect_hide_cursor();
716 expect_output_sequence("\x1b[11;29H"); /* set cursor */
717 expect_output_sequence("\x1b[?25h"); /* show cursor */
718 expect_empty_output();
720 child_string_request(REQ_WRITE_CONSOLE, L"xy");
721 skip_hide_cursor();
722 expect_output_sequence("xy");
723 if (!skip_sequence("\b")) skip_sequence("\r\n");
724 skip_sequence("\x1b[?25h"); /* show cursor */
725 expect_empty_output();
727 child_set_cursor(28, 10);
728 fetch_console_output();
729 if (!skip_sequence("\b"))
731 expect_hide_cursor();
732 expect_output_sequence("\x1b[11;29H"); /* set cursor */
733 expect_output_sequence("\x1b[?25h"); /* show cursor */
735 expect_empty_output();
737 child_string_request(REQ_WRITE_CONSOLE, L"abc");
738 skip_hide_cursor();
739 expect_output_sequence("\r ab");
740 expect_output_sequence("\r\nc");
741 if (expect_erase_line(29))
742 expect_output_sequence("\x1b[12;2H"); /* set cursor */
743 skip_sequence("\x1b[?25h"); /* show cursor */
744 expect_empty_output();
746 child_set_cursor(28, 39);
747 expect_hide_cursor();
748 expect_output_sequence("\x1b[40;29H"); /* set cursor */
749 expect_output_sequence("\x1b[?25h"); /* show cursor */
750 expect_empty_output();
752 child_string_request(REQ_WRITE_CONSOLE, L"abc");
753 skip_hide_cursor();
754 expect_output_sequence("ab");
755 skip_sequence("\x1b[40;29H"); /* set cursor */
756 if (skip_sequence("\x1b[?25h")) /* show cursor */
757 expect_output_sequence("\x1b[?25l"); /* hide cursor */
758 else
759 skip_sequence("\b");
760 expect_output_sequence("\r\nc");
761 if (skip_sequence("\x1b[29X")) /* erase the line */
763 expect_output_sequence("\x1b[29C"); /* move cursor to the end of the line */
764 expect_output_sequence("\x1b[40;2H"); /* set cursor */
766 skip_sequence("\x1b[?25h"); /* show cursor */
767 expect_empty_output();
769 child_set_cursor(28, 39);
770 skip_hide_cursor();
771 if (!skip_sequence("\x1b[27C"))
772 expect_output_sequence("\x1b[40;29H"); /* set cursor */
773 skip_sequence("\x1b[?25h"); /* show cursor */
774 expect_empty_output();
776 child_string_request(REQ_WRITE_CONSOLE, L"XY");
777 skip_hide_cursor();
778 expect_output_sequence("XY");
779 skip_sequence("\x1b[40;29H"); /* set cursor */
780 if (skip_sequence("\x1b[?25h")) /* show cursor */
781 skip_sequence("\x1b[?25l"); /* hide cursor */
782 if (!skip_sequence("\b") && skip_sequence("\r\n"))
784 expect_output_sequence("\x1b[30X"); /* erase the line */
785 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
786 expect_output_sequence("\r"); /* set cursor */
788 skip_sequence("\x1b[?25h"); /* show cursor */
789 expect_empty_output();
791 child_string_request(REQ_WRITE_CONSOLE, L"\n");
792 skip_hide_cursor();
793 if (!skip_sequence("\r\n"))
795 expect_output_sequence("\n");
796 expect_output_sequence("\x1b[30X"); /* erase the line */
797 expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */
798 expect_output_sequence("\r"); /* set cursor */
800 skip_sequence("\x1b[?25h"); /* show cursor */
801 expect_empty_output();
803 child_set_output_mode(ENABLE_PROCESSED_OUTPUT);
805 child_set_cursor(28, 11);
806 expect_hide_cursor();
807 expect_output_sequence("\x1b[12;29H"); /* set cursor */
808 skip_sequence("\x1b[?25h"); /* show cursor */
810 child_string_request(REQ_WRITE_CONSOLE, L"xyz1234");
811 skip_hide_cursor();
812 expect_output_sequence("43\b");
813 skip_sequence("\x1b[?25h"); /* show cursor */
814 expect_empty_output();
816 child_set_cursor(28, 11);
817 skip_hide_cursor();
818 expect_output_sequence("\b"); /* backspace */
819 skip_sequence("\x1b[?25h"); /* show cursor */
821 child_string_request(REQ_WRITE_CONSOLE, L"xyz123");
822 expect_hide_cursor();
823 expect_output_sequence("23");
824 if (!skip_sequence("\x1b[2D"))
825 expect_output_sequence("\x1b[12;29H");/* set cursor */
826 expect_output_sequence("\x1b[?25h"); /* show cursor */
827 expect_empty_output();
829 child_set_cursor(28, 11);
830 child_string_request(REQ_WRITE_CONSOLE, L"abcdef\n\r123456789012345678901234567890xyz");
831 expect_hide_cursor();
832 if (skip_sequence("\x1b[?25h")) expect_hide_cursor();
833 expect_output_sequence("\r ef\r\n");
834 expect_output_sequence("xyz456789012345678901234567890");
835 if (!skip_sequence("\x1b[27D"))
836 expect_output_sequence("\x1b[13;4H"); /* set cursor */
837 expect_output_sequence("\x1b[?25h"); /* show cursor */
838 expect_empty_output();
840 child_set_cursor(28, 11);
841 expect_hide_cursor();
842 expect_output_sequence("\x1b[12;29H"); /* set cursor */
843 expect_output_sequence("\x1b[?25h"); /* show cursor */
845 child_string_request(REQ_WRITE_CONSOLE, L"AB\r\n");
846 skip_hide_cursor();
847 expect_output_sequence("AB\r\n");
848 skip_sequence("\x1b[?25h"); /* show cursor */
849 expect_empty_output();
851 child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
853 child_set_cursor(28, 12);
854 skip_hide_cursor();
855 expect_output_sequence("\x1b[28C"); /* move cursor to the end of the line */
856 skip_sequence("\x1b[?25h"); /* show cursor */
857 expect_empty_output();
859 child_string_request(REQ_WRITE_CONSOLE, L"ab");
860 skip_hide_cursor();
861 expect_output_sequence("ab");
862 skip_sequence("\b");
863 skip_sequence("\x1b[?25h"); /* show cursor */
864 expect_empty_output();
865 test_cursor_pos(29, 12);
867 child_string_request(REQ_WRITE_CONSOLE, L"c");
868 skip_hide_cursor();
869 expect_output_sequence("\r\n");
870 expect_output_sequence("c");
871 skip_sequence("\x1b[?25h"); /* show cursor */
872 expect_empty_output();
873 test_cursor_pos(1, 13);
875 child_set_cursor(28, 14);
876 skip_hide_cursor();
877 expect_output_sequence("\x1b[15;29H"); /* set cursor */
878 skip_sequence("\x1b[?25h"); /* show cursor */
879 expect_empty_output();
881 child_string_request(REQ_WRITE_CONSOLE, L"x");
882 skip_hide_cursor();
883 expect_output_sequence("x");
884 skip_sequence("\x1b[?25h"); /* show cursor */
885 expect_empty_output();
886 test_cursor_pos(29, 14);
888 child_string_request(REQ_WRITE_CONSOLE, L"y");
889 skip_hide_cursor();
890 expect_output_sequence("y");
891 skip_sequence("\x1b[?25h"); /* show cursor */
892 expect_empty_output();
893 test_cursor_pos(29, 14);
895 child_string_request(REQ_WRITE_CONSOLE, L"\b");
896 skip_hide_cursor();
897 expect_output_sequence("\b");
898 skip_sequence("\x1b[?25h"); /* show cursor */
899 expect_empty_output();
900 test_cursor_pos(28, 14);
902 child_string_request(REQ_WRITE_CONSOLE, L"z");
903 skip_hide_cursor();
904 expect_output_sequence("z");
905 skip_sequence("\x1b[?25h"); /* show cursor */
906 expect_empty_output();
907 test_cursor_pos(29, 14);
909 child_string_request(REQ_WRITE_CONSOLE, L"w");
910 skip_hide_cursor();
911 expect_output_sequence("w");
912 skip_sequence("\x1b[?25h"); /* show cursor */
913 expect_empty_output();
914 test_cursor_pos(29, 14);
916 child_string_request(REQ_WRITE_CONSOLE, L"X");
917 skip_hide_cursor();
918 expect_output_sequence("\r\n");
919 expect_output_sequence("X");
920 skip_sequence("\x1b[?25h"); /* show cursor */
921 expect_empty_output();
922 test_cursor_pos(1, 15);
924 child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
926 child_set_cursor(28, 20);
927 skip_hide_cursor();
928 expect_output_sequence("\x1b[21;29H"); /* set cursor */
929 skip_sequence("\x1b[?25h"); /* show cursor */
930 expect_empty_output();
932 child_string_request(REQ_WRITE_CONSOLE, L"ab");
933 skip_hide_cursor();
934 expect_output_sequence("ab");
935 expect_output_sequence("\r\n");
936 skip_sequence("\x1b[?25h"); /* show cursor */
937 expect_empty_output();
938 test_cursor_pos(0, 21);
940 child_string_request(REQ_WRITE_CONSOLE, L"c");
941 skip_hide_cursor();
942 expect_output_sequence("c");
943 skip_sequence("\x1b[?25h"); /* show cursor */
944 expect_empty_output();
945 test_cursor_pos(1, 21);
947 child_set_cursor(28, 22);
948 skip_hide_cursor();
949 expect_output_sequence("\x1b[23;29H"); /* set cursor */
950 skip_sequence("\x1b[?25h"); /* show cursor */
951 expect_empty_output();
953 child_string_request(REQ_WRITE_CONSOLE, L"x");
954 skip_hide_cursor();
955 expect_output_sequence("x");
956 skip_sequence("\x1b[?25h"); /* show cursor */
957 expect_empty_output();
958 test_cursor_pos(29, 22);
960 child_string_request(REQ_WRITE_CONSOLE, L"y");
961 skip_hide_cursor();
962 expect_output_sequence("y");
963 expect_output_sequence("\r\n");
964 skip_sequence("\x1b[?25h"); /* show cursor */
965 expect_empty_output();
966 test_cursor_pos(0, 23);
968 child_string_request(REQ_WRITE_CONSOLE, L"z");
969 skip_hide_cursor();
970 expect_output_sequence("z");
971 skip_sequence("\x1b[?25h"); /* show cursor */
972 expect_empty_output();
973 test_cursor_pos(1, 23);
975 return TRUE;
978 static BOOL test_tty_output(void)
980 CHAR_INFO char_info_buf[2048], char_info;
981 HANDLE sb, sb2;
982 unsigned int i;
984 /* simple write chars */
985 child_write_characters(L"child", 3, 4);
986 expect_hide_cursor();
987 expect_output_sequence("\x1b[5;4H"); /* set cursor */
988 expect_output_sequence("child");
989 expect_output_sequence("\x1b[H"); /* set cursor */
990 expect_output_sequence("\x1b[?25h"); /* show cursor */
991 expect_empty_output();
993 /* wrapped write chars */
994 child_write_characters(L"bound", 28, 6);
995 expect_hide_cursor();
996 expect_output_sequence("\x1b[7;1H"); /* set cursor */
997 expect_output_sequence(" bo\r\nund");
998 expect_erase_line(27);
999 expect_output_sequence("\x1b[H"); /* set cursor */
1000 expect_output_sequence("\x1b[?25h"); /* show cursor */
1001 expect_empty_output();
1003 /* fill line 4 with a few simple writes */
1004 child_write_characters(L"xxx", 13, 4);
1005 expect_hide_cursor();
1006 expect_output_sequence("\x1b[5;14H"); /* set cursor */
1007 expect_output_sequence("xxx");
1008 expect_output_sequence("\x1b[H"); /* set cursor */
1009 expect_output_sequence("\x1b[?25h"); /* show cursor */
1010 expect_empty_output();
1012 /* write one char at the end of row */
1013 child_write_characters(L"y", 29, 4);
1014 expect_hide_cursor();
1015 expect_output_sequence("\x1b[5;30H"); /* set cursor */
1016 expect_output_sequence("y");
1017 expect_output_sequence("\x1b[H"); /* set cursor */
1018 expect_output_sequence("\x1b[?25h"); /* show cursor */
1019 expect_empty_output();
1021 /* wrapped write chars */
1022 child_write_characters(L"zz", 29, 4);
1023 expect_hide_cursor();
1024 expect_output_sequence("\x1b[5;1H"); /* set cursor */
1025 expect_output_sequence(" child xxx z");
1026 expect_output_sequence("\r\nz");
1027 expect_erase_line(29);
1028 expect_output_sequence("\x1b[H"); /* set cursor */
1029 expect_output_sequence("\x1b[?25h"); /* show cursor */
1030 expect_empty_output();
1032 /* trailing spaces */
1033 child_write_characters(L"child ", 3, 4);
1034 expect_hide_cursor();
1035 expect_output_sequence("\x1b[5;4H"); /* set cursor */
1036 expect_output_sequence("child ");
1037 expect_output_sequence("\x1b[H"); /* set cursor */
1038 expect_output_sequence("\x1b[?25h"); /* show cursor */
1039 expect_empty_output();
1041 child_set_cursor(2, 3);
1042 expect_hide_cursor();
1043 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1044 expect_output_sequence("\x1b[?25h"); /* show cursor */
1045 expect_empty_output();
1047 child_string_request(REQ_SET_TITLE, L"new title");
1048 fetch_console_output();
1049 skip_sequence("\x1b[?25l"); /* hide cursor */
1050 expect_output_sequence("\x1b]0;new title\x07"); /* set title */
1051 skip_sequence("\x1b[?25h"); /* show cursor */
1052 expect_empty_output();
1054 for (i = 0; i < ARRAY_SIZE(char_info_buf); i++)
1056 char_info_buf[i].Char.UnicodeChar = '0' + i % 10;
1057 char_info_buf[i].Attributes = 0;
1060 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 1, 2,
1061 /* region */ 3, 7, 5, 9, /* out region */ 3, 7, 5, 9);
1062 expect_hide_cursor();
1063 expect_output_sequence("\x1b[30m"); /* foreground black */
1064 expect_output_sequence("\x1b[8;4H"); /* set cursor */
1065 expect_output_sequence("567");
1066 expect_output_sequence("\x1b[9;4H"); /* set cursor */
1067 expect_output_sequence("234");
1068 expect_output_sequence("\x1b[10;4H"); /* set cursor */
1069 expect_output_sequence("901");
1070 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1071 expect_output_sequence("\x1b[?25h"); /* show cursor */
1072 expect_empty_output();
1074 child_write_output(char_info_buf, /* size */ 2, 3, /* coord */ 1, 2,
1075 /* region */ 3, 8, 15, 19, /* out region */ 3, 8, 3, 8);
1076 expect_hide_cursor();
1077 if (skip_sequence("\x1b[m")) /* default attr */
1078 expect_output_sequence("\x1b[30m");/* foreground black */
1079 expect_output_sequence("\x1b[9;4H"); /* set cursor */
1080 expect_output_sequence("5");
1081 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1082 expect_output_sequence("\x1b[?25h"); /* show cursor */
1083 expect_empty_output();
1085 child_write_output(char_info_buf, /* size */ 3, 4, /* coord */ 1, 2,
1086 /* region */ 3, 8, 15, 19, /* out region */ 3, 8, 4, 9);
1087 expect_hide_cursor();
1088 if (skip_sequence("\x1b[m")) /* default attr */
1089 expect_output_sequence("\x1b[30m");/* foreground black */
1090 expect_output_sequence("\x1b[9;4H"); /* set cursor */
1091 expect_output_sequence("78");
1092 expect_output_sequence("\x1b[10;4H"); /* set cursor */
1093 expect_output_sequence("01");
1094 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1095 expect_output_sequence("\x1b[?25h"); /* show cursor */
1096 expect_empty_output();
1098 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 2, 3,
1099 /* region */ 28, 38, 31, 60, /* out region */ 28, 38, 29, 39);
1100 expect_hide_cursor();
1101 if (skip_sequence("\x1b[m")) /* default attr */
1102 expect_output_sequence("\x1b[30m");/* foreground black */
1103 expect_output_sequence("\x1b[39;29H"); /* set cursor */
1104 expect_output_sequence("34");
1105 expect_output_sequence("\x1b[40;29H"); /* set cursor */
1106 expect_output_sequence("01");
1107 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1108 expect_output_sequence("\x1b[?25h"); /* show cursor */
1109 expect_empty_output();
1111 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 1, 2,
1112 /* region */ 0, 7, 5, 9, /* out region */ 0, 7, 5, 9);
1113 expect_hide_cursor();
1114 if (skip_sequence("\x1b[m")) /* default attr */
1115 expect_output_sequence("\x1b[30m");/* foreground black */
1116 expect_output_sequence("\x1b[8;1H"); /* set cursor */
1117 expect_output_sequence("567890\r\n");
1118 expect_output_sequence("234567\r\n");
1119 expect_output_sequence("901234");
1120 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1121 expect_output_sequence("\x1b[?25h"); /* show cursor */
1122 expect_empty_output();
1124 child_scroll(/* scroll rect */ 0, 7, 2, 8, /* destination */ 2, 8, /* fill */ 'x');
1125 expect_hide_cursor();
1126 if (skip_sequence("\x1b[m")) /* default attr */
1127 expect_output_sequence("\x1b[30m");/* foreground black */
1128 expect_output_sequence("\x1b[8;1H"); /* set cursor */
1129 expect_output_sequence("xxx89\r\n");
1130 expect_output_sequence("xx567\r\n");
1131 expect_output_sequence("90234");
1132 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1133 expect_output_sequence("\x1b[?25h"); /* show cursor */
1134 expect_empty_output();
1136 child_write_characters(L"xxx", 3, 10);
1137 expect_hide_cursor();
1138 expect_output_sequence("\x1b[m"); /* default attributes */
1139 expect_output_sequence("\x1b[11;4H"); /* set cursor */
1140 expect_output_sequence("xxx");
1141 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1142 expect_output_sequence("\x1b[?25h"); /* show cursor */
1143 expect_empty_output();
1145 /* test attributes */
1146 for (i = 0; i < 0x100 - 0xff; i++)
1148 unsigned int expect;
1149 char expect_buf[16];
1150 char_info.Char.UnicodeChar = 'a';
1151 char_info.Attributes = i;
1152 child_write_output(&char_info, /* size */ 1, 1, /* coord */ 0, 0,
1153 /* region */ 12, 3, 12, 3, /* out region */ 12, 3, 12, 3);
1154 expect_hide_cursor();
1155 if (i != 0x190 && i && ((i & 0xff) != 8)) expect_output_sequence_ctx(i, "\x1b[m");
1156 if ((i & 0x0f) != 7)
1158 expect = 30;
1159 if (i & FOREGROUND_BLUE) expect += 4;
1160 if (i & FOREGROUND_GREEN) expect += 2;
1161 if (i & FOREGROUND_RED) expect += 1;
1162 if (i & FOREGROUND_INTENSITY) expect += 60;
1163 sprintf(expect_buf, "\x1b[%um", expect);
1164 expect_output_sequence_ctx(i, expect_buf);
1166 if (i & 0xf0)
1168 expect = 40;
1169 if (i & BACKGROUND_BLUE) expect += 4;
1170 if (i & BACKGROUND_GREEN) expect += 2;
1171 if (i & BACKGROUND_RED) expect += 1;
1172 if (i & BACKGROUND_INTENSITY) expect += 60;
1173 sprintf(expect_buf, "\x1b[%um", expect);
1174 expect_output_sequence_ctx(i, expect_buf);
1176 if (!skip_sequence("\x1b[10C"))
1177 expect_output_sequence_ctx(i, "\x1b[4;13H"); /* set cursor */
1178 expect_output_sequence("a");
1179 if (!skip_sequence("\x1b[11D"))
1180 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1181 expect_output_sequence("\x1b[?25h"); /* show cursor */
1182 expect_empty_output();
1185 char_info_buf[0].Attributes = FOREGROUND_GREEN;
1186 char_info_buf[1].Attributes = FOREGROUND_GREEN | BACKGROUND_RED;
1187 char_info_buf[2].Attributes = BACKGROUND_RED;
1188 child_write_output(char_info_buf, /* size */ 7, 8, /* coord */ 0, 0,
1189 /* region */ 7, 0, 9, 0, /* out region */ 7, 0, 9, 0);
1190 expect_hide_cursor();
1191 skip_sequence("\x1b[m"); /* default attr */
1192 expect_output_sequence("\x1b[32m"); /* foreground black */
1193 expect_output_sequence("\x1b[1;8H"); /* set cursor */
1194 expect_output_sequence("0");
1195 expect_output_sequence("\x1b[41m"); /* background red */
1196 expect_output_sequence("1");
1197 expect_output_sequence("\x1b[30m"); /* foreground black */
1198 expect_output_sequence("2");
1199 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1200 expect_output_sequence("\x1b[?25h"); /* show cursor */
1201 expect_empty_output();
1203 child_fill_character('i', 5, 15, 16);
1204 expect_hide_cursor();
1205 expect_output_sequence("\x1b[m"); /* default attributes */
1206 expect_output_sequence("\x1b[17;16H"); /* set cursor */
1207 expect_output_sequence("iiiii");
1208 expect_output_sequence("\x1b[4;3H"); /* set cursor */
1209 expect_output_sequence("\x1b[?25h"); /* show cursor */
1210 expect_empty_output();
1212 if (!test_write_console()) return FALSE;
1214 sb = child_create_screen_buffer();
1215 child_set_active(sb);
1216 expect_hide_cursor();
1217 expect_output_sequence("\x1b[H"); /* set cursor */
1218 for (i = 0; i < 40; i++)
1220 expect_erase_line(30);
1221 if (i != 39) expect_output_sequence("\r\n");
1223 expect_output_sequence("\x1b[H"); /* set cursor */
1224 expect_output_sequence("\x1b[?25h"); /* show cursor */
1225 expect_empty_output();
1227 child_write_characters(L"new sb", 0, 0);
1228 skip_hide_cursor();
1229 expect_output_sequence("new sb");
1230 ok(skip_sequence("\x1b[H") || skip_sequence("\r"), "expected set cursor\n");
1231 skip_sequence("\x1b[?25h"); /* show cursor */
1232 expect_empty_output();
1234 sb2 = child_create_screen_buffer();
1235 child_set_active(sb2);
1236 expect_hide_cursor();
1237 for (i = 0; i < 40; i++)
1239 expect_erase_line(30);
1240 if (i != 39) expect_output_sequence("\r\n");
1242 expect_output_sequence("\x1b[H"); /* set cursor */
1243 expect_output_sequence("\x1b[?25h"); /* show cursor */
1244 expect_empty_output();
1246 child_set_active(sb);
1247 expect_hide_cursor();
1248 expect_output_sequence("new sb");
1249 expect_erase_line(24);
1250 expect_output_sequence("\r\n");
1251 for (i = 1; i < 40; i++)
1253 expect_erase_line(30);
1254 if (i != 39) expect_output_sequence("\r\n");
1256 expect_output_sequence("\x1b[H"); /* set cursor */
1257 expect_output_sequence("\x1b[?25h"); /* show cursor */
1258 expect_empty_output();
1260 return TRUE;
1263 static void write_console_pipe(const char *buf)
1265 DWORD written;
1266 BOOL res;
1267 res = WriteFile(console_pipe, buf, strlen(buf), &written, NULL);
1268 ok(res, "WriteFile failed: %lu\n", GetLastError());
1271 static void test_read_console(void)
1273 child_set_input_mode(child_pipe, ENABLE_PROCESSED_INPUT);
1275 child_read_console(child_pipe, 100);
1276 write_console_pipe("abc");
1277 expect_empty_output();
1278 child_expect_read_result(child_pipe, L"abc");
1279 expect_empty_output();
1281 child_read_console(child_pipe, 1);
1282 write_console_pipe("xyz");
1283 child_expect_read_result(child_pipe, L"x");
1284 child_read_console(child_pipe, 100);
1285 child_expect_read_result(child_pipe, L"yz");
1286 expect_empty_output();
1288 child_set_input_cp(932);
1290 child_read_console_a(child_pipe, 2);
1291 write_console_pipe("\xe3\x81\x81");
1292 child_expect_read_result_a(child_pipe, "\x82\x9f");
1293 expect_empty_output();
1295 child_read_console_a(child_pipe, 1);
1296 write_console_pipe("\xe3\x81\x81""a");
1297 child_expect_read_result_a(child_pipe, "\x82\xcc");
1298 child_read_console_a(child_pipe, 1);
1299 child_expect_read_result_a(child_pipe, "a");
1300 expect_empty_output();
1302 child_read_console_a(child_pipe, 2);
1303 write_console_pipe("a\xe3\x81\x81""b");
1304 child_expect_read_result_a(child_pipe, "a\x82\xcc");
1305 child_read_console_a(child_pipe, 1);
1306 child_expect_read_result_a(child_pipe, "b");
1307 expect_empty_output();
1309 child_read_console_file(child_pipe, 2);
1310 write_console_pipe("\xe3\x81\x81");
1311 child_expect_read_result_a(child_pipe, "\x82\x9f");
1312 expect_empty_output();
1314 child_read_console_file(child_pipe, 1);
1315 write_console_pipe("\xe3\x81\x81""a");
1316 child_expect_read_result_a(child_pipe, "\x82\xcc");
1317 child_read_console_file(child_pipe, 1);
1318 child_expect_read_result_a(child_pipe, "a");
1319 expect_empty_output();
1321 child_read_console_file(child_pipe, 2);
1322 write_console_pipe("a\xe3\x81\x81""b");
1323 child_expect_read_result_a(child_pipe, "a\x82\xcc");
1324 child_read_console_file(child_pipe, 1);
1325 child_expect_read_result_a(child_pipe, "b");
1326 expect_empty_output();
1328 child_set_input_cp(437);
1330 child_set_input_mode(child_pipe, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
1331 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
1332 ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION);
1334 child_read_console(child_pipe, 100);
1335 write_console_pipe("xyz");
1336 skip_hide_cursor();
1337 expect_output_sequence("xyz");
1338 skip_sequence("\x1b[?25h"); /* show cursor */
1339 write_console_pipe("ab\r\n");
1340 child_expect_read_result(child_pipe, L"xyzab\r\n");
1341 skip_hide_cursor();
1342 expect_output_sequence("ab\r\n");
1343 skip_sequence("\x1b[?25h"); /* show cursor */
1344 expect_key_input('\r', VK_RETURN, 0, FALSE);
1345 expect_key_pressed('\n', VK_RETURN, LEFT_CTRL_PRESSED);
1346 expect_empty_output();
1349 static void test_read_console_control(void)
1351 CONSOLE_SCREEN_BUFFER_INFO info1, info2;
1352 char ctrl;
1353 char buf[16];
1354 WCHAR bufw[16];
1356 child_set_input_mode(child_pipe, ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT |
1357 ENABLE_ECHO_INPUT | ENABLE_MOUSE_INPUT | ENABLE_INSERT_MODE |
1358 ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_AUTO_POSITION);
1360 /* test simple behavior */
1361 for (ctrl = 0; ctrl < ' '; ctrl++)
1363 DWORD state;
1364 SHORT cc;
1366 /* don't play with fire */
1367 if (ctrl == 0 || ctrl == 3 || ctrl == '\n' || ctrl == 27) continue;
1369 /* Simulate initial characters
1370 * Note: as ReadConsole with CONTROL structure will need to back up the cursor
1371 * up to the length of the initial characters, all the following tests ensure that
1372 * this backup doesn't imply backing up to the previous line.
1373 * This is the "regular" behavior when using completion.
1375 child_string_request(REQ_WRITE_CONSOLE, L"\rabc");
1376 skip_sequence("\x1b[25l"); /* broken hide cursor */
1377 skip_sequence("\x1b[?25l"); /* hide cursor */
1378 skip_sequence("\x1b[H");
1379 skip_sequence("\r");
1380 expect_output_sequence("abc");
1381 skip_sequence("\x1b[?25h"); /* show cursor */
1383 fetch_child_sb_info(&info1);
1384 child_read_console_control(child_pipe, 100, 1ul << ctrl, L"abc");
1385 strcpy(buf, "def."); buf[3] = ctrl;
1386 write_console_pipe(buf);
1387 wcscpy(bufw, L"abcdef."); bufw[6] = (WCHAR)ctrl;
1388 /* ^H requires special handling */
1389 cc = (ctrl == 8) ? 0x0200 : VkKeyScanA(ctrl);
1390 if (cc == -1) cc = 0;
1391 state = 0;
1392 if (cc & 0x0100) state |= SHIFT_PRESSED;
1393 if (cc & 0x0200) state |= LEFT_CTRL_PRESSED;
1394 child_expect_read_control_result(child_pipe, bufw, state);
1395 skip_sequence("\x1b[?25l"); /* hide cursor */
1396 expect_output_sequence("def");
1397 skip_sequence("\x1b[?25h"); /* show cursor */
1398 fetch_child_sb_info(&info2);
1399 ok(info1.dwCursorPosition.X + 3 == info2.dwCursorPosition.X,
1400 "Bad x-position: expected %u => %u but got => %u instead\n",
1401 info1.dwCursorPosition.X, info1.dwCursorPosition.X + 3, info2.dwCursorPosition.X);
1402 ok(info1.dwCursorPosition.Y == info2.dwCursorPosition.Y, "Cursor shouldn't have changed line\n");
1405 /* test two different control characters in input */
1406 fetch_child_sb_info(&info1);
1407 child_read_console_control(child_pipe, 100, 1ul << '\x01', L"abc");
1408 write_console_pipe("d\x02""ef\x01");
1409 child_expect_read_control_result(child_pipe, L"abcd\x02""ef\x01", LEFT_CTRL_PRESSED);
1410 skip_sequence("\x1b[?25l"); /* hide cursor */
1411 expect_output_sequence("d^Bef");
1412 skip_sequence("\x1b[?25h"); /* show cursor */
1413 fetch_child_sb_info(&info2);
1414 ok(info1.dwCursorPosition.X + 5 == info2.dwCursorPosition.X,
1415 "Bad x-position: expected %u => %u but got => %u instead\n",
1416 info1.dwCursorPosition.X, info1.dwCursorPosition.X + 5, info2.dwCursorPosition.X);
1417 ok(info1.dwCursorPosition.Y == info2.dwCursorPosition.Y, "Cursor shouldn't have changed line\n");
1419 /* test that ctrl character not in mask is handled as without ctrl mask */
1420 child_read_console_control(child_pipe, 100, 1ul << '\x01', L"abc");
1421 write_console_pipe("d\ref\x01");
1422 child_expect_read_control_result(child_pipe, L"abcd\r\n", 0);
1423 skip_sequence("\x1b[?25l"); /* hide cursor */
1424 expect_output_sequence("d\r\n");
1425 skip_sequence("\x1b[?25h"); /* show cursor */
1426 expect_empty_output();
1427 /* see note above... ditto */
1428 child_string_request(REQ_WRITE_CONSOLE, L"abc");
1429 skip_sequence("\x1b[?25l"); /* hide cursor */
1430 expect_output_sequence("abc");
1431 skip_sequence("\x1b[?25h"); /* show cursor */
1433 child_read_console_control(child_pipe, 100, 1ul << '\x01', L"abc");
1434 child_expect_read_control_result(child_pipe, L"abcef\x01", LEFT_CTRL_PRESSED);
1435 skip_sequence("\x1b[?25l"); /* hide cursor */
1436 expect_output_sequence("ef");
1437 skip_sequence("\x1b[?25h"); /* show cursor */
1439 /* test when output buffer becomes full before control event */
1440 child_read_console_control(child_pipe, 4, 1ul << '\x01', L"abc");
1441 write_console_pipe("def\x01");
1442 child_expect_read_control_result(child_pipe, L"abcd", LEFT_CTRL_PRESSED);
1443 skip_sequence("\x1b[?25l"); /* hide cursor */
1444 expect_output_sequence("def");
1445 skip_sequence("\x1b[?25h"); /* show cursor */
1446 expect_empty_output();
1447 child_read_console_control(child_pipe, 20, 1ul << '\x01', L"abc");
1448 child_expect_read_control_result(child_pipe, L"ef\x01", 0);
1450 /* TODO: add tests:
1451 * - when initial characters go back to previous line
1452 * - edition inside initial characters can occur
1454 expect_empty_output();
1457 static void test_tty_input(void)
1459 INPUT_RECORD ir;
1460 unsigned int i;
1461 char buf[8];
1463 static const struct
1465 const char *str;
1466 unsigned int vk;
1467 unsigned int ctrl;
1468 } escape_test[] = {
1469 { "\x1b[A", VK_UP, 0 },
1470 { "\x1b[B", VK_DOWN, 0 },
1471 { "\x1b[C", VK_RIGHT, 0 },
1472 { "\x1b[D", VK_LEFT, 0 },
1473 { "\x1b[H", VK_HOME, 0 },
1474 { "\x1b[F", VK_END, 0 },
1475 { "\x1b[2~", VK_INSERT, 0 },
1476 { "\x1b[3~", VK_DELETE, 0 },
1477 { "\x1b[5~", VK_PRIOR, 0 },
1478 { "\x1b[6~", VK_NEXT, 0 },
1479 { "\x1b[15~", VK_F5, 0 },
1480 { "\x1b[17~", VK_F6, 0 },
1481 { "\x1b[18~", VK_F7, 0 },
1482 { "\x1b[19~", VK_F8, 0 },
1483 { "\x1b[20~", VK_F9, 0 },
1484 { "\x1b[21~", VK_F10, 0 },
1485 /* 0x10 */
1486 { "\x1b[23~", VK_F11, 0 },
1487 { "\x1b[24~", VK_F12, 0 },
1488 { "\x1bOP", VK_F1, 0 },
1489 { "\x1bOQ", VK_F2, 0 },
1490 { "\x1bOR", VK_F3, 0 },
1491 { "\x1bOS", VK_F4, 0 },
1492 { "\x1b[1;1A", VK_UP, 0 },
1493 { "\x1b[1;2A", VK_UP, SHIFT_PRESSED },
1494 { "\x1b[1;3A", VK_UP, LEFT_ALT_PRESSED },
1495 { "\x1b[1;4A", VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED },
1496 { "\x1b[1;5A", VK_UP, LEFT_CTRL_PRESSED },
1497 { "\x1b[1;6A", VK_UP, SHIFT_PRESSED | LEFT_CTRL_PRESSED },
1498 { "\x1b[1;7A", VK_UP, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1499 { "\x1b[1;8A", VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1500 { "\x1b[1;9A", VK_UP, 0 },
1501 { "\x1b[1;10A", VK_UP, SHIFT_PRESSED },
1502 /* 0x20 */
1503 { "\x1b[1;11A", VK_UP, LEFT_ALT_PRESSED },
1504 { "\x1b[1;12A", VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED },
1505 { "\x1b[1;13A", VK_UP, LEFT_CTRL_PRESSED },
1506 { "\x1b[1;14A", VK_UP, SHIFT_PRESSED | LEFT_CTRL_PRESSED },
1507 { "\x1b[1;15A", VK_UP, LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1508 { "\x1b[1;16A", VK_UP, SHIFT_PRESSED | LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED },
1509 { "\x1b[1;2P", VK_F1, SHIFT_PRESSED },
1510 { "\x1b[2;3~", VK_INSERT, LEFT_ALT_PRESSED },
1511 { "\x1b[2;3;5;6~", VK_INSERT, 0 },
1512 { "\x1b[6;2;3;5;1~", VK_NEXT, 0 },
1514 static const struct
1516 const char *str;
1517 WCHAR ch;
1518 unsigned int ctrl;
1519 } escape_char_test[] = {
1520 { "\x1b[Z", 0x9, SHIFT_PRESSED },
1521 { "\xe4\xb8\x80", 0x4e00, 0 },
1522 { "\x1b\x1b", 0x1b, LEFT_ALT_PRESSED },
1523 { "\x1b""1", '1', LEFT_ALT_PRESSED },
1524 { "\x1b""x", 'x', LEFT_ALT_PRESSED },
1525 { "\x1b""[", '[', LEFT_ALT_PRESSED },
1526 { "\x7f", '\b', 0 },
1529 write_console_pipe("x");
1530 if (!get_input_key_vt())
1532 skip("Skipping tests on settings that don't have VT mapping for 'x'\n");
1533 get_input_key_vt();
1534 return;
1536 get_input_key_vt();
1538 write_console_pipe("aBCd");
1539 expect_char_key('a');
1540 expect_char_key('B');
1541 expect_char_key('C');
1542 expect_char_key('d');
1544 for (i = 1; i < 0x7f; i++)
1546 if (i == 3 || i == '\n' || i == 0x1b || i == 0x1f) continue;
1547 buf[0] = i;
1548 buf[1] = 0;
1549 write_console_pipe(buf);
1550 if (i == 8)
1551 expect_key_pressed('\b', 'H', LEFT_CTRL_PRESSED);
1552 else if (i == 0x7f)
1553 expect_char_key(8);
1554 else
1555 expect_char_key(i);
1558 write_console_pipe("\r\n");
1559 expect_key_pressed('\r', VK_RETURN, 0);
1560 expect_key_pressed('\n', VK_RETURN, LEFT_CTRL_PRESSED);
1562 write_console_pipe("\xc4\x85");
1563 if (get_key_input(VK_MENU, &ir))
1565 expect_key_input(0x105, 'A', TRUE, LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
1566 expect_key_input(0x105, 'A', FALSE, LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
1567 expect_key_input(0, VK_MENU, FALSE, ENHANCED_KEY);
1569 else
1571 expect_key_input(0x105, 0, TRUE, 0);
1572 expect_key_input(0x105, 0, FALSE, 0);
1575 for (i = 0; i < ARRAY_SIZE(escape_test); i++)
1577 write_console_pipe(escape_test[i].str);
1578 expect_key_pressed_ctx(i, 0, escape_test[i].vk, escape_test[i].ctrl);
1581 for (i = 0; i < ARRAY_SIZE(escape_char_test); i++)
1583 write_console_pipe(escape_char_test[i].str);
1584 expect_char_key_ctrl(escape_char_test[i].ch, escape_char_test[i].ctrl);
1587 for (i = 0x80; i < 0x100; i += 11)
1589 buf[0] = i;
1590 buf[1] = 0;
1591 write_console_pipe(buf);
1592 expect_empty_output();
1596 static void child_process(HANDLE pipe)
1598 HANDLE output, input;
1599 DWORD size, count;
1600 char buf[4096];
1601 BOOL ret;
1603 output = CreateFileA("CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1604 ok(output != INVALID_HANDLE_VALUE, "could not open console output\n");
1606 input = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
1607 ok(output != INVALID_HANDLE_VALUE, "could not open console output\n");
1609 while(ReadFile(pipe, buf, sizeof(buf), &size, NULL))
1611 const struct pseudoconsole_req *req = (void *)buf;
1612 switch (req->type)
1614 case REQ_CREATE_SCREEN_BUFFER:
1616 HANDLE handle;
1617 SetLastError(0xdeadbeef);
1618 handle = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
1619 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1620 CONSOLE_TEXTMODE_BUFFER, NULL);
1621 ok(handle != INVALID_HANDLE_VALUE, "CreateConsoleScreenBuffer failed: %lu\n", GetLastError());
1622 ret = WriteFile(pipe, &handle, sizeof(handle), &count, NULL);
1623 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1624 break;
1627 case REQ_GET_INPUT:
1629 INPUT_RECORD record;
1630 ret = ReadConsoleInputW(input, &record, 1, &count);
1631 ok(ret, "ReadConsoleInputW failed: %lu\n", GetLastError());
1632 ok(count == 1, "count = %lu\n", count);
1633 ret = WriteFile(pipe, &record, sizeof(record), &count, NULL);
1634 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1635 break;
1638 case REQ_GET_SB_INFO:
1640 CONSOLE_SCREEN_BUFFER_INFO info;
1641 ret = GetConsoleScreenBufferInfo(output, &info);
1642 ok(ret, "GetConsoleScreenBufferInfo failed: %lu\n", GetLastError());
1643 ret = WriteFile(pipe, &info, sizeof(info), &count, NULL);
1644 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1645 break;
1648 case REQ_READ_CONSOLE:
1649 ret = ReadConsoleW(input, buf, req->u.size, &count, NULL );
1650 ok(ret, "ReadConsoleW failed: %lu\n", GetLastError());
1651 ret = WriteFile(pipe, buf, count * sizeof(WCHAR), NULL, NULL);
1652 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1653 break;
1655 case REQ_READ_CONSOLE_A:
1656 count = req->u.size;
1657 memset(buf, 0xcc, sizeof(buf));
1658 ret = ReadConsoleA(input, buf, count, &count, NULL );
1659 ok(ret, "ReadConsoleA failed: %lu\n", GetLastError());
1660 ret = WriteFile(pipe, buf, count, NULL, NULL);
1661 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1662 break;
1664 case REQ_READ_CONSOLE_FILE:
1665 count = req->u.size;
1666 memset(buf, 0xcc, sizeof(buf));
1667 ret = ReadFile(input, buf, count, &count, NULL );
1668 ok(ret, "ReadFile failed: %lu\n", GetLastError());
1669 ret = WriteFile(pipe, buf, count, NULL, NULL);
1670 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1671 break;
1673 case REQ_READ_CONSOLE_CONTROL:
1675 CONSOLE_READCONSOLE_CONTROL crc;
1676 WCHAR result[1024];
1677 WCHAR *ptr = (void *)((char *)result + sizeof(DWORD));
1679 count = req->u.control.size;
1680 memset(result, 0xcc, sizeof(result));
1681 crc.nLength = sizeof(crc);
1682 crc.dwCtrlWakeupMask = req->u.control.mask;
1683 crc.nInitialChars = wcslen(req->u.control.initial);
1684 crc.dwConsoleKeyState = 0xa5;
1685 memcpy(ptr, req->u.control.initial, crc.nInitialChars * sizeof(WCHAR));
1686 ret = ReadConsoleW(input, ptr, count, &count, &crc);
1687 ok(ret, "ReadConsoleW failed: %lu\n", GetLastError());
1688 *(DWORD *)result = crc.dwConsoleKeyState;
1689 ret = WriteFile(pipe, result, sizeof(DWORD) + count * sizeof(WCHAR), NULL, NULL);
1690 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1692 break;
1694 case REQ_SCROLL:
1695 ret = ScrollConsoleScreenBufferW(output, &req->u.scroll.rect, NULL, req->u.scroll.dst, &req->u.scroll.fill);
1696 ok(ret, "ScrollConsoleScreenBuffer failed: %lu\n", GetLastError());
1697 break;
1699 case REQ_FILL_CHAR:
1700 ret = FillConsoleOutputCharacterW(output, req->u.fill.ch, req->u.fill.count, req->u.fill.coord, &count);
1701 ok(ret, "FillConsoleOutputCharacter failed: %lu\n", GetLastError());
1702 ok(count == req->u.fill.count, "count = %lu, expected %lu\n", count, req->u.fill.count);
1703 break;
1705 case REQ_SET_ACTIVE:
1706 output = req->u.handle;
1707 ret = SetConsoleActiveScreenBuffer(output);
1708 ok(ret, "SetConsoleActiveScreenBuffer failed: %lu\n", GetLastError());
1709 break;
1711 case REQ_SET_CURSOR:
1712 ret = SetConsoleCursorPosition(output, req->u.coord);
1713 ok(ret, "SetConsoleCursorPosition failed: %lu\n", GetLastError());
1714 break;
1716 case REQ_SET_INPUT_CP:
1717 ret = SetConsoleCP(req->u.cp);
1718 ok(ret, "SetConsoleCP failed: %lu\n", GetLastError());
1719 break;
1721 case REQ_SET_INPUT_MODE:
1722 ret = SetConsoleMode(input, req->u.mode);
1723 ok(ret, "SetConsoleMode failed: %lu\n", GetLastError());
1724 break;
1726 case REQ_SET_OUTPUT_MODE:
1727 ret = SetConsoleMode(output, req->u.mode);
1728 ok(ret, "SetConsoleMode failed: %lu\n", GetLastError());
1729 break;
1731 case REQ_SET_TITLE:
1732 ret = SetConsoleTitleW(req->u.string);
1733 ok(ret, "SetConsoleTitleW failed: %lu\n", GetLastError());
1734 break;
1736 case REQ_WRITE_CHARACTERS:
1737 ret = WriteConsoleOutputCharacterW(output, req->u.write_characters.buf,
1738 req->u.write_characters.len,
1739 req->u.write_characters.coord, &count);
1740 ok(ret, "WriteConsoleOutputCharacterW failed: %lu\n", GetLastError());
1741 break;
1743 case REQ_WRITE_CONSOLE:
1744 ret = WriteConsoleW(output, req->u.string, lstrlenW(req->u.string), NULL, NULL);
1745 ok(ret, "SetConsoleTitleW failed: %lu\n", GetLastError());
1746 break;
1748 case REQ_WRITE_OUTPUT:
1750 SMALL_RECT region = req->u.write_output.region;
1751 ret = WriteConsoleOutputW(output, req->u.write_output.buf, req->u.write_output.size, req->u.write_output.coord, &region);
1752 ok(ret, "WriteConsoleOutput failed: %lu\n", GetLastError());
1753 ret = WriteFile(pipe, &region, sizeof(region), &count, NULL);
1754 ok(ret, "WriteFile failed: %lu\n", GetLastError());
1755 break;
1758 default:
1759 ok(0, "unexpected request type %u\n", req->type);
1762 ok(GetLastError() == ERROR_BROKEN_PIPE, "ReadFile failed: %lu\n", GetLastError());
1763 CloseHandle(output);
1764 CloseHandle(input);
1767 static HANDLE run_child(HANDLE console, HANDLE pipe)
1769 STARTUPINFOEXA startup = {{ sizeof(startup) }};
1770 char **argv, cmdline[MAX_PATH];
1771 PROCESS_INFORMATION info;
1772 SIZE_T size;
1773 BOOL ret;
1775 InitializeProcThreadAttributeList(NULL, 1, 0, &size);
1776 startup.lpAttributeList = HeapAlloc(GetProcessHeap(), 0, size);
1777 InitializeProcThreadAttributeList(startup.lpAttributeList, 1, 0, &size);
1778 UpdateProcThreadAttribute(startup.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, console,
1779 sizeof(console), NULL, NULL);
1781 winetest_get_mainargs(&argv);
1782 sprintf(cmdline, "\"%s\" %s child %p", argv[0], argv[1], pipe);
1783 ret = CreateProcessA(NULL, cmdline, NULL, NULL, TRUE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
1784 &startup.StartupInfo, &info);
1785 ok(ret, "CreateProcessW failed: %lu\n", GetLastError());
1787 CloseHandle(info.hThread);
1788 HeapFree(GetProcessHeap(), 0, startup.lpAttributeList);
1789 return info.hProcess;
1792 static HPCON create_pseudo_console(HANDLE *console_pipe_end, HANDLE *child_process)
1794 SECURITY_ATTRIBUTES sec_attr = { sizeof(sec_attr), NULL, TRUE };
1795 HANDLE child_pipe_end;
1796 COORD size = { 30, 40 };
1797 DWORD read_mode;
1798 HPCON console;
1799 HRESULT hres;
1800 BOOL r;
1802 console_pipe = CreateNamedPipeW(L"\\\\.\\pipe\\pseudoconsoleconn", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
1803 PIPE_WAIT | PIPE_TYPE_BYTE, 1, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, NULL);
1804 ok(console_pipe != INVALID_HANDLE_VALUE, "CreateNamedPipeW failed: %lu\n", GetLastError());
1806 *console_pipe_end = CreateFileW(L"\\\\.\\pipe\\pseudoconsoleconn", GENERIC_READ | GENERIC_WRITE,
1807 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
1808 ok(*console_pipe_end != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError());
1810 child_pipe = CreateNamedPipeW(L"\\\\.\\pipe\\pseudoconsoleserver", PIPE_ACCESS_DUPLEX,
1811 PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 5000, 6000,
1812 NMPWAIT_USE_DEFAULT_WAIT, NULL);
1813 ok(child_pipe != INVALID_HANDLE_VALUE, "CreateNamedPipeW failed: %lu\n", GetLastError());
1815 child_pipe_end = CreateFileW(L"\\\\.\\pipe\\pseudoconsoleserver", GENERIC_READ | GENERIC_WRITE, 0,
1816 &sec_attr, OPEN_EXISTING, 0, NULL);
1817 ok(child_pipe_end != INVALID_HANDLE_VALUE, "CreateFile failed: %lu\n", GetLastError());
1819 read_mode = PIPE_READMODE_MESSAGE;
1820 r = SetNamedPipeHandleState(child_pipe_end, &read_mode, NULL, NULL);
1821 ok(r, "SetNamedPipeHandleState failed: %lu\n", GetLastError());
1823 hres = pCreatePseudoConsole(size, *console_pipe_end, *console_pipe_end, 0, &console);
1824 ok(hres == S_OK, "CreatePseudoConsole failed: %08lx\n", hres);
1826 *child_process = run_child(console, child_pipe_end);
1827 CloseHandle(child_pipe_end);
1828 return console;
1831 static void test_pseudoconsole(void)
1833 HANDLE console_pipe_end, child_process;
1834 BOOL broken_version;
1835 HPCON console;
1837 console = create_pseudo_console(&console_pipe_end, &child_process);
1839 child_string_request(REQ_SET_TITLE, L"test title");
1840 expect_output_sequence("\x1b[2J"); /* erase display */
1841 skip_hide_cursor();
1842 expect_output_sequence("\x1b[m"); /* default attributes */
1843 expect_output_sequence("\x1b[H"); /* set cursor */
1844 skip_sequence("\x1b[H"); /* some windows versions emit it twice */
1845 expect_output_sequence("\x1b]0;test title"); /* set title */
1846 broken_version = skip_byte(0); /* some win versions emit nullbyte */
1847 expect_output_sequence("\x07");
1848 skip_sequence("\x1b[?25h"); /* show cursor */
1849 expect_empty_output();
1851 if (!broken_version)
1853 if (test_tty_output())
1855 test_read_console_control();
1856 test_read_console();
1857 test_tty_input();
1859 else
1860 win_skip("Partially supported windows version. Skipping rest of the tests\n");
1862 else win_skip("Skipping tty tests on broken Windows version\n");
1864 CloseHandle(child_pipe);
1865 wait_child_process(child_process);
1866 CloseHandle(child_process);
1868 /* native sometimes clears the screen here */
1869 if (skip_sequence("\x1b[25l"))
1871 unsigned int i;
1872 skip_sequence("\x1b[H");
1873 for (i = 0; i < 40; i++)
1875 expect_output_sequence("\x1b[K");
1876 if (i != 39) expect_output_sequence("\r\n");
1878 skip_sequence("\x1b[H\x1b[?25h");
1879 /* native sometimes redraws the screen afterwards */
1880 if (skip_sequence("\x1b[25l"))
1882 unsigned int line_feed = 0;
1883 /* not checking exact output, depends too heavily on previous tests */
1884 for (i = 0; i < console_output_count - 2; i++)
1885 if (!memcmp(console_output + i, "\r\n", 2)) line_feed++;
1886 ok(line_feed == 39, "Wrong number of line feeds %u\n", line_feed);
1887 console_output_count = 0;
1890 expect_empty_output();
1892 pClosePseudoConsole(console);
1893 CloseHandle(console_pipe_end);
1894 CloseHandle(console_pipe);
1897 START_TEST(tty)
1899 HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
1900 char **argv;
1901 int argc;
1903 argc = winetest_get_mainargs(&argv);
1904 if (argc > 3)
1906 HANDLE pipe;
1907 DWORD mode;
1908 sscanf(argv[3], "%p", &pipe);
1909 /* if std output is console, silence debug output so it does not interfere with tests */
1910 if (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode))
1911 winetest_debug = 0;
1912 child_process(pipe);
1913 return;
1916 pCreatePseudoConsole = (void *)GetProcAddress(kernel32, "CreatePseudoConsole");
1917 pClosePseudoConsole = (void *)GetProcAddress(kernel32, "ClosePseudoConsole");
1918 if (!pCreatePseudoConsole)
1920 win_skip("CreatePseudoConsole is not available\n");
1921 return;
1924 test_pseudoconsole();