diff options
Diffstat (limited to '')
-rw-r--r-- | osdep/terminal-win.c | 201 |
1 files changed, 179 insertions, 22 deletions
diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c index 8f3410c..dc75180 100644 --- a/osdep/terminal-win.c +++ b/osdep/terminal-win.c @@ -53,20 +53,17 @@ static void attempt_native_out_vt(HANDLE hOut, DWORD basemode) SetConsoleMode(hOut, basemode); } -static bool is_native_out_vt(HANDLE hOut) -{ - DWORD cmode; - return GetConsoleMode(hOut, &cmode) && - (cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) && - !(cmode & DISABLE_NEWLINE_AUTO_RETURN); -} +#define hSTDIN GetStdHandle(STD_INPUT_HANDLE) #define hSTDOUT GetStdHandle(STD_OUTPUT_HANDLE) #define hSTDERR GetStdHandle(STD_ERROR_HANDLE) #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) +static bool is_console[STDERR_FILENO + 1]; +static bool is_vt[STDERR_FILENO + 1]; +static bool utf8_output; static short stdoutAttrs = 0; // copied from the screen buffer on init static const unsigned char ansi2win32[8] = { 0, @@ -94,6 +91,23 @@ static HANDLE death; static mp_thread input_thread; static struct input_ctx *input_ctx; +static bool is_native_out_vt_internal(HANDLE hOut) +{ + DWORD cmode; + return GetConsoleMode(hOut, &cmode) && + (cmode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) && + !(cmode & DISABLE_NEWLINE_AUTO_RETURN); +} + +static bool is_native_out_vt(HANDLE hOut) +{ + if (hOut == hSTDOUT) + return is_vt[STDOUT_FILENO]; + if (hOut == hSTDERR) + return is_vt[STDERR_FILENO]; + return is_native_out_vt_internal(hOut); +} + void terminal_get_size(int *w, int *h) { CONSOLE_SCREEN_BUFFER_INFO cinfo; @@ -191,6 +205,12 @@ void terminal_setup_getch(struct input_ctx *ictx) } } +DWORD tmp_buffers_key = FLS_OUT_OF_INDEXES; +struct tmp_buffers { + bstr write_console_buf; + wchar_t *write_console_wbuf; +}; + void terminal_uninit(void) { if (running) { @@ -199,6 +219,7 @@ void terminal_uninit(void) input_ctx = NULL; running = false; } + FlsFree(tmp_buffers_key); } bool terminal_in_background(void) @@ -206,16 +227,53 @@ bool terminal_in_background(void) return false; } -void mp_write_console_ansi(HANDLE wstream, char *buf) +int mp_console_vfprintf(HANDLE wstream, const char *format, va_list args) { - wchar_t *wbuf = mp_from_utf8(NULL, buf); - wchar_t *pos = wbuf; + struct tmp_buffers *buffers = FlsGetValue(tmp_buffers_key); + bool free_buf = false; + if (!buffers) { + buffers = talloc_zero(NULL, struct tmp_buffers); + free_buf = !FlsSetValue(tmp_buffers_key, buffers); + } - while (*pos) { - if (is_native_out_vt(wstream)) { - WriteConsoleW(wstream, pos, wcslen(pos), NULL, NULL); - break; + buffers->write_console_buf.len = 0; + bstr_xappend_vasprintf(buffers, &buffers->write_console_buf, format, args); + + int ret = mp_console_write(wstream, buffers->write_console_buf); + + if (free_buf) + talloc_free(buffers); + + return ret; +} + +int mp_console_write(HANDLE wstream, bstr str) +{ + struct tmp_buffers *buffers = FlsGetValue(tmp_buffers_key); + bool free_buf = false; + if (!buffers) { + buffers = talloc_zero(NULL, struct tmp_buffers); + free_buf = !FlsSetValue(tmp_buffers_key, buffers); + } + + bool vt = is_native_out_vt(wstream); + int wlen = 0; + wchar_t *pos = NULL; + if (!utf8_output || !vt) { + wlen = bstr_to_wchar(buffers, str, &buffers->write_console_wbuf); + pos = buffers->write_console_wbuf; + } + + if (vt) { + if (utf8_output) { + WriteConsoleA(wstream, str.start, str.len, NULL, NULL); + } else { + WriteConsoleW(wstream, pos, wlen, NULL, NULL); } + goto done; + } + + while (*pos) { wchar_t *next = wcschr(pos, '\033'); if (!next) { WriteConsoleW(wstream, pos, wcslen(pos), NULL, NULL); @@ -227,6 +285,10 @@ void mp_write_console_ansi(HANDLE wstream, char *buf) // CSI - Control Sequence Introducer next += 2; + // private sequences + bool priv = next[0] == '?'; + next += priv; + // CSI codes generally follow this syntax: // "\033[" [ <i> (';' <i> )* ] <c> // where <i> are integers, and <c> a single char command code. @@ -250,18 +312,84 @@ void mp_write_console_ansi(HANDLE wstream, char *buf) CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(wstream, &info); switch (code) { - case 'K': { // erase to end of line - COORD at = info.dwCursorPosition; - int len = info.dwSize.X - at.X; - FillConsoleOutputCharacterW(wstream, ' ', len, at, &(DWORD){0}); - SetConsoleCursorPosition(wstream, at); + case 'K': { // erase line + COORD cursor_pos = info.dwCursorPosition; + COORD at = cursor_pos; + int len; + switch (num_params ? params[0] : 0) { + case 1: + len = at.X; + at.X = 0; + break; + case 2: + len = info.dwSize.X; + at.X = 0; + break; + case 0: + default: + len = info.dwSize.X - at.X; + } + FillConsoleOutputCharacterW(wstream, L' ', len, at, &(DWORD){0}); + SetConsoleCursorPosition(wstream, cursor_pos); + break; + } + case 'B': { // cursor down + info.dwCursorPosition.Y += !num_params ? 1 : params[0]; + SetConsoleCursorPosition(wstream, info.dwCursorPosition); break; } case 'A': { // cursor up - info.dwCursorPosition.Y -= 1; + info.dwCursorPosition.Y -= !num_params ? 1 : params[0]; SetConsoleCursorPosition(wstream, info.dwCursorPosition); break; } + case 'J': { + // Only full screen clear is supported + if (!num_params || params[0] != 2) + break; + + COORD top_left = {0, 0}; + FillConsoleOutputCharacterW(wstream, L' ', info.dwSize.X * info.dwSize.Y, + top_left, &(DWORD){0}); + SetConsoleCursorPosition(wstream, top_left); + break; + } + case 'f': { + if (num_params != 2) + break; + SetConsoleCursorPosition(wstream, (COORD){params[0], params[1]}); + break; + } + case 'l': { + if (!priv || !num_params) + break; + + switch (params[0]) { + case 25:; // hide the cursor + CONSOLE_CURSOR_INFO cursor_info; + if (!GetConsoleCursorInfo(wstream, &cursor_info)) + break; + cursor_info.bVisible = FALSE; + SetConsoleCursorInfo(wstream, &cursor_info); + break; + } + break; + } + case 'h': { + if (!priv || !num_params) + break; + + switch (params[0]) { + case 25:; // show the cursor + CONSOLE_CURSOR_INFO cursor_info; + if (!GetConsoleCursorInfo(wstream, &cursor_info)) + break; + cursor_info.bVisible = TRUE; + SetConsoleCursorInfo(wstream, &cursor_info); + break; + } + break; + } case 'm': { // "SGR" short attr = info.wAttributes; if (num_params == 0) // reset @@ -352,7 +480,13 @@ void mp_write_console_ansi(HANDLE wstream, char *buf) pos = next; } - talloc_free(wbuf); +done:; + int ret = buffers->write_console_buf.len; + + if (free_buf) + talloc_free(buffers); + + return ret; } static bool is_a_console(HANDLE h) @@ -360,6 +494,17 @@ static bool is_a_console(HANDLE h) return GetConsoleMode(h, &(DWORD){0}); } +bool mp_check_console(void *handle) +{ + if (handle == hSTDIN) + return is_console[STDIN_FILENO]; + if (handle == hSTDOUT) + return is_console[STDOUT_FILENO]; + if (handle == hSTDERR) + return is_console[STDERR_FILENO]; + return is_a_console(handle); +} + static void reopen_console_handle(DWORD std, int fd, FILE *stream) { HANDLE handle = GetStdHandle(std); @@ -369,7 +514,6 @@ static void reopen_console_handle(DWORD std, int fd, FILE *stream) } else { freopen("CONOUT$", "wt", stream); } - setvbuf(stream, NULL, _IONBF, 0); // Set the low-level FD to the new handle value, since mp_subprocess2 // callers might rely on low-level FDs being set. Note, with this @@ -420,6 +564,19 @@ void terminal_init(void) cmode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); attempt_native_out_vt(hSTDOUT, cmode); attempt_native_out_vt(hSTDERR, cmode); + + // Init for mp_check_console(), this never changes during runtime + is_console[STDIN_FILENO] = is_a_console(hSTDIN); + is_console[STDOUT_FILENO] = is_a_console(hSTDOUT); + is_console[STDERR_FILENO] = is_a_console(hSTDERR); + + // Init for is_native_out_vt(), this is never disabled/changed during runtime + is_vt[STDOUT_FILENO] = is_native_out_vt_internal(hSTDOUT); + is_vt[STDERR_FILENO] = is_native_out_vt_internal(hSTDERR); + GetConsoleScreenBufferInfo(hSTDOUT, &cinfo); stdoutAttrs = cinfo.wAttributes; + + tmp_buffers_key = FlsAlloc((PFLS_CALLBACK_FUNCTION)talloc_free); + utf8_output = SetConsoleOutputCP(CP_UTF8); } |