Newer versions of Windows set it by default.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- programs/conhost/conhost.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 39544fb..54ead48 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -80,7 +80,7 @@ static struct screen_buffer *create_screen_buffer( struct console *console, int if (!(screen_buffer = calloc( 1, sizeof(*screen_buffer) ))) return NULL; screen_buffer->console = console; screen_buffer->id = id; - screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT; + screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING; screen_buffer->cursor_size = 25; screen_buffer->cursor_visible = 1; screen_buffer->width = width;
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- programs/conhost/conhost.c | 11 ++++- programs/conhost/tests/tty.c | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/programs/conhost/conhost.c b/programs/conhost/conhost.c index 54ead48..7cd36e2 100644 --- a/programs/conhost/conhost.c +++ b/programs/conhost/conhost.c @@ -1948,7 +1948,16 @@ static NTSTATUS write_console( struct screen_buffer *screen_buffer, const WCHAR
if (screen_buffer->cursor_x == screen_buffer->width) { - if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT) screen_buffer->cursor_x--; + if (screen_buffer->mode & ENABLE_WRAP_AT_EOL_OUTPUT) + { + if (!(screen_buffer->mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + { + screen_buffer->cursor_x = 0; + 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/tests/tty.c b/programs/conhost/tests/tty.c index 7cc37fa..122e8a9 100644 --- a/programs/conhost/tests/tty.c +++ b/programs/conhost/tests/tty.c @@ -149,6 +149,7 @@ enum req_type REQ_CREATE_SCREEN_BUFFER, REQ_FILL_CHAR, REQ_GET_INPUT, + REQ_GET_SB_INFO, REQ_READ_CONSOLE, REQ_READ_CONSOLE_A, REQ_READ_CONSOLE_FILE, @@ -551,6 +552,26 @@ static void expect_char_key_(unsigned int line, WCHAR ch) expect_key_pressed_(line, ch, ch, vk, ctrl); }
+#define test_cursor_pos(a,b) _test_cursor_pos(__LINE__,a,b) +static void _test_cursor_pos(unsigned line, int expect_x, int expect_y) +{ + struct pseudoconsole_req req = { REQ_GET_SB_INFO }; + CONSOLE_SCREEN_BUFFER_INFO info; + DWORD read; + BOOL ret; + + ret = WriteFile(child_pipe, &req, sizeof(req), &read, NULL); + ok(ret, "WriteFile failed: %u\n", GetLastError()); + + ret = ReadFile(child_pipe, &info, sizeof(info), &read, NULL); + ok(ret, "ReadFile failed: %u\n", GetLastError()); + + ok_(__FILE__,line)(info.dwCursorPosition.X == expect_x, "dwCursorPosition.X = %u, expected %u\n", + info.dwCursorPosition.X, expect_x); + ok_(__FILE__,line)(info.dwCursorPosition.Y == expect_y, "dwCursorPosition.Y = %u, expected %u\n", + info.dwCursorPosition.Y, expect_y); +} + static void test_write_console(void) { child_string_request(REQ_WRITE_CONSOLE, L"abc"); @@ -761,6 +782,55 @@ static void test_write_console(void) expect_empty_output();
child_set_output_mode(ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); + + child_set_cursor(28, 20); + skip_hide_cursor(); + expect_output_sequence("\x1b[21;29H"); /* set cursor */ + 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("\r\n"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(0, 21); + + child_string_request(REQ_WRITE_CONSOLE, L"c"); + skip_hide_cursor(); + expect_output_sequence("c"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(1, 21); + + child_set_cursor(28, 22); + skip_hide_cursor(); + expect_output_sequence("\x1b[23;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, 22); + + child_string_request(REQ_WRITE_CONSOLE, L"y"); + skip_hide_cursor(); + expect_output_sequence("y"); + expect_output_sequence("\r\n"); + skip_sequence("\x1b[?25h"); /* show cursor */ + expect_empty_output(); + test_cursor_pos(0, 23); + + 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(1, 23); }
static void test_tty_output(void) @@ -1300,6 +1370,16 @@ static void child_process(HANDLE pipe) break; }
+ case REQ_GET_SB_INFO: + { + CONSOLE_SCREEN_BUFFER_INFO info; + ret = GetConsoleScreenBufferInfo(output, &info); + ok(ret, "GetConsoleScreenBufferInfo failed: %u\n", GetLastError()); + ret = WriteFile(pipe, &info, sizeof(info), &count, NULL); + ok(ret, "WriteFile failed: %u\n", GetLastError()); + break; + } + case REQ_READ_CONSOLE: ret = ReadConsoleW(input, buf, req->u.size, &count, NULL ); ok(ret, "ReadConsoleW failed: %u\n", GetLastError());
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 ); }
Hi Gabriel,
On 4/3/21 3:12 PM, Gabriel Ivăncescu wrote:
- screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
- screen_buffer->mode = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
While I think we want that, it's probably too soon to do that now. Applications check the mode and their behavior depends on that, so it would be nice to have at least a basic support for VT processing first. See bug 49780 for an example (although for that one, default mode is not enough). I resent the series without the first patch.
Thanks, Jacek