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

Gabriel Ivăncescu gabrielopcode at gmail.com
Thu Apr 1 11:43:55 CDT 2021


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 at 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);
-- 
2.30.0




More information about the wine-devel mailing list