From fb7138614daa42f737799833d45eb4ea3268c8a7 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 20 Oct 2011 19:23:57 -0700 Subject: [PATCH] Improve ansi escape code support on windows --- include/uv-private/uv-win.h | 4 +- src/win/internal.h | 42 +++++----- src/win/tty.c | 160 ++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 21 deletions(-) diff --git a/include/uv-private/uv-win.h b/include/uv-private/uv-win.h index b7fb0a4b..ed132d3a 100644 --- a/include/uv-private/uv-win.h +++ b/include/uv-private/uv-win.h @@ -313,7 +313,9 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* ansi parser state */ \ unsigned char ansi_parser_state; \ unsigned char ansi_csi_argc; \ - unsigned short ansi_csi_argv[4]; + unsigned short ansi_csi_argv[4]; \ + COORD saved_position; \ + WORD saved_attributes; #define UV_TIMER_PRIVATE_FIELDS \ RB_ENTRY(uv_timer_s) tree_entry; \ diff --git a/src/win/internal.h b/src/win/internal.h index ea0867c1..55e02df5 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -44,28 +44,30 @@ void uv_process_timers(uv_loop_t* loop); */ /* Private uv_handle flags */ -#define UV_HANDLE_CLOSING 0x0001 -#define UV_HANDLE_CLOSED 0x0002 -#define UV_HANDLE_BOUND 0x0004 -#define UV_HANDLE_LISTENING 0x0008 -#define UV_HANDLE_CONNECTION 0x0010 -#define UV_HANDLE_CONNECTED 0x0020 -#define UV_HANDLE_READING 0x0040 -#define UV_HANDLE_ACTIVE 0x0040 -#define UV_HANDLE_EOF 0x0080 -#define UV_HANDLE_SHUTTING 0x0100 -#define UV_HANDLE_SHUT 0x0200 -#define UV_HANDLE_ENDGAME_QUEUED 0x0400 -#define UV_HANDLE_BIND_ERROR 0x1000 -#define UV_HANDLE_IPV6 0x2000 -#define UV_HANDLE_PIPESERVER 0x4000 -#define UV_HANDLE_READ_PENDING 0x8000 -#define UV_HANDLE_UV_ALLOCED 0x10000 -#define UV_HANDLE_SYNC_BYPASS_IOCP 0x20000 -#define UV_HANDLE_ZERO_READ 0x40000 -#define UV_HANDLE_TTY_RAW 0x80000 +#define UV_HANDLE_CLOSING 0x000001 +#define UV_HANDLE_CLOSED 0x000002 +#define UV_HANDLE_BOUND 0x000004 +#define UV_HANDLE_LISTENING 0x000008 +#define UV_HANDLE_CONNECTION 0x000010 +#define UV_HANDLE_CONNECTED 0x000020 +#define UV_HANDLE_READING 0x000040 +#define UV_HANDLE_ACTIVE 0x000040 +#define UV_HANDLE_EOF 0x000080 +#define UV_HANDLE_SHUTTING 0x000100 +#define UV_HANDLE_SHUT 0x000200 +#define UV_HANDLE_ENDGAME_QUEUED 0x000400 +#define UV_HANDLE_BIND_ERROR 0x001000 +#define UV_HANDLE_IPV6 0x002000 +#define UV_HANDLE_PIPESERVER 0x004000 +#define UV_HANDLE_READ_PENDING 0x008000 +#define UV_HANDLE_UV_ALLOCED 0x010000 +#define UV_HANDLE_SYNC_BYPASS_IOCP 0x020000 +#define UV_HANDLE_ZERO_READ 0x040000 +#define UV_HANDLE_TTY_RAW 0x080000 #define UV_HANDLE_EMULATE_IOCP 0x100000 #define UV_HANDLE_NON_OVERLAPPED_PIPE 0x200000 +#define UV_HANDLE_TTY_SAVED_POSITION 0x400000 +#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x800000 void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle); void uv_process_endgames(uv_loop_t* loop); diff --git a/src/win/tty.c b/src/win/tty.c index c02c102f..46aae272 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -898,6 +898,64 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, } +static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { + const COORD origin = {0, 0}; + const WORD char_attrs = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_RED; + CONSOLE_SCREEN_BUFFER_INFO info; + DWORD count, written; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + /* Reset original text attributes. */ + if (!SetConsoleTextAttribute(handle->handle, char_attrs)) { + *error = GetLastError(); + return -1; + } + + /* Move the cursor position to (0, 0). */ + if (!SetConsoleCursorPosition(handle->handle, origin)) { + *error = GetLastError(); + return -1; + } + + /* Clear the screen buffer. */ + retry: + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + count = info.dwSize.X * info.dwSize.Y; + + if (!(FillConsoleOutputCharacterW(handle->handle, + L'\x20', + count, + origin, + &written) && + FillConsoleOutputAttribute(handle->handle, + char_attrs, + written, + origin, + &written))) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + /* The console may be resized - retry */ + goto retry; + } else { + *error = GetLastError(); + return -1; + } + } + + /* Move the virtual window up to the top. */ + uv_tty_virtual_offset = 0; + uv_tty_update_virtual_window(&info); + + return 0; +} + + static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, DWORD* error) { unsigned short argc = handle->ansi_csi_argc; @@ -1084,6 +1142,76 @@ static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) { } +static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes, + DWORD* error) { + CONSOLE_SCREEN_BUFFER_INFO info; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + uv_tty_update_virtual_window(&info); + + handle->saved_position.X = info.dwCursorPosition.X; + handle->saved_position.Y = info.dwCursorPosition.Y - uv_tty_virtual_offset; + handle->flags |= UV_HANDLE_TTY_SAVED_POSITION; + + if (save_attributes) { + handle->saved_attributes = info.wAttributes & + (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); + handle->flags |= UV_HANDLE_TTY_SAVED_ATTRIBUTES; + } + + return 0; +} + + +static int uv_tty_restore_state(uv_tty_t* handle, + unsigned char restore_attributes, DWORD* error) { + CONSOLE_SCREEN_BUFFER_INFO info; + WORD new_attributes; + + if (*error != ERROR_SUCCESS) { + return -1; + } + + if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) { + if (uv_tty_move_caret(handle, + handle->saved_position.X, + 0, + handle->saved_position.Y, + 0, + error) != 0) { + return -1; + } + } + + if (restore_attributes && + (handle->flags & UV_HANDLE_TTY_SAVED_ATTRIBUTES)) { + if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { + *error = GetLastError(); + return -1; + } + + new_attributes = info.wAttributes; + new_attributes &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY); + new_attributes |= handle->saved_attributes; + + if (!SetConsoleTextAttribute(handle->handle, new_attributes)) { + *error = GetLastError(); + return -1; + } + } + + return 0; +} + + static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, DWORD* error) { /* We can only write 8k characters at a time. Windows can't handle */ @@ -1204,6 +1332,26 @@ static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, /* Ignore double escape. */ continue; + case 'c': + /* Full console reset. */ + uv_tty_reset(handle, error); + ansi_parser_state = ANSI_NORMAL; + continue; + + case '7': + /* Save the cursor position and text attributes. */ + FLUSH_TEXT(); + uv_tty_save_state(handle, 1, error); + ansi_parser_state = ANSI_NORMAL; + continue; + + case '8': + /* Restore the cursor position and text attributes */ + FLUSH_TEXT(); + uv_tty_restore_state(handle, 1, error); + ansi_parser_state = ANSI_NORMAL; + continue; + default: if (utf8_codepoint >= '@' && utf8_codepoint <= '_') { /* Single-char control. */ @@ -1360,6 +1508,18 @@ static int uv_tty_write_bufs(uv_tty_t* handle, uv_buf_t bufs[], int bufcnt, FLUSH_TEXT(); uv_tty_set_style(handle, error); break; + + case 's': + /* Save the cursor position. */ + FLUSH_TEXT(); + uv_tty_save_state(handle, 0, error); + break; + + case 'u': + /* Restore the cursor position */ + FLUSH_TEXT(); + uv_tty_restore_state(handle, 0, error); + break; } /* Sequence ended - go back to normal state. */