Remember the fact we completed the entire line but normalize the cursor otherwise. A newline is delayed so it wraps when writing a new character if the cursor is outside the width.
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 --- programs/conhost/conhost.c | 36 ++++++++++------ programs/conhost/conhost.h | 5 +++ programs/conhost/tests/tty.c | 80 ++++++++++++++++++++++++++++++++++-- programs/conhost/window.c | 6 +-- 4 files changed, 107 insertions(+), 20 deletions(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 7cd36e2..7f0516f 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -195,13 +195,21 @@ static void set_tty_cursor( struct console *console, unsigned int x, unsigned in else if (!x && y == console->tty_cursor_y) strcpy( buf, "\r" ); else if (y == console->tty_cursor_y) { - if (console->is_unix && console->tty_cursor_x >= console->active->width) + if (console->tty_cursor_x >= console->active->width) { - /* Unix will usually have the cursor at width-1 in this case. instead of depending - * on the exact behaviour, move the cursor to the first column and move forward - * from threre. */ - tty_write( console, "\r", 1 ); - console->tty_cursor_x = 0; + if (console->is_unix) + { + /* Unix will usually have the cursor at width-1 in this case. instead of depending + * on the exact behaviour, move the cursor to the first column and move forward + * from there. */ + tty_write( console, "\r", 1 ); + console->tty_cursor_x = 0; + } + else if (console->active->mode & ENABLE_WRAP_AT_EOL_OUTPUT) + { + console->tty_cursor_x--; + } + if (console->tty_cursor_x == x) return; } if (x + 1 == console->tty_cursor_x) strcpy( buf, "\b" ); else if (x > console->tty_cursor_x) sprintf( buf, "\x1b[%uC", x - console->tty_cursor_x ); @@ -278,7 +286,7 @@ static void tty_sync( struct console *console )
if (console->active->cursor_visible) { - set_tty_cursor( console, console->active->cursor_x, console->active->cursor_y ); + set_tty_cursor( console, get_bounded_cursor_x( console->active ), console->active->cursor_y ); if (!console->tty_cursor_visible) { tty_write( console, "\x1b[?25h", 6 ); /* show cursor */ @@ -305,13 +313,14 @@ static void init_tty_output( struct console *console )
static void scroll_to_cursor( struct screen_buffer *screen_buffer ) { + unsigned int cursor_x = get_bounded_cursor_x( screen_buffer ); int w = screen_buffer->win.right - screen_buffer->win.left + 1; int h = screen_buffer->win.bottom - screen_buffer->win.top + 1;
- if (screen_buffer->cursor_x < screen_buffer->win.left) - screen_buffer->win.left = min( screen_buffer->cursor_x, screen_buffer->width - w ); - else if (screen_buffer->cursor_x > screen_buffer->win.right) - screen_buffer->win.left = max( screen_buffer->cursor_x, w ) - w + 1; + if (cursor_x < screen_buffer->win.left) + screen_buffer->win.left = min( cursor_x, screen_buffer->width - w ); + else if (cursor_x > screen_buffer->win.right) + screen_buffer->win.left = max( cursor_x, w ) - w + 1; screen_buffer->win.right = screen_buffer->win.left + w - 1;
if (screen_buffer->cursor_y < screen_buffer->win.top) @@ -1716,7 +1725,7 @@ static NTSTATUS get_output_info( struct screen_buffer *screen_buffer, size_t *ou
info->cursor_size = screen_buffer->cursor_size; info->cursor_visible = screen_buffer->cursor_visible; - info->cursor_x = screen_buffer->cursor_x; + info->cursor_x = get_bounded_cursor_x( screen_buffer ); info->cursor_y = screen_buffer->cursor_y; info->width = screen_buffer->width; info->height = screen_buffer->height; @@ -1917,10 +1926,12 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR switch (buffer[i]) { case '\b': + screen_buffer->cursor_x = get_bounded_cursor_x( screen_buffer ); 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) ); + if (!j) j = 8; while (j--) write_char( screen_buffer, ' ', &update_rect, NULL ); continue; case '\n': @@ -1956,7 +1967,6 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR if (++screen_buffer->cursor_y == screen_buffer->height) new_line( screen_buffer, &update_rect ); } - else screen_buffer->cursor_x--; } else screen_buffer->cursor_x = update_rect.left; } diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h index 6446cd1..1813676 100644 --- a/programs/conhost/conhost.h +++ b/programs/conhost/conhost.h @@ -145,6 +145,11 @@ static inline void empty_update_rect( struct screen_buffer *screen_buffer, RECT SetRect( rect, screen_buffer->width, screen_buffer->height, 0, 0 ); }
+static inline unsigned int get_bounded_cursor_x( struct screen_buffer *screen_buffer ) +{ + return min( screen_buffer->cursor_x, screen_buffer->width - 1 ); +} + #endif /* RC_INVOKED */
/* strings */ diff --git a/programs/conhost/tests/tty.c b/programs/conhost/tests/tty.c index 122e8a9..122715e 100644 --- a/programs/conhost/tests/tty.c +++ b/programs/conhost/tests/tty.c @@ -652,7 +652,7 @@ static void test_write_console(void) child_string_request(REQ_WRITE_CONSOLE, L"xy"); skip_hide_cursor(); expect_output_sequence("xy"); - if (!skip_sequence("\b")) expect_output_sequence("\r\n"); + if (!skip_sequence("\b")) skip_sequence("\r\n"); skip_sequence("\x1b[?25h"); /* show cursor */ expect_empty_output();
@@ -710,10 +710,9 @@ static void test_write_console(void) expect_output_sequence("XY"); skip_sequence("\x1b[40;29H"); /* set cursor */ if (skip_sequence("\x1b[?25h")) /* show cursor */ - expect_output_sequence("\x1b[?25l"); /* hide cursor */ - if (!skip_sequence("\b")) + skip_sequence("\x1b[?25l"); /* hide cursor */ + if (!skip_sequence("\b") && skip_sequence("\r\n")) { - expect_output_sequence("\r\n"); expect_output_sequence("\x1b[30X"); /* erase the line */ expect_output_sequence("\x1b[30C"); /* move cursor to the end of the line */ expect_output_sequence("\r"); /* set cursor */ @@ -781,6 +780,79 @@ 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"); + skip_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("\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("\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); diff --git a/programs/conhost/window.c b/programs/conhost/window.c index 0ea929d..92970b4 100644 --- a/programs/conhost/window.c +++ b/programs/conhost/window.c @@ -430,7 +430,7 @@ static void update_window_cursor( struct console *console ) { if (console->win != GetFocus() || !console->active->cursor_visible) return;
- SetCaretPos( (console->active->cursor_x - console->active->win.left) * console->active->font.width, + SetCaretPos( (get_bounded_cursor_x( console->active ) - console->active->win.left) * console->active->font.width, (console->active->cursor_y - console->active->win.top) * console->active->font.height ); ShowCaret( console->win ); } @@ -607,10 +607,10 @@ static void update_window( struct console *console ) } }
- if (update_all || console->active->cursor_x != console->window->cursor_pos.X || + if (update_all || get_bounded_cursor_x( console->active ) != console->window->cursor_pos.X || console->active->cursor_y != console->window->cursor_pos.Y) { - console->window->cursor_pos.X = console->active->cursor_x; + console->window->cursor_pos.X = get_bounded_cursor_x( console->active ); console->window->cursor_pos.Y = console->active->cursor_y; update_window_cursor( console ); }