[PATCH v4 3/3] conhost: Fix delayed wrapping around when ENABLE_VIRTUAL_TERMINAL_PROCESSING is set.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Fri Apr 2 12:07:14 CDT 2021
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 at gmail.com>
---
programs/conhost/conhost.c | 30 +++++++++++----
programs/conhost/conhost.h | 5 +++
programs/conhost/tests/tty.c | 73 ++++++++++++++++++++++++++++++++++++
programs/conhost/window.c | 8 ++--
4 files changed, 104 insertions(+), 12 deletions(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c
index 7cd36e2..74ea1f9 100644
--- a/programs/conhost/conhost.c
+++ b/programs/conhost/conhost.c
@@ -278,7 +278,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 */
@@ -307,11 +307,12 @@ static void scroll_to_cursor( struct screen_buffer *screen_buffer )
{
int w = screen_buffer->win.right - screen_buffer->win.left + 1;
int h = screen_buffer->win.bottom - screen_buffer->win.top + 1;
+ unsigned int cursor_x = get_bounded_cursor_x(screen_buffer);
- 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)
@@ -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->cursor_x >= console->active->width)
+ {
+ 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;
@@ -1716,7 +1722,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;
@@ -1903,6 +1909,7 @@ static NTSTATUS set_output_info( struct screen_buffer *screen_buffer,
static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR *buffer, size_t len )
{
+ unsigned int orig_x = screen_buffer->cursor_x, orig_y = screen_buffer->cursor_y;
RECT update_rect;
size_t i, j;
@@ -1917,10 +1924,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':
@@ -1956,13 +1965,18 @@ 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;
}
scroll_to_cursor( screen_buffer );
update_output( screen_buffer, &update_rect );
+
+ /* Windows doesn't put a \b if only one position changed for some reason */
+ if (screen_buffer->cursor_x == screen_buffer->width)
+ if (orig_x == screen_buffer->cursor_x - 1 && orig_y == screen_buffer->cursor_y)
+ screen_buffer->console->tty_cursor_x--;
+
tty_sync( screen_buffer->console );
update_window_config( screen_buffer->console, TRUE );
return STATUS_SUCCESS;
diff --git a/programs/conhost/conhost.h b/programs/conhost/conhost.h
index 6446cd1..c773ce4 100644
--- a/programs/conhost/conhost.h
+++ b/programs/conhost/conhost.h
@@ -140,6 +140,11 @@ NTSTATUS write_console_input( struct console *console, const INPUT_RECORD *recor
void notify_screen_buffer_size( struct screen_buffer *screen_buffer );
NTSTATUS change_screen_buffer_size( struct screen_buffer *screen_buffer, int new_width, int new_height );
+static inline unsigned int get_bounded_cursor_x( struct screen_buffer *screen_buffer )
+{
+ return min(screen_buffer->cursor_x, screen_buffer->width - 1);
+}
+
static inline void empty_update_rect( struct screen_buffer *screen_buffer, RECT *rect )
{
SetRect( rect, screen_buffer->width, screen_buffer->height, 0, 0 );
diff --git a/programs/conhost/tests/tty.c b/programs/conhost/tests/tty.c
index 122e8a9..f0b36b1 100644
--- a/programs/conhost/tests/tty.c
+++ b/programs/conhost/tests/tty.c
@@ -781,6 +781,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");
+ 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("\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..12437bb 100644
--- a/programs/conhost/window.c
+++ b/programs/conhost/window.c
@@ -430,8 +430,8 @@ 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,
- (console->active->cursor_y - console->active->win.top) * console->active->font.height );
+ 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 );
}
--
2.30.0
More information about the wine-devel
mailing list