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"
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
)
38 if (console_output_count
== sizeof(console_output
)) return;
40 o
.hEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
41 ret
= ReadFile(console_pipe
, console_output
+ console_output_count
,
42 sizeof(console_output
) - console_output_count
, NULL
, &o
);
45 ok_(__FILE__
,line
)(GetLastError() == ERROR_IO_PENDING
, "read failed: %u\n", GetLastError());
46 if (GetLastError() != ERROR_IO_PENDING
) return;
47 WaitForSingleObject(o
.hEvent
, 1000);
49 ret
= GetOverlappedResult(console_pipe
, &o
, &count
, FALSE
);
50 if (!ret
&& GetLastError() == ERROR_IO_PENDING
)
51 CancelIoEx(console_pipe
, &o
);
53 ok_(__FILE__
,line
)(ret
, "Read file failed: %u\n", GetLastError());
54 CloseHandle(o
.hEvent
);
55 if (ret
) console_output_count
+= count
;
58 #define expect_empty_output() expect_empty_output_(__LINE__)
59 static void expect_empty_output_(unsigned int line
)
64 ret
= PeekNamedPipe(console_pipe
, NULL
, 0, NULL
, &avail
, NULL
);
65 ok_(__FILE__
,line
)(ret
, "PeekNamedPipe failed: %u\n", GetLastError());
66 ok_(__FILE__
,line
)(!avail
, "avail = %u\n", avail
);
67 if (avail
) fetch_console_output_(line
);
68 ok_(__FILE__
,line
)(!console_output_count
, "expected empty buffer, got %s\n",
69 wine_dbgstr_an(console_output
, console_output_count
));
70 console_output_count
= 0;
73 #define expect_output_sequence(a) expect_output_sequence_(__LINE__,0,a)
74 #define expect_output_sequence_ctx(a,b) expect_output_sequence_(__LINE__,a,b)
75 static void expect_output_sequence_(unsigned int line
, unsigned ctx
, const char *expect
)
77 size_t len
= strlen(expect
);
78 if (console_output_count
< len
) fetch_console_output_(line
);
79 if (len
<= console_output_count
&& !memcmp(console_output
, expect
, len
))
81 console_output_count
-= len
;
82 memmove(console_output
, console_output
+ len
, console_output_count
);
84 else ok_(__FILE__
,line
)(0, "%x: expected %s got %s\n", ctx
, wine_dbgstr_a(expect
),
85 wine_dbgstr_an(console_output
, console_output_count
));
88 #define skip_sequence(a) skip_sequence_(__LINE__,a)
89 static BOOL
skip_sequence_(unsigned int line
, const char *expect
)
91 size_t len
= strlen(expect
);
95 r
= PeekNamedPipe(console_pipe
, NULL
, 0, NULL
, &avail
, NULL
);
96 if (!console_output_count
&& r
&& !avail
)
99 r
= PeekNamedPipe(console_pipe
, NULL
, 0, NULL
, &avail
, NULL
);
101 if (r
&& avail
) fetch_console_output_(line
);
103 if (len
> console_output_count
|| memcmp(console_output
, expect
, len
)) return FALSE
;
104 console_output_count
-= len
;
105 memmove(console_output
, console_output
+ len
, console_output_count
);
109 #define skip_byte(a) skip_byte_(__LINE__,a)
110 static BOOL
skip_byte_(unsigned int line
, char ch
)
112 if (!console_output_count
|| console_output
[0] != ch
) return FALSE
;
113 console_output_count
--;
114 memmove(console_output
, console_output
+ 1, console_output_count
);
118 #define expect_hide_cursor() expect_hide_cursor_(__LINE__)
119 static void expect_hide_cursor_(unsigned int line
)
121 if (!console_output_count
) fetch_console_output_(line
);
122 ok_(__FILE__
,line
)(skip_sequence_(line
, "\x1b[?25l") || broken(skip_sequence_(line
, "\x1b[25l")),
123 "expected hide cursor escape\n");
126 #define skip_hide_cursor() skip_hide_cursor_(__LINE__)
127 static BOOL
skip_hide_cursor_(unsigned int line
)
129 if (!console_output_count
) fetch_console_output_(line
);
130 return skip_sequence_(line
, "\x1b[25l") || broken(skip_sequence_(line
, "\x1b[?25l"));
133 #define expect_erase_line(a) expect_erase_line_(__LINE__,a)
134 static BOOL
expect_erase_line_(unsigned line
, unsigned int cnt
)
137 if (skip_sequence("\x1b[K")) return FALSE
;
138 ok(broken(1), "expected erase line\n");
139 sprintf(buf
, "\x1b[%uX", cnt
);
140 expect_output_sequence(buf
); /* erase the rest of the line */
141 sprintf(buf
, "\x1b[%uC", cnt
);
142 expect_output_sequence(buf
); /* move cursor to the end of the line */
148 REQ_CREATE_SCREEN_BUFFER
,
155 REQ_WRITE_CHARACTERS
,
159 struct pseudoconsole_req
195 static void child_string_request(enum req_type type
, const WCHAR
*title
)
198 struct pseudoconsole_req
*req
= (void *)buf
;
199 size_t len
= lstrlenW(title
) + 1;
204 memcpy(req
->u
.string
, title
, len
* sizeof(WCHAR
));
205 ret
= WriteFile(child_pipe
, req
, FIELD_OFFSET(struct pseudoconsole_req
, u
.string
[len
]),
207 ok(ret
, "WriteFile failed: %u\n", GetLastError());
210 static void child_write_characters(const WCHAR
*buf
, unsigned int x
, unsigned int y
)
213 struct pseudoconsole_req
*req
= (void *)req_buf
;
214 size_t len
= lstrlenW(buf
);
218 req
->type
= REQ_WRITE_CHARACTERS
;
219 req
->u
.write_characters
.coord
.X
= x
;
220 req
->u
.write_characters
.coord
.Y
= y
;
221 req
->u
.write_characters
.len
= len
;
222 memcpy(req
->u
.write_characters
.buf
, buf
, len
* sizeof(WCHAR
));
223 ret
= WriteFile(child_pipe
, req
, FIELD_OFFSET(struct pseudoconsole_req
, u
.write_characters
.buf
[len
+ 1]),
225 ok(ret
, "WriteFile failed: %u\n", GetLastError());
228 static void child_set_cursor(const unsigned int x
, unsigned int y
)
230 struct pseudoconsole_req req
;
234 req
.type
= REQ_SET_CURSOR
;
237 ret
= WriteFile(child_pipe
, &req
, sizeof(req
), &count
, NULL
);
238 ok(ret
, "WriteFile failed: %u\n", GetLastError());
241 static HANDLE
child_create_screen_buffer(void)
243 struct pseudoconsole_req req
;
248 req
.type
= REQ_CREATE_SCREEN_BUFFER
;
249 ret
= WriteFile(child_pipe
, &req
, sizeof(req
), &count
, NULL
);
250 ok(ret
, "WriteFile failed: %u\n", GetLastError());
251 ret
= ReadFile(child_pipe
, &handle
, sizeof(handle
), &count
, NULL
);
252 ok(ret
, "ReadFile failed: %u\n", GetLastError());
256 static void child_set_active(HANDLE handle
)
258 struct pseudoconsole_req req
;
262 req
.type
= REQ_SET_ACTIVE
;
263 req
.u
.handle
= handle
;
264 ret
= WriteFile(child_pipe
, &req
, sizeof(req
), &count
, NULL
);
265 ok(ret
, "WriteFile failed: %u\n", GetLastError());
268 #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)
269 static void child_write_output_(unsigned int line
, CHAR_INFO
*buf
, unsigned int size_x
, unsigned int size_y
,
270 unsigned int coord_x
, unsigned int coord_y
, unsigned int left
,
271 unsigned int top
, unsigned int right
, unsigned int bottom
, unsigned int out_left
,
272 unsigned int out_top
, unsigned int out_right
, unsigned int out_bottom
)
275 struct pseudoconsole_req
*req
= (void *)req_buf
;
280 req
->type
= REQ_WRITE_OUTPUT
;
281 req
->u
.write_output
.size
.X
= size_x
;
282 req
->u
.write_output
.size
.Y
= size_y
;
283 req
->u
.write_output
.coord
.X
= coord_x
;
284 req
->u
.write_output
.coord
.Y
= coord_y
;
285 req
->u
.write_output
.region
.Left
= left
;
286 req
->u
.write_output
.region
.Top
= top
;
287 req
->u
.write_output
.region
.Right
= right
;
288 req
->u
.write_output
.region
.Bottom
= bottom
;
289 memcpy(req
->u
.write_output
.buf
, buf
, size_x
* size_y
* sizeof(*buf
));
290 ret
= WriteFile(child_pipe
, req
, FIELD_OFFSET(struct pseudoconsole_req
, u
.write_output
.buf
[size_x
* size_y
]), &count
, NULL
);
291 ok_(__FILE__
,line
)(ret
, "WriteFile failed: %u\n", GetLastError());
292 ret
= ReadFile(child_pipe
, ®ion
, sizeof(region
), &count
, NULL
);
293 ok_(__FILE__
,line
)(ret
, "WriteFile failed: %u\n", GetLastError());
294 ok_(__FILE__
,line
)(region
.Left
== out_left
, "Left = %u\n", region
.Left
);
295 ok_(__FILE__
,line
)(region
.Top
== out_top
, "Top = %u\n", region
.Top
);
296 ok_(__FILE__
,line
)(region
.Right
== out_right
, "Right = %u\n", region
.Right
);
297 ok_(__FILE__
,line
)(region
.Bottom
== out_bottom
, "Bottom = %u\n", region
.Bottom
);
300 static void child_scroll(unsigned int src_left
, unsigned int src_top
, unsigned int src_right
,
301 unsigned int src_bottom
, unsigned int dst_x
, unsigned int dst_y
, WCHAR fill
)
303 struct pseudoconsole_req req
;
307 req
.type
= REQ_SCROLL
;
308 req
.u
.scroll
.rect
.Left
= src_left
;
309 req
.u
.scroll
.rect
.Top
= src_top
;
310 req
.u
.scroll
.rect
.Right
= src_right
;
311 req
.u
.scroll
.rect
.Bottom
= src_bottom
;
312 req
.u
.scroll
.dst
.X
= dst_x
;
313 req
.u
.scroll
.dst
.Y
= dst_y
;
314 req
.u
.scroll
.fill
.Char
.UnicodeChar
= fill
;
315 req
.u
.scroll
.fill
.Attributes
= 0;
316 ret
= WriteFile(child_pipe
, &req
, sizeof(req
), &count
, NULL
);
317 ok(ret
, "WriteFile failed: %u\n", GetLastError());
320 static void child_fill_character(WCHAR ch
, DWORD count
, int x
, int y
)
322 struct pseudoconsole_req req
;
325 req
.type
= REQ_FILL_CHAR
;
327 req
.u
.fill
.count
= count
;
328 req
.u
.fill
.coord
.X
= x
;
329 req
.u
.fill
.coord
.Y
= y
;
330 ret
= WriteFile(child_pipe
, &req
, sizeof(req
), &count
, NULL
);
331 ok(ret
, "WriteFile failed: %u\n", GetLastError());
334 static void expect_input(unsigned int event_type
, INPUT_RECORD
*record
)
336 struct pseudoconsole_req req
= { REQ_GET_INPUT
};
341 ret
= WriteFile(child_pipe
, &req
, sizeof(req
), &read
, NULL
);
342 ok(ret
, "WriteFile failed: %u\n", GetLastError());
344 ret
= ReadFile(child_pipe
, &input
, sizeof(input
), &read
, NULL
);
345 ok(ret
, "ReadFile failed: %u\n", GetLastError());
347 ok(input
.EventType
== event_type
, "EventType = %u, expected %u\n", input
.EventType
, event_type
);
348 if (record
) *record
= input
;
351 static BOOL
get_key_input(unsigned int vt
, INPUT_RECORD
*record
)
353 static INPUT_RECORD prev_record
;
354 static BOOL have_prev_record
;
356 if (!have_prev_record
)
358 expect_input(KEY_EVENT
, &prev_record
);
359 have_prev_record
= TRUE
;
362 if (vt
&& prev_record
.Event
.KeyEvent
.wVirtualKeyCode
!= vt
) return FALSE
;
363 *record
= prev_record
;
364 have_prev_record
= FALSE
;
368 #define expect_key_input(a,b,c,d) expect_key_input_(__LINE__,0,a,b,c,d)
369 static void expect_key_input_(unsigned int line
, unsigned int ctx
, WCHAR ch
, unsigned int vk
,
370 BOOL down
, unsigned int ctrl_state
)
372 unsigned int vs
= MapVirtualKeyW(vk
, MAPVK_VK_TO_VSC
);
375 get_key_input(0, &record
);
376 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.bKeyDown
== down
, "%x: bKeyDown = %x\n",
377 ctx
, record
.Event
.KeyEvent
.bKeyDown
);
378 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.wRepeatCount
== 1, "%x: wRepeatCount = %x\n",
379 ctx
, record
.Event
.KeyEvent
.wRepeatCount
);
380 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.uChar
.UnicodeChar
== ch
, "%x: UnicodeChar = %x\n",
381 ctx
, record
.Event
.KeyEvent
.uChar
.UnicodeChar
);
382 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.wVirtualKeyCode
== vk
,
383 "%x: wVirtualKeyCode = %x, expected %x\n", ctx
,
384 record
.Event
.KeyEvent
.wVirtualKeyCode
, vk
);
385 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.wVirtualScanCode
== vs
,
386 "%x: wVirtualScanCode = %x expected %x\n", ctx
,
387 record
.Event
.KeyEvent
.wVirtualScanCode
, vs
);
388 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.dwControlKeyState
== ctrl_state
,
389 "%x: dwControlKeyState = %x\n", ctx
, record
.Event
.KeyEvent
.dwControlKeyState
);
392 #define get_input_key_vt() get_input_key_vt_(__LINE__)
393 static unsigned int get_input_key_vt_(unsigned int line
)
397 get_key_input(0, &record
);
398 ok_(__FILE__
,line
)(record
.Event
.KeyEvent
.wRepeatCount
== 1, "wRepeatCount = %x\n",
399 record
.Event
.KeyEvent
.wRepeatCount
);
400 return record
.Event
.KeyEvent
.wVirtualKeyCode
;
403 #define expect_key_pressed(a,b,c) expect_key_pressed_(__LINE__,0,a,b,c)
404 #define expect_key_pressed_ctx(a,b,c,d) expect_key_pressed_(__LINE__,a,b,c,d)
405 static void expect_key_pressed_(unsigned int line
, unsigned int ctx
, WCHAR ch
, unsigned int vk
,
406 unsigned int ctrl_state
)
408 if (ctrl_state
& SHIFT_PRESSED
)
409 expect_key_input_(line
, ctx
, 0, VK_SHIFT
, TRUE
, SHIFT_PRESSED
);
410 if (ctrl_state
& LEFT_ALT_PRESSED
)
411 expect_key_input_(line
, ctx
, 0, VK_MENU
, TRUE
,
412 LEFT_ALT_PRESSED
| (ctrl_state
& SHIFT_PRESSED
));
413 if (ctrl_state
& LEFT_CTRL_PRESSED
)
414 expect_key_input_(line
, ctx
, 0, VK_CONTROL
, TRUE
,
415 LEFT_CTRL_PRESSED
| (ctrl_state
& (SHIFT_PRESSED
| LEFT_ALT_PRESSED
)));
416 expect_key_input_(line
, ctx
, ch
, vk
, TRUE
, ctrl_state
);
417 expect_key_input_(line
, ctx
, ch
, vk
, FALSE
, ctrl_state
);
418 if (ctrl_state
& LEFT_CTRL_PRESSED
)
419 expect_key_input_(line
, ctx
, 0, VK_CONTROL
, FALSE
,
420 ctrl_state
& (SHIFT_PRESSED
| LEFT_ALT_PRESSED
));
421 if (ctrl_state
& LEFT_ALT_PRESSED
)
422 expect_key_input_(line
, ctx
, 0, VK_MENU
, FALSE
, ctrl_state
& SHIFT_PRESSED
);
423 if (ctrl_state
& SHIFT_PRESSED
)
424 expect_key_input_(line
, ctx
, 0, VK_SHIFT
, FALSE
, 0);
427 #define expect_char_key(a) expect_char_key_(__LINE__,a)
428 static void expect_char_key_(unsigned int line
, WCHAR ch
)
430 unsigned int ctrl
= 0, vk
;
432 if (vk
== ~0) vk
= 0;
433 if (vk
& 0x0100) ctrl
|= SHIFT_PRESSED
;
434 if (vk
& 0x0200) ctrl
|= LEFT_CTRL_PRESSED
;
436 expect_key_pressed_(line
, ch
, ch
, vk
, ctrl
);
439 static void test_tty_output(void)
441 CHAR_INFO char_info_buf
[2048], char_info
;
445 /* simple write chars */
446 child_write_characters(L
"child", 3, 4);
447 expect_hide_cursor();
448 expect_output_sequence("\x1b[5;4H"); /* set cursor */
449 expect_output_sequence("child");
450 expect_output_sequence("\x1b[H"); /* set cursor */
451 expect_output_sequence("\x1b[?25h"); /* show cursor */
452 expect_empty_output();
454 /* wrapped write chars */
455 child_write_characters(L
"bound", 28, 6);
456 expect_hide_cursor();
457 expect_output_sequence("\x1b[7;1H"); /* set cursor */
458 expect_output_sequence(" bo\r\nund");
459 expect_erase_line(27);
460 expect_output_sequence("\x1b[H"); /* set cursor */
461 expect_output_sequence("\x1b[?25h"); /* show cursor */
462 expect_empty_output();
464 /* fill line 4 with a few simple writes */
465 child_write_characters(L
"xxx", 13, 4);
466 expect_hide_cursor();
467 expect_output_sequence("\x1b[5;14H"); /* set cursor */
468 expect_output_sequence("xxx");
469 expect_output_sequence("\x1b[H"); /* set cursor */
470 expect_output_sequence("\x1b[?25h"); /* show cursor */
471 expect_empty_output();
473 /* write one char at the end of row */
474 child_write_characters(L
"y", 29, 4);
475 expect_hide_cursor();
476 expect_output_sequence("\x1b[5;30H"); /* set cursor */
477 expect_output_sequence("y");
478 expect_output_sequence("\x1b[H"); /* set cursor */
479 expect_output_sequence("\x1b[?25h"); /* show cursor */
480 expect_empty_output();
482 /* wrapped write chars */
483 child_write_characters(L
"zz", 29, 4);
484 expect_hide_cursor();
485 expect_output_sequence("\x1b[5;1H"); /* set cursor */
486 expect_output_sequence(" child xxx z");
487 expect_output_sequence("\r\nz");
488 expect_erase_line(29);
489 expect_output_sequence("\x1b[H"); /* set cursor */
490 expect_output_sequence("\x1b[?25h"); /* show cursor */
491 expect_empty_output();
493 /* trailing spaces */
494 child_write_characters(L
"child ", 3, 4);
495 expect_hide_cursor();
496 expect_output_sequence("\x1b[5;4H"); /* set cursor */
497 expect_output_sequence("child ");
498 expect_output_sequence("\x1b[H"); /* set cursor */
499 expect_output_sequence("\x1b[?25h"); /* show cursor */
500 expect_empty_output();
502 child_set_cursor(2, 3);
503 expect_hide_cursor();
504 expect_output_sequence("\x1b[4;3H"); /* set cursor */
505 expect_output_sequence("\x1b[?25h"); /* show cursor */
506 expect_empty_output();
508 child_string_request(REQ_SET_TITLE
, L
"new title");
509 fetch_console_output();
510 skip_sequence("\x1b[?25l"); /* hide cursor */
511 expect_output_sequence("\x1b]0;new title\x07"); /* set title */
512 skip_sequence("\x1b[?25h"); /* show cursor */
513 expect_empty_output();
515 for (i
= 0; i
< ARRAY_SIZE(char_info_buf
); i
++)
517 char_info_buf
[i
].Char
.UnicodeChar
= '0' + i
% 10;
518 char_info_buf
[i
].Attributes
= 0;
521 child_write_output(char_info_buf
, /* size */ 7, 8, /* coord */ 1, 2,
522 /* region */ 3, 7, 5, 9, /* out region */ 3, 7, 5, 9);
523 expect_hide_cursor();
524 expect_output_sequence("\x1b[30m"); /* foreground black */
525 expect_output_sequence("\x1b[8;4H"); /* set cursor */
526 expect_output_sequence("567");
527 expect_output_sequence("\x1b[9;4H"); /* set cursor */
528 expect_output_sequence("234");
529 expect_output_sequence("\x1b[10;4H"); /* set cursor */
530 expect_output_sequence("901");
531 expect_output_sequence("\x1b[4;3H"); /* set cursor */
532 expect_output_sequence("\x1b[?25h"); /* show cursor */
533 expect_empty_output();
535 child_write_output(char_info_buf
, /* size */ 2, 3, /* coord */ 1, 2,
536 /* region */ 3, 8, 15, 19, /* out region */ 3, 8, 3, 8);
537 expect_hide_cursor();
538 if (skip_sequence("\x1b[m")) /* default attr */
539 expect_output_sequence("\x1b[30m");/* foreground black */
540 expect_output_sequence("\x1b[9;4H"); /* set cursor */
541 expect_output_sequence("5");
542 expect_output_sequence("\x1b[4;3H"); /* set cursor */
543 expect_output_sequence("\x1b[?25h"); /* show cursor */
544 expect_empty_output();
546 child_write_output(char_info_buf
, /* size */ 3, 4, /* coord */ 1, 2,
547 /* region */ 3, 8, 15, 19, /* out region */ 3, 8, 4, 9);
548 expect_hide_cursor();
549 if (skip_sequence("\x1b[m")) /* default attr */
550 expect_output_sequence("\x1b[30m");/* foreground black */
551 expect_output_sequence("\x1b[9;4H"); /* set cursor */
552 expect_output_sequence("78");
553 expect_output_sequence("\x1b[10;4H"); /* set cursor */
554 expect_output_sequence("01");
555 expect_output_sequence("\x1b[4;3H"); /* set cursor */
556 expect_output_sequence("\x1b[?25h"); /* show cursor */
557 expect_empty_output();
559 child_write_output(char_info_buf
, /* size */ 7, 8, /* coord */ 2, 3,
560 /* region */ 28, 38, 31, 60, /* out region */ 28, 38, 29, 39);
561 expect_hide_cursor();
562 if (skip_sequence("\x1b[m")) /* default attr */
563 expect_output_sequence("\x1b[30m");/* foreground black */
564 expect_output_sequence("\x1b[39;29H"); /* set cursor */
565 expect_output_sequence("34");
566 expect_output_sequence("\x1b[40;29H"); /* set cursor */
567 expect_output_sequence("01");
568 expect_output_sequence("\x1b[4;3H"); /* set cursor */
569 expect_output_sequence("\x1b[?25h"); /* show cursor */
570 expect_empty_output();
572 child_write_output(char_info_buf
, /* size */ 7, 8, /* coord */ 1, 2,
573 /* region */ 0, 7, 5, 9, /* out region */ 0, 7, 5, 9);
574 expect_hide_cursor();
575 if (skip_sequence("\x1b[m")) /* default attr */
576 expect_output_sequence("\x1b[30m");/* foreground black */
577 expect_output_sequence("\x1b[8;1H"); /* set cursor */
578 expect_output_sequence("567890\r\n");
579 expect_output_sequence("234567\r\n");
580 expect_output_sequence("901234");
581 expect_output_sequence("\x1b[4;3H"); /* set cursor */
582 expect_output_sequence("\x1b[?25h"); /* show cursor */
583 expect_empty_output();
585 child_scroll(/* scroll rect */ 0, 7, 2, 8, /* destination */ 2, 8, /* fill */ 'x');
586 expect_hide_cursor();
587 if (skip_sequence("\x1b[m")) /* default attr */
588 expect_output_sequence("\x1b[30m");/* foreground black */
589 expect_output_sequence("\x1b[8;1H"); /* set cursor */
590 expect_output_sequence("xxx89\r\n");
591 expect_output_sequence("xx567\r\n");
592 expect_output_sequence("90234");
593 expect_output_sequence("\x1b[4;3H"); /* set cursor */
594 expect_output_sequence("\x1b[?25h"); /* show cursor */
595 expect_empty_output();
597 child_write_characters(L
"xxx", 3, 10);
598 expect_hide_cursor();
599 expect_output_sequence("\x1b[m"); /* default attributes */
600 expect_output_sequence("\x1b[11;4H"); /* set cursor */
601 expect_output_sequence("xxx");
602 expect_output_sequence("\x1b[4;3H"); /* set cursor */
603 expect_output_sequence("\x1b[?25h"); /* show cursor */
604 expect_empty_output();
606 /* test attributes */
607 for (i
= 0; i
< 0x100 - 0xff; i
++)
611 char_info
.Char
.UnicodeChar
= 'a';
612 char_info
.Attributes
= i
;
613 child_write_output(&char_info
, /* size */ 1, 1, /* coord */ 0, 0,
614 /* region */ 12, 3, 12, 3, /* out region */ 12, 3, 12, 3);
615 expect_hide_cursor();
616 if (i
!= 0x190 && i
&& ((i
& 0xff) != 8)) expect_output_sequence_ctx(i
, "\x1b[m");
620 if (i
& FOREGROUND_BLUE
) expect
+= 4;
621 if (i
& FOREGROUND_GREEN
) expect
+= 2;
622 if (i
& FOREGROUND_RED
) expect
+= 1;
623 if (i
& FOREGROUND_INTENSITY
) expect
+= 60;
624 sprintf(expect_buf
, "\x1b[%um", expect
);
625 expect_output_sequence_ctx(i
, expect_buf
);
630 if (i
& BACKGROUND_BLUE
) expect
+= 4;
631 if (i
& BACKGROUND_GREEN
) expect
+= 2;
632 if (i
& BACKGROUND_RED
) expect
+= 1;
633 if (i
& BACKGROUND_INTENSITY
) expect
+= 60;
634 sprintf(expect_buf
, "\x1b[%um", expect
);
635 expect_output_sequence_ctx(i
, expect_buf
);
637 if (!skip_sequence("\x1b[10C"))
638 expect_output_sequence_ctx(i
, "\x1b[4;13H"); /* set cursor */
639 expect_output_sequence("a");
640 if (!skip_sequence("\x1b[11D"))
641 expect_output_sequence("\x1b[4;3H"); /* set cursor */
642 expect_output_sequence("\x1b[?25h"); /* show cursor */
643 expect_empty_output();
646 char_info_buf
[0].Attributes
= FOREGROUND_GREEN
;
647 char_info_buf
[1].Attributes
= FOREGROUND_GREEN
| BACKGROUND_RED
;
648 char_info_buf
[2].Attributes
= BACKGROUND_RED
;
649 child_write_output(char_info_buf
, /* size */ 7, 8, /* coord */ 0, 0,
650 /* region */ 7, 0, 9, 0, /* out region */ 7, 0, 9, 0);
651 expect_hide_cursor();
652 skip_sequence("\x1b[m"); /* default attr */
653 expect_output_sequence("\x1b[32m"); /* foreground black */
654 expect_output_sequence("\x1b[1;8H"); /* set cursor */
655 expect_output_sequence("0");
656 expect_output_sequence("\x1b[41m"); /* backgorund red */
657 expect_output_sequence("1");
658 expect_output_sequence("\x1b[30m"); /* foreground black */
659 expect_output_sequence("2");
660 expect_output_sequence("\x1b[4;3H"); /* set cursor */
661 expect_output_sequence("\x1b[?25h"); /* show cursor */
662 expect_empty_output();
664 child_fill_character('i', 5, 15, 16);
665 expect_hide_cursor();
666 expect_output_sequence("\x1b[m"); /* default attributes */
667 expect_output_sequence("\x1b[17;16H"); /* set cursor */
668 expect_output_sequence("iiiii");
669 expect_output_sequence("\x1b[4;3H"); /* set cursor */
670 expect_output_sequence("\x1b[?25h"); /* show cursor */
671 expect_empty_output();
673 sb
= child_create_screen_buffer();
674 child_set_active(sb
);
675 expect_hide_cursor();
676 expect_output_sequence("\x1b[H"); /* set cursor */
677 for (i
= 0; i
< 40; i
++)
679 expect_erase_line(30);
680 if (i
!= 39) expect_output_sequence("\r\n");
682 expect_output_sequence("\x1b[H"); /* set cursor */
683 expect_output_sequence("\x1b[?25h"); /* show cursor */
684 expect_empty_output();
686 child_write_characters(L
"new sb", 0, 0);
688 expect_output_sequence("new sb");
689 ok(skip_sequence("\x1b[H") || skip_sequence("\r"), "expected set cursor\n");
690 skip_sequence("\x1b[?25h"); /* show cursor */
691 expect_empty_output();
693 sb2
= child_create_screen_buffer();
694 child_set_active(sb2
);
695 expect_hide_cursor();
696 for (i
= 0; i
< 40; i
++)
698 expect_erase_line(30);
699 if (i
!= 39) expect_output_sequence("\r\n");
701 expect_output_sequence("\x1b[H"); /* set cursor */
702 expect_output_sequence("\x1b[?25h"); /* show cursor */
703 expect_empty_output();
705 child_set_active(sb
);
706 expect_hide_cursor();
707 expect_output_sequence("new sb");
708 expect_erase_line(24);
709 expect_output_sequence("\r\n");
710 for (i
= 1; i
< 40; i
++)
712 expect_erase_line(30);
713 if (i
!= 39) expect_output_sequence("\r\n");
715 expect_output_sequence("\x1b[H"); /* set cursor */
716 expect_output_sequence("\x1b[?25h"); /* show cursor */
717 expect_empty_output();
720 static void write_console_pipe(const char *buf
)
724 res
= WriteFile(console_pipe
, buf
, strlen(buf
), &written
, NULL
);
725 ok(res
, "WriteFile failed: %u\n", GetLastError());
728 static void test_tty_input(void)
741 { "\x1b[A", 0, VK_UP
, 0 },
742 { "\x1b[B", 0, VK_DOWN
, 0 },
743 { "\x1b[C", 0, VK_RIGHT
, 0 },
744 { "\x1b[D", 0, VK_LEFT
, 0 },
745 { "\x1b[H", 0, VK_HOME
, 0 },
746 { "\x1b[F", 0, VK_END
, 0 },
747 { "\x1b[2~", 0, VK_INSERT
, 0 },
748 { "\x1b[3~", 0, VK_DELETE
, 0 },
749 { "\x1b[5~", 0, VK_PRIOR
, 0 },
750 { "\x1b[6~", 0, VK_NEXT
, 0 },
751 { "\x1b[15~", 0, VK_F5
, 0 },
752 { "\x1b[17~", 0, VK_F6
, 0 },
753 { "\x1b[18~", 0, VK_F7
, 0 },
754 { "\x1b[19~", 0, VK_F8
, 0 },
755 { "\x1b[20~", 0, VK_F9
, 0 },
756 { "\x1b[21~", 0, VK_F10
, 0 },
758 { "\x1b[23~", 0, VK_F11
, 0 },
759 { "\x1b[24~", 0, VK_F12
, 0 },
760 { "\x1bOP", 0, VK_F1
, 0 },
761 { "\x1bOQ", 0, VK_F2
, 0 },
762 { "\x1bOR", 0, VK_F3
, 0 },
763 { "\x1bOS", 0, VK_F4
, 0 },
764 { "\x1b[1;1A", 0, VK_UP
, 0 },
765 { "\x1b[1;2A", 0, VK_UP
, SHIFT_PRESSED
},
766 { "\x1b[1;3A", 0, VK_UP
, LEFT_ALT_PRESSED
},
767 { "\x1b[1;4A", 0, VK_UP
, SHIFT_PRESSED
| LEFT_ALT_PRESSED
},
768 { "\x1b[1;5A", 0, VK_UP
, LEFT_CTRL_PRESSED
},
769 { "\x1b[1;6A", 0, VK_UP
, SHIFT_PRESSED
| LEFT_CTRL_PRESSED
},
770 { "\x1b[1;7A", 0, VK_UP
, LEFT_ALT_PRESSED
| LEFT_CTRL_PRESSED
},
771 { "\x1b[1;8A", 0, VK_UP
, SHIFT_PRESSED
| LEFT_ALT_PRESSED
| LEFT_CTRL_PRESSED
},
772 { "\x1b[1;9A", 0, VK_UP
, 0 },
773 { "\x1b[1;10A", 0, VK_UP
, SHIFT_PRESSED
},
775 { "\x1b[1;11A", 0, VK_UP
, LEFT_ALT_PRESSED
},
776 { "\x1b[1;12A", 0, VK_UP
, SHIFT_PRESSED
| LEFT_ALT_PRESSED
},
777 { "\x1b[1;13A", 0, VK_UP
, LEFT_CTRL_PRESSED
},
778 { "\x1b[1;14A", 0, VK_UP
, SHIFT_PRESSED
| LEFT_CTRL_PRESSED
},
779 { "\x1b[1;15A", 0, VK_UP
, LEFT_ALT_PRESSED
| LEFT_CTRL_PRESSED
},
780 { "\x1b[1;16A", 0, VK_UP
, SHIFT_PRESSED
| LEFT_ALT_PRESSED
| LEFT_CTRL_PRESSED
},
781 { "\x1b[1;2P", 0, VK_F1
, SHIFT_PRESSED
},
782 { "\x1b[2;3~", 0, VK_INSERT
, LEFT_ALT_PRESSED
},
783 { "\x1b[2;3;5;6~", 0, VK_INSERT
, 0 },
784 { "\x1b[6;2;3;5;1~", 0, VK_NEXT
, 0 },
785 { "\xe4\xb8\x80", 0x4e00, 0, 0 },
786 { "\x1b\x1b", 0x1b, VK_ESCAPE
, LEFT_ALT_PRESSED
},
787 { "\x1b""1", '1', '1', LEFT_ALT_PRESSED
},
788 { "\x1b""x", 'x', 'X', LEFT_ALT_PRESSED
},
789 { "\x1b""[", '[', VK_OEM_4
, LEFT_ALT_PRESSED
},
790 { "\x7f", '\b', VK_BACK
, 0 },
793 write_console_pipe("x");
794 if (!get_input_key_vt())
796 skip("Skipping tests on settings that don't have VT mapping for 'x'\n");
802 write_console_pipe("aBCd");
803 expect_char_key('a');
804 expect_char_key('B');
805 expect_char_key('C');
806 expect_char_key('d');
808 for (i
= 1; i
< 0x7f; i
++)
810 if (i
== 3 || i
== '\n' || i
== 0x1b || i
== 0x1f) continue;
813 write_console_pipe(buf
);
815 expect_key_pressed('\b', 'H', LEFT_CTRL_PRESSED
);
822 write_console_pipe("\r\n");
823 expect_key_pressed('\r', VK_RETURN
, 0);
824 expect_key_pressed('\n', VK_RETURN
, LEFT_CTRL_PRESSED
);
826 write_console_pipe("\xc4\x85");
827 if (get_key_input(VK_MENU
, &ir
))
829 expect_key_input(0x105, 'A', TRUE
, LEFT_CTRL_PRESSED
| RIGHT_ALT_PRESSED
);
830 expect_key_input(0x105, 'A', FALSE
, LEFT_CTRL_PRESSED
| RIGHT_ALT_PRESSED
);
831 expect_key_input(0, VK_MENU
, FALSE
, ENHANCED_KEY
);
835 expect_key_input(0x105, 0, TRUE
, 0);
836 expect_key_input(0x105, 0, FALSE
, 0);
839 for (i
= 0; i
< ARRAY_SIZE(escape_test
); i
++)
841 write_console_pipe(escape_test
[i
].str
);
842 expect_key_pressed_ctx(i
, escape_test
[i
].ch
, escape_test
[i
].vk
, escape_test
[i
].ctrl
);
845 for (i
= 0x80; i
< 0x100; i
+= 11)
849 write_console_pipe(buf
);
850 expect_empty_output();
854 static void child_process(HANDLE pipe
)
856 HANDLE output
, input
;
861 output
= CreateFileA("CONOUT$", GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, 0);
862 ok(output
!= INVALID_HANDLE_VALUE
, "could not open console output\n");
864 input
= CreateFileA("CONIN$", GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, 0);
865 ok(output
!= INVALID_HANDLE_VALUE
, "could not open console output\n");
867 while(ReadFile(pipe
, buf
, sizeof(buf
), &size
, NULL
))
869 const struct pseudoconsole_req
*req
= (void *)buf
;
872 case REQ_CREATE_SCREEN_BUFFER
:
876 SetLastError(0xdeadbeef);
877 handle
= CreateConsoleScreenBuffer(GENERIC_READ
| GENERIC_WRITE
,
878 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
879 CONSOLE_TEXTMODE_BUFFER
, NULL
);
880 ok(handle
!= INVALID_HANDLE_VALUE
, "CreateConsoleScreenBuffer failed: %u\n", GetLastError());
881 ret
= WriteFile(pipe
, &handle
, sizeof(handle
), &count
, NULL
);
882 ok(ret
, "WriteFile failed: %u\n", GetLastError());
889 ret
= ReadConsoleInputW(input
, &record
, 1, &count
);
890 ok(ret
, "ReadConsoleInputW failed: %u\n", GetLastError());
891 ok(count
== 1, "count = %u\n", count
);
892 ret
= WriteFile(pipe
, &record
, sizeof(record
), &count
, NULL
);
893 ok(ret
, "WriteFile failed: %u\n", GetLastError());
898 ret
= ScrollConsoleScreenBufferW(output
, &req
->u
.scroll
.rect
, NULL
, req
->u
.scroll
.dst
, &req
->u
.scroll
.fill
);
899 ok(ret
, "ScrollConsoleScreenBuffer failed: %u\n", GetLastError());
903 ret
= FillConsoleOutputCharacterW(output
, req
->u
.fill
.ch
, req
->u
.fill
.count
, req
->u
.fill
.coord
, &count
);
904 ok(ret
, "FillConsoleOutputCharacter failed: %u\n", GetLastError());
905 ok(count
== req
->u
.fill
.count
, "count = %u, expected %u\n", count
, req
->u
.fill
.count
);
909 output
= req
->u
.handle
;
910 ret
= SetConsoleActiveScreenBuffer(output
);
911 ok(ret
, "SetConsoleActiveScreenBuffer failed: %u\n", GetLastError());
915 ret
= SetConsoleCursorPosition(output
, req
->u
.coord
);
916 ok(ret
, "SetConsoleCursorPosition failed: %u\n", GetLastError());
920 ret
= SetConsoleTitleW(req
->u
.string
);
921 ok(ret
, "SetConsoleTitleW failed: %u\n", GetLastError());
924 case REQ_WRITE_CHARACTERS
:
925 ret
= WriteConsoleOutputCharacterW(output
, req
->u
.write_characters
.buf
,
926 req
->u
.write_characters
.len
,
927 req
->u
.write_characters
.coord
, &count
);
928 ok(ret
, "WriteConsoleOutputCharacterW failed: %u\n", GetLastError());
931 case REQ_WRITE_OUTPUT
:
933 SMALL_RECT region
= req
->u
.write_output
.region
;
934 ret
= WriteConsoleOutputW(output
, req
->u
.write_output
.buf
, req
->u
.write_output
.size
, req
->u
.write_output
.coord
, ®ion
);
935 ok(ret
, "WriteConsoleOutput failed: %u\n", GetLastError());
936 ret
= WriteFile(pipe
, ®ion
, sizeof(region
), &count
, NULL
);
937 ok(ret
, "WriteFile failed: %u\n", GetLastError());
942 ok(0, "unexpected request type %u\n", req
->type
);
945 ok(GetLastError() == ERROR_BROKEN_PIPE
, "ReadFile failed: %u\n", GetLastError());
950 static HANDLE
run_child(HANDLE console
, HANDLE pipe
)
952 STARTUPINFOEXA startup
= {{ sizeof(startup
) }};
953 char **argv
, cmdline
[MAX_PATH
];
954 PROCESS_INFORMATION info
;
958 InitializeProcThreadAttributeList(NULL
, 1, 0, &size
);
959 startup
.lpAttributeList
= HeapAlloc(GetProcessHeap(), 0, size
);
960 InitializeProcThreadAttributeList(startup
.lpAttributeList
, 1, 0, &size
);
961 UpdateProcThreadAttribute(startup
.lpAttributeList
, 0, PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE
, console
,
962 sizeof(console
), NULL
, NULL
);
964 winetest_get_mainargs(&argv
);
965 sprintf(cmdline
, "\"%s\" %s child %p", argv
[0], argv
[1], pipe
);
966 ret
= CreateProcessA(NULL
, cmdline
, NULL
, NULL
, TRUE
, EXTENDED_STARTUPINFO_PRESENT
, NULL
, NULL
,
967 &startup
.StartupInfo
, &info
);
968 ok(ret
, "CreateProcessW failed: %u\n", GetLastError());
970 CloseHandle(info
.hThread
);
971 HeapFree(GetProcessHeap(), 0, startup
.lpAttributeList
);
972 return info
.hProcess
;
975 static HPCON
create_pseudo_console(HANDLE
*console_pipe_end
, HANDLE
*child_process
)
977 SECURITY_ATTRIBUTES sec_attr
= { sizeof(sec_attr
), NULL
, TRUE
};
978 HANDLE child_pipe_end
;
979 COORD size
= { 30, 40 };
985 console_pipe
= CreateNamedPipeW(L
"\\\\.\\pipe\\pseudoconsoleconn", PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
986 PIPE_WAIT
| PIPE_TYPE_BYTE
, 1, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT
, NULL
);
987 ok(console_pipe
!= INVALID_HANDLE_VALUE
, "CreateNamedPipeW failed: %u\n", GetLastError());
989 *console_pipe_end
= CreateFileW(L
"\\\\.\\pipe\\pseudoconsoleconn", GENERIC_READ
| GENERIC_WRITE
,
990 0, &sec_attr
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
, NULL
);
991 ok(*console_pipe_end
!= INVALID_HANDLE_VALUE
, "CreateFile failed: %u\n", GetLastError());
993 child_pipe
= CreateNamedPipeW(L
"\\\\.\\pipe\\pseudoconsoleserver", PIPE_ACCESS_DUPLEX
,
994 PIPE_WAIT
| PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
, 1, 5000, 6000,
995 NMPWAIT_USE_DEFAULT_WAIT
, NULL
);
996 ok(child_pipe
!= INVALID_HANDLE_VALUE
, "CreateNamedPipeW failed: %u\n", GetLastError());
998 child_pipe_end
= CreateFileW(L
"\\\\.\\pipe\\pseudoconsoleserver", GENERIC_READ
| GENERIC_WRITE
, 0,
999 &sec_attr
, OPEN_EXISTING
, 0, NULL
);
1000 ok(child_pipe_end
!= INVALID_HANDLE_VALUE
, "CreateFile failed: %u\n", GetLastError());
1002 read_mode
= PIPE_READMODE_MESSAGE
;
1003 r
= SetNamedPipeHandleState(child_pipe_end
, &read_mode
, NULL
, NULL
);
1004 ok(r
, "SetNamedPipeHandleState failed: %u\n", GetLastError());
1006 hres
= pCreatePseudoConsole(size
, *console_pipe_end
, *console_pipe_end
, 0, &console
);
1007 ok(hres
== S_OK
, "CreatePseudoConsole failed: %08x\n", hres
);
1009 *child_process
= run_child(console
, child_pipe_end
);
1010 CloseHandle(child_pipe_end
);
1014 static void test_pseudoconsole(void)
1016 HANDLE console_pipe_end
, child_process
;
1017 BOOL broken_version
;
1020 console
= create_pseudo_console(&console_pipe_end
, &child_process
);
1022 child_string_request(REQ_SET_TITLE
, L
"test title");
1023 expect_output_sequence("\x1b[2J"); /* erase display */
1025 expect_output_sequence("\x1b[m"); /* default attributes */
1026 expect_output_sequence("\x1b[H"); /* set cursor */
1027 skip_sequence("\x1b[H"); /* some windows versions emit it twice */
1028 expect_output_sequence("\x1b]0;test title"); /* set title */
1029 broken_version
= skip_byte(0); /* some win versions emit nullbyte */
1030 expect_output_sequence("\x07");
1031 skip_sequence("\x1b[?25h"); /* show cursor */
1032 expect_empty_output();
1034 if (!broken_version
)
1039 else win_skip("Skipping tty output tests on broken Windows version\n");
1041 pClosePseudoConsole(console
);
1042 CloseHandle(console_pipe_end
);
1043 CloseHandle(console_pipe
);
1048 HMODULE kernel32
= GetModuleHandleW(L
"kernel32.dll");
1052 argc
= winetest_get_mainargs(&argv
);
1056 sscanf(argv
[3], "%p", &pipe
);
1057 child_process(pipe
);
1061 pCreatePseudoConsole
= (void *)GetProcAddress(kernel32
, "CreatePseudoConsole");
1062 pClosePseudoConsole
= (void *)GetProcAddress(kernel32
, "ClosePseudoConsole");
1063 if (!pCreatePseudoConsole
)
1065 win_skip("CreatePseudoConsole is not available\n");
1069 test_pseudoconsole();