summaryrefslogtreecommitdiffstats
path: root/osdep/terminal-win.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--osdep/terminal-win.c201
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);
}