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:
parent
218ace2b02
commit
47b29094c3
28
common/msg.c
28
common/msg.c
@ -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);
|
||||
}
|
||||
|
||||
|
10
osdep/io.c
10
osdep/io.c
@ -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);
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user