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 | 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 ); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=88183
Your paranoid android.
=== w10pro64 (32 bit report) ===
conhost.exe: tty: Timeout
Hi Gabriel,
On 4/2/21 7:07 PM, Gabriel Ivăncescu wrote:
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.
I experimented a bit with your patches and think that we can just emit fewer '\b's. What do you think about something like the attached version?
Thanks, Jacek
On 03/04/2021 00:29, Jacek Caban wrote:
Hi Gabriel,
On 4/2/21 7:07 PM, Gabriel Ivăncescu wrote:
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.
I experimented a bit with your patches and think that we can just emit fewer '\b's. What do you think about something like the attached version?
Thanks, Jacek
Thanks, I don't have particularly strong opinions on such an edge case, as long as you're fine with it.
I resent new version with some typo fixes while at it, and silenced a `value computed is not used` warning in the tests.