mirror of
https://github.com/mpv-player/mpv
synced 2025-04-01 14:50:07 +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);
|
size_t clear_lines = MPMIN(MPMAX(new_lines, old_lines), root->blank_lines);
|
||||||
|
|
||||||
// clear the status line itself
|
// clear the status line itself
|
||||||
if (terminal_erase_to_end_of_line[0]) {
|
fprintf(f, "\r\033[K");
|
||||||
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, "");
|
|
||||||
}
|
|
||||||
// and clear all previous old lines
|
// and clear all previous old lines
|
||||||
for (size_t n = 1; n < clear_lines; n++)
|
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
|
// skip "unused" blank lines, so that status is aligned to term bottom
|
||||||
for (size_t n = new_lines; n < clear_lines; n++)
|
for (size_t n = new_lines; n < clear_lines; n++)
|
||||||
fprintf(f, "\n");
|
fprintf(f, "\n");
|
||||||
@ -200,10 +194,20 @@ bool mp_msg_has_status_line(struct mpv_global *global)
|
|||||||
return r;
|
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 void set_msg_color(FILE* stream, int lev)
|
||||||
{
|
{
|
||||||
static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, 8, -1};
|
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)
|
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;
|
unsigned int mod = 0;
|
||||||
for (int i = 0; i < prefix_len; ++i)
|
for (int i = 0; i < prefix_len; ++i)
|
||||||
mod = mod * 33 + prefix[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);
|
fprintf(stream, "%10s", prefix);
|
||||||
if (use_color)
|
if (use_color)
|
||||||
terminal_set_foreground_color(stream, -1);
|
set_term_color(stream, -1);
|
||||||
fprintf(stream, ": ");
|
fprintf(stream, ": ");
|
||||||
if (use_color)
|
if (use_color)
|
||||||
set_msg_color(stream, lev);
|
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);
|
fprintf(stream, "%s", terminate);
|
||||||
|
|
||||||
if (root->color)
|
if (root->color)
|
||||||
terminal_set_foreground_color(stream, -1);
|
set_term_color(stream, -1);
|
||||||
fflush(stream);
|
fflush(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
osdep/io.c
10
osdep/io.c
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "osdep/io.h"
|
#include "osdep/io.h"
|
||||||
|
#include "osdep/terminal.h"
|
||||||
|
|
||||||
// Set the CLOEXEC flag on the given fd.
|
// Set the CLOEXEC flag on the given fd.
|
||||||
// On error, false is returned (and errno set).
|
// 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);
|
char *buf = talloc_array(NULL, char, len);
|
||||||
|
|
||||||
if (buf) {
|
if (buf) {
|
||||||
vsnprintf(buf, len, format, args);
|
done = vsnprintf(buf, len, format, args);
|
||||||
wchar_t *out = mp_from_utf8(NULL, buf);
|
mp_write_console_ansi(wstream, buf);
|
||||||
size_t out_len = wcslen(out);
|
|
||||||
talloc_free(buf);
|
|
||||||
done = WriteConsoleW(wstream, out, out_len, NULL, NULL);
|
|
||||||
talloc_free(out);
|
|
||||||
}
|
}
|
||||||
|
talloc_free(buf);
|
||||||
} else {
|
} else {
|
||||||
done = vfprintf(stream, format, args);
|
done = vfprintf(stream, format, args);
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,6 @@ static volatile int tio_orig_set;
|
|||||||
|
|
||||||
int screen_width = 80;
|
int screen_width = 80;
|
||||||
int screen_height = 24;
|
int screen_height = 24;
|
||||||
char *terminal_erase_to_end_of_line = "\033[K";
|
|
||||||
char *terminal_cursor_up = "\033[A";
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *cap;
|
char *cap;
|
||||||
@ -271,19 +269,11 @@ static int load_termcap(char *termtype){
|
|||||||
|
|
||||||
static char term_buf[128];
|
static char term_buf[128];
|
||||||
char *buf_ptr = &term_buf[0];
|
char *buf_ptr = &term_buf[0];
|
||||||
char *tmp;
|
|
||||||
|
|
||||||
// References for terminfo/termcap codes:
|
// References for terminfo/termcap codes:
|
||||||
// http://linux.die.net/man/5/termcap
|
// http://linux.die.net/man/5/termcap
|
||||||
// http://unixhelp.ed.ac.uk/CGI/man-cgi?terminfo+5
|
// 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_width = tgetnum("co");
|
||||||
screen_height = tgetnum("li");
|
screen_height = tgetnum("li");
|
||||||
if (screen_width < 1 || screen_width > 255)
|
if (screen_width < 1 || screen_width > 255)
|
||||||
@ -614,15 +604,6 @@ bool terminal_in_background(void)
|
|||||||
return isatty(2) && tcgetpgrp(2) != getpgrp();
|
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)
|
int terminal_init(void)
|
||||||
{
|
{
|
||||||
if (isatty(1))
|
if (isatty(1))
|
||||||
|
@ -33,12 +33,11 @@
|
|||||||
#include "input/keycodes.h"
|
#include "input/keycodes.h"
|
||||||
#include "input/input.h"
|
#include "input/input.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "osdep/io.h"
|
||||||
#include "osdep/w32_keyboard.h"
|
#include "osdep/w32_keyboard.h"
|
||||||
|
|
||||||
int screen_width = 79;
|
int screen_width = 79;
|
||||||
int screen_height = 24;
|
int screen_height = 24;
|
||||||
char *terminal_erase_to_end_of_line = "";
|
|
||||||
char *terminal_cursor_up = "";
|
|
||||||
|
|
||||||
#define hSTDOUT GetStdHandle(STD_OUTPUT_HANDLE)
|
#define hSTDOUT GetStdHandle(STD_OUTPUT_HANDLE)
|
||||||
#define hSTDERR GetStdHandle(STD_ERROR_HANDLE)
|
#define hSTDERR GetStdHandle(STD_ERROR_HANDLE)
|
||||||
@ -58,7 +57,7 @@ void get_screen_size(void)
|
|||||||
{
|
{
|
||||||
CONSOLE_SCREEN_BUFFER_INFO cinfo;
|
CONSOLE_SCREEN_BUFFER_INFO cinfo;
|
||||||
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo)) {
|
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo)) {
|
||||||
screen_width = cinfo.dwMaximumWindowSize.X;
|
screen_width = cinfo.dwMaximumWindowSize.X - 1;
|
||||||
screen_height = cinfo.dwMaximumWindowSize.Y;
|
screen_height = cinfo.dwMaximumWindowSize.Y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,13 +176,80 @@ bool terminal_in_background(void)
|
|||||||
return false;
|
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;
|
wchar_t *out = mp_from_utf8(NULL, buf);
|
||||||
if (c < 0 || c >= 8) { // reset or invalid
|
size_t out_len = wcslen(out);
|
||||||
SetConsoleTextAttribute(wstream, stdoutAttrs);
|
WriteConsoleW(wstream, out, out_len, NULL, NULL);
|
||||||
} else {
|
talloc_free(out);
|
||||||
SetConsoleTextAttribute(wstream, ansi2win32[c] | FOREGROUND_INTENSITY);
|
}
|
||||||
|
|
||||||
|
// 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_width;
|
||||||
extern int screen_height;
|
extern int screen_height;
|
||||||
|
|
||||||
extern char *terminal_erase_to_end_of_line;
|
|
||||||
extern char *terminal_cursor_up;
|
|
||||||
|
|
||||||
/* Global initialization for terminal output. */
|
/* Global initialization for terminal output. */
|
||||||
int terminal_init(void);
|
int terminal_init(void);
|
||||||
|
|
||||||
@ -45,10 +42,6 @@ void terminal_setup_getch(struct input_ctx *ictx);
|
|||||||
/* Return whether the process has been backgrounded. */
|
/* Return whether the process has been backgrounded. */
|
||||||
bool terminal_in_background(void);
|
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. */
|
/* Get screen-size using IOCTL call. */
|
||||||
void get_screen_size(void);
|
void get_screen_size(void);
|
||||||
|
|
||||||
@ -59,4 +52,7 @@ void getch2_disable(void);
|
|||||||
/* Enable and disable STDIN line-buffering */
|
/* Enable and disable STDIN line-buffering */
|
||||||
void getch2_poll(void);
|
void getch2_poll(void);
|
||||||
|
|
||||||
|
// Windows only.
|
||||||
|
void mp_write_console_ansi(void **wstream, char *buf);
|
||||||
|
|
||||||
#endif /* MPLAYER_GETCH2_H */
|
#endif /* MPLAYER_GETCH2_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user