From 47b29094c34bdf0aab9f213c8eb2347993e4d505 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 21 Aug 2014 22:11:38 +0200 Subject: win32: emulate some ANSI terminal escape codes We already redirect all terminal output through our own wrappers (for the sake of UTF-8), so we might as well use it to handle ANSI escape codes. This also changes behavior on UNIX: we don't retrieve some escape codes per terminfo anymore, and just hardcode them. Every terminal should understand them. The advantage is that we can pretend to have a real terminal in the normal player code, and Windows atrocities are locked away in glue code. --- osdep/terminal-win.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 75 insertions(+), 9 deletions(-) (limited to 'osdep/terminal-win.c') diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c index 19603d87d1..9b985d7378 100644 --- a/osdep/terminal-win.c +++ b/osdep/terminal-win.c @@ -33,12 +33,11 @@ #include "input/keycodes.h" #include "input/input.h" #include "terminal.h" +#include "osdep/io.h" #include "osdep/w32_keyboard.h" int screen_width = 79; int screen_height = 24; -char *terminal_erase_to_end_of_line = ""; -char *terminal_cursor_up = ""; #define hSTDOUT GetStdHandle(STD_OUTPUT_HANDLE) #define hSTDERR GetStdHandle(STD_ERROR_HANDLE) @@ -58,7 +57,7 @@ void get_screen_size(void) { CONSOLE_SCREEN_BUFFER_INFO cinfo; if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo)) { - screen_width = cinfo.dwMaximumWindowSize.X; + screen_width = cinfo.dwMaximumWindowSize.X - 1; screen_height = cinfo.dwMaximumWindowSize.Y; } } @@ -177,13 +176,80 @@ bool terminal_in_background(void) return false; } -void terminal_set_foreground_color(FILE *stream, int c) +static void write_console_text(HANDLE *wstream, char *buf) { - HANDLE *wstream = stream == stderr ? hSTDERR : hSTDOUT; - if (c < 0 || c >= 8) { // reset or invalid - SetConsoleTextAttribute(wstream, stdoutAttrs); - } else { - SetConsoleTextAttribute(wstream, ansi2win32[c] | FOREGROUND_INTENSITY); + wchar_t *out = mp_from_utf8(NULL, buf); + size_t out_len = wcslen(out); + WriteConsoleW(wstream, out, out_len, NULL, NULL); + talloc_free(out); +} + +// Mutates the input argument (buf), because we're evil. +void mp_write_console_ansi(HANDLE *wstream, char *buf) +{ + while (*buf) { + char *next = strchr(buf, '\033'); + if (!next) { + write_console_text(wstream, buf); + break; + } + next[0] = '\0'; // mutate input for fun and profit + write_console_text(wstream, buf); + if (next[1] != '[') { + write_console_text(wstream, "\033"); + buf = next; + continue; + } + next += 2; + // ANSI codes generally follow this syntax: + // "\033[" [ (';' )* ] + // where are integers, and a single char command code. + // Also see: http://en.wikipedia.org/wiki/ANSI_escape_code#CSI_codes + int params[2] = {-1, -1}; // 'm' might be unlimited; ignore that + int num_params = 0; + while (num_params < 2) { + char *end = next; + long p = strtol(next, &end, 10); + if (end == next) + break; + next = end; + params[num_params++] = p; + if (next[0] != ';' || !next[0]) + break; + next += 1; + } + char code = next[0]; + if (code) + next += 1; + 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); + break; + } + case 'A': { // cursor up + info.dwCursorPosition.Y -= 1; + SetConsoleCursorPosition(wstream, info.dwCursorPosition); + break; + } + case 'm': { // "SGR" + for (int n = 0; n < num_params; n++) { + int p = params[n]; + if (p <= 0) { + SetConsoleTextAttribute(wstream, stdoutAttrs); + } else if (p >= 0 && p < 8) { + SetConsoleTextAttribute(wstream, + ansi2win32[p] | FOREGROUND_INTENSITY); + } + } + break; + } + } + buf = next; } } -- cgit v1.2.3