1
0
mirror of https://github.com/mpv-player/mpv synced 2025-03-25 04:38:01 +00:00

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.
This commit is contained in:
wm4 2014-08-21 22:11:38 +02:00
parent 218ace2b02
commit 47b29094c3
5 changed files with 98 additions and 53 deletions

View File

@ -158,16 +158,10 @@ static void prepare_status_line(struct mp_log_root *root, char *new_status)
size_t clear_lines = MPMIN(MPMAX(new_lines, old_lines), root->blank_lines);
// clear the status line itself
if (terminal_erase_to_end_of_line[0]) {
fprintf(f, "\r%s", terminal_erase_to_end_of_line);
} else {
// This code is for MS windows (no ANSI control sequences)
get_screen_size();
fprintf(f, "\r%*s\r", screen_width - 1, "");
}
fprintf(f, "\r\033[K");
// and clear all previous old lines
for (size_t n = 1; n < clear_lines; n++)
fprintf(f, "%s\r%s", terminal_cursor_up, terminal_erase_to_end_of_line);
fprintf(f, "\033[A\r\033[K");
// skip "unused" blank lines, so that status is aligned to term bottom
for (size_t n = new_lines; n < clear_lines; n++)
fprintf(f, "\n");
@ -200,10 +194,20 @@ bool mp_msg_has_status_line(struct mpv_global *global)
return r;
}
static void set_term_color(FILE *stream, int c)
{
if (c == -1) {
fprintf(stream, "\033[0m");
} else {
fprintf(stream, "\033[%d;3%dm", c >> 3, c & 7);
}
}
static void set_msg_color(FILE* stream, int lev)
{
static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, 8, -1};
terminal_set_foreground_color(stream, v_colors[lev]);
set_term_color(stream, v_colors[lev]);
}
static void pretty_print_module(FILE* stream, const char *prefix, bool use_color, int lev)
@ -214,12 +218,12 @@ static void pretty_print_module(FILE* stream, const char *prefix, bool use_color
unsigned int mod = 0;
for (int i = 0; i < prefix_len; ++i)
mod = mod * 33 + prefix[i];
terminal_set_foreground_color(stream, (mod + 1) % 15 + 1);
set_term_color(stream, (mod + 1) % 15 + 1);
}
fprintf(stream, "%10s", prefix);
if (use_color)
terminal_set_foreground_color(stream, -1);
set_term_color(stream, -1);
fprintf(stream, ": ");
if (use_color)
set_msg_color(stream, lev);
@ -291,7 +295,7 @@ static void print_msg_on_terminal(struct mp_log *log, int lev, char *text)
fprintf(stream, "%s", terminate);
if (root->color)
terminal_set_foreground_color(stream, -1);
set_term_color(stream, -1);
fflush(stream);
}

View File

@ -25,6 +25,7 @@
#include "config.h"
#include "osdep/io.h"
#include "osdep/terminal.h"
// Set the CLOEXEC flag on the given fd.
// On error, false is returned (and errno set).
@ -179,13 +180,10 @@ static int mp_vfprintf(FILE *stream, const char *format, va_list args)
char *buf = talloc_array(NULL, char, len);
if (buf) {
vsnprintf(buf, len, format, args);
wchar_t *out = mp_from_utf8(NULL, buf);
size_t out_len = wcslen(out);
talloc_free(buf);
done = WriteConsoleW(wstream, out, out_len, NULL, NULL);
talloc_free(out);
done = vsnprintf(buf, len, format, args);
mp_write_console_ansi(wstream, buf);
}
talloc_free(buf);
} else {
done = vfprintf(stream, format, args);
}

View File

@ -54,8 +54,6 @@ static volatile int tio_orig_set;
int screen_width = 80;
int screen_height = 24;
char *terminal_erase_to_end_of_line = "\033[K";
char *terminal_cursor_up = "\033[A";
typedef struct {
char *cap;
@ -271,19 +269,11 @@ static int load_termcap(char *termtype){
static char term_buf[128];
char *buf_ptr = &term_buf[0];
char *tmp;
// References for terminfo/termcap codes:
// http://linux.die.net/man/5/termcap
// http://unixhelp.ed.ac.uk/CGI/man-cgi?terminfo+5
tmp = tgetstr("ce", &buf_ptr);
if (tmp)
terminal_erase_to_end_of_line = tmp;
tmp = tgetstr("up", &buf_ptr);
if (tmp)
terminal_cursor_up = tmp;
screen_width = tgetnum("co");
screen_height = tgetnum("li");
if (screen_width < 1 || screen_width > 255)
@ -614,15 +604,6 @@ bool terminal_in_background(void)
return isatty(2) && tcgetpgrp(2) != getpgrp();
}
void terminal_set_foreground_color(FILE *stream, int c)
{
if (c == -1) {
fprintf(stream, "\033[0m");
} else {
fprintf(stream, "\033[%d;3%dm", c >> 3, c & 7);
}
}
int terminal_init(void)
{
if (isatty(1))

View File

@ -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[" [ <i> (';' <i> )* ] <c>
// where <i> are integers, and <c> 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;
}
}

View File

@ -33,9 +33,6 @@ struct input_ctx;
extern int screen_width;
extern int screen_height;
extern char *terminal_erase_to_end_of_line;
extern char *terminal_cursor_up;
/* Global initialization for terminal output. */
int terminal_init(void);
@ -45,10 +42,6 @@ void terminal_setup_getch(struct input_ctx *ictx);
/* Return whether the process has been backgrounded. */
bool terminal_in_background(void);
/* Set ANSI text foreground color. c is [-1, 7], where 0-7 are colors, and
* -1 means reset to default. stream is either stdout or stderr. */
void terminal_set_foreground_color(FILE *stream, int c);
/* Get screen-size using IOCTL call. */
void get_screen_size(void);
@ -59,4 +52,7 @@ void getch2_disable(void);
/* Enable and disable STDIN line-buffering */
void getch2_poll(void);
// Windows only.
void mp_write_console_ansi(void **wstream, char *buf);
#endif /* MPLAYER_GETCH2_H */