[PATCH v5 3/3] conhost: Fix delayed wrapping around when ENABLE_VIRTUAL_TERMINAL_PROCESSING is set.

Gabriel Ivăncescu gabrielopcode at gmail.com
Sat Apr 3 08:12:31 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   | 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 );
     }
-- 
2.30.0




More information about the wine-devel mailing list