Remember the fact we completed the entire line without altering the cursor. A newline is delayed so it wraps when writing a new character.
This can happen in practice when writing one character at a time to the console; right now it will keep overwriting the last character on the line.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
Note: I don't know why Windows sends a \b if we write two characters at once (i.e. "ab") to complete the line, but not if we write them one at a time ("a" then "b"). Doesn't the latter mess up the tty cursor? It seems like a Windows bug to me. For now I just skipped it in the tests, since Wine outputs it (which is proper when it syncs the tty cursor).
programs/conhost/conhost.c | 27 +++++++++++-- programs/conhost/conhost.h | 1 + programs/conhost/tests/tty.c | 75 ++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 3 deletions(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 7377945..7970eb1 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -1136,6 +1136,7 @@ static void update_read_output( struct console *console ) RECT update_rect;
empty_update_rect( screen_buffer, &update_rect ); + screen_buffer->delayed_newline = 0;
if (ctx->update_end >= ctx->update_begin) { @@ -1359,6 +1360,11 @@ static NTSTATUS read_console( struct console *console, unsigned int ioctl, size_ console->edit_line.home_x = console->active->cursor_x; console->edit_line.home_y = console->active->cursor_y; console->edit_line.status = STATUS_PENDING; + if (console->active->delayed_newline) + { + console->edit_line.home_x = 0; + console->edit_line.home_y++; + } if (edit_line_grow( console, 1 )) console->edit_line.buf[0] = 0;
console->pending_read = out_size; @@ -1827,6 +1833,7 @@ static NTSTATUS set_output_info( struct screen_buffer *screen_buffer, return STATUS_INVALID_PARAMETER; }
+ screen_buffer->delayed_newline = 0; if (screen_buffer->cursor_x != info->cursor_x || screen_buffer->cursor_y != info->cursor_y) { screen_buffer->cursor_x = info->cursor_x; @@ -1856,8 +1863,13 @@ static NTSTATUS set_output_info( struct screen_buffer *screen_buffer, screen_buffer->win.bottom -= screen_buffer->win.top; screen_buffer->win.top = 0; } - if (screen_buffer->cursor_x >= info->width) screen_buffer->cursor_x = info->width - 1; - if (screen_buffer->cursor_y >= info->height) screen_buffer->cursor_y = info->height - 1; + if (screen_buffer->cursor_x >= info->width) + { + screen_buffer->cursor_x = info->width - 1; + screen_buffer->delayed_newline = 0; + } + if (screen_buffer->cursor_y >= info->height) + screen_buffer->cursor_y = info->height - 1;
notify_screen_buffer_size( screen_buffer ); } @@ -1910,6 +1922,10 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR
empty_update_rect( screen_buffer, &update_rect );
+ if (screen_buffer->delayed_newline && screen_buffer->cursor_x == screen_buffer->width - 1) + screen_buffer->cursor_x = screen_buffer->width; + screen_buffer->delayed_newline = 0; + for (i = 0; i < len; i++) { if (screen_buffer->mode & ENABLE_PROCESSED_OUTPUT) @@ -1917,10 +1933,12 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR switch (buffer[i]) { case '\b': + if (screen_buffer->cursor_x == screen_buffer->width) screen_buffer->cursor_x--; if (screen_buffer->cursor_x) screen_buffer->cursor_x--; continue; case '\t': - j = min( screen_buffer->width - screen_buffer->cursor_x, 8 - (screen_buffer->cursor_x % 8) ); + j = screen_buffer->cursor_x < screen_buffer->width ? screen_buffer->cursor_x : 0; + j = min( screen_buffer->width - j, 8 - (j % 8) ); while (j--) write_char( screen_buffer, ' ', &update_rect, NULL ); continue; case '\n': @@ -1951,7 +1969,10 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT) { if (screen_buffer->mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) + { + screen_buffer->delayed_newline = 1; screen_buffer->cursor_x--; + } else { screen_buffer->cursor_x = 0; diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h index 6446cd1..fddd771 100644 --- a/programs/conhost/conhost.h +++ b/programs/conhost/conhost.h @@ -117,6 +117,7 @@ struct screen_buffer unsigned int height; unsigned int cursor_size; /* size of cursor (percentage filled) */ unsigned int cursor_visible; /* cursor visibility flag */ + unsigned int delayed_newline; /* whether to output a newline on the next character written */ unsigned int cursor_x; /* position of cursor */ unsigned int cursor_y; /* position of cursor */ unsigned short attr; /* default fill attributes (screen colors) */ diff --git a/programs/conhost/tests/tty.c b/programs/conhost/tests/tty.c index 122e8a9..cbcaf76 100644 --- a/programs/conhost/tests/tty.c +++ b/programs/conhost/tests/tty.c @@ -781,6 +781,81 @@ static void test_write_console(void) skip_sequence("\x1b[?25h"); /* show cursor */ expect_empty_output();
+ child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + + child_set_cursor(28, 12); + skip_hide_cursor(); + expect_output_sequence("\x1b[28C"); /* move cursor to the end of the line */ + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + + child_string_request(REQ_WRITE_CONSOLE, L"ab"); + skip_hide_cursor(); + expect_output_sequence("ab"); + expect_output_sequence("\b"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(29, 12); + + child_string_request(REQ_WRITE_CONSOLE, L"c"); + skip_hide_cursor(); + expect_output_sequence("\r\n"); + expect_output_sequence("c"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(1, 13); + + child_set_cursor(28, 14); + skip_hide_cursor(); + expect_output_sequence("\x1b[15;29H"); /* set cursor */ + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + + child_string_request(REQ_WRITE_CONSOLE, L"x"); + skip_hide_cursor(); + expect_output_sequence("x"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(29, 14); + + child_string_request(REQ_WRITE_CONSOLE, L"y"); + skip_hide_cursor(); + expect_output_sequence("y"); + skip_sequence("\b"); /* Windows doesn't send this here for some reason */ + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(29, 14); + + child_string_request(REQ_WRITE_CONSOLE, L"\b"); + skip_hide_cursor(); + expect_output_sequence("\b"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(28, 14); + + child_string_request(REQ_WRITE_CONSOLE, L"z"); + skip_hide_cursor(); + expect_output_sequence("z"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(29, 14); + + child_string_request(REQ_WRITE_CONSOLE, L"w"); + skip_hide_cursor(); + expect_output_sequence("w"); + skip_sequence("\b"); /* Windows doesn't send this here for some reason */ + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(29, 14); + + child_string_request(REQ_WRITE_CONSOLE, L"X"); + skip_hide_cursor(); + expect_output_sequence("\r\n"); + expect_output_sequence("X"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(1, 15); + child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
child_set_cursor(28, 20);