diff options
Diffstat (limited to 'common/msg.c')
-rw-r--r-- | common/msg.c | 402 |
1 files changed, 163 insertions, 239 deletions
diff --git a/common/msg.c b/common/msg.c index e80f6fcf16..ff3071a92c 100644 --- a/common/msg.c +++ b/common/msg.c @@ -22,195 +22,133 @@ #include <string.h> #include <unistd.h> #include <assert.h> +#include <pthread.h> #include "talloc.h" -#include "config.h" +#include "bstr/bstr.h" +#include "compat/atomics.h" +#include "common/common.h" #include "common/global.h" +#include "options/options.h" #include "osdep/terminal.h" #include "osdep/io.h" -#ifndef __MINGW32__ -#include <signal.h> -#endif - #include "common/msg.h" -bool mp_msg_stdout_in_use = 0; +/* maximum message length of mp_msg */ +#define MSGSIZE_MAX 6144 struct mp_log_root { - /* This should, at some point, contain all mp_msg related state, instead - * of having global variables (at least as long as we don't want to - * control the terminal, which is global anyway). But for now, there is - * not much. */ struct mpv_global *global; + // --- protected by mp_msg_lock + char *msglevels; + bool smode; // slave mode compatibility glue + bool module; + // --- semi-atomic access + bool color; + int verbose; + bool force_stderr; + bool mute; + // --- must be accessed atomically + /* This is incremented every time the msglevels must be reloaded. + * (This is perhaps better than maintaining a globally accessible and + * synchronized mp_log tree.) */ + int64_t reload_counter; + int header; // indicate if last line printed ended with \n or \r + int statusline; // indicates if last line printed was a status line }; struct mp_log { struct mp_log_root *root; const char *prefix; const char *verbose_prefix; - int legacy_mod; + int level; + int64_t reload_counter; }; -// should not exist -static bool initialized; -static struct mp_log *legacy_logs[MSGT_MAX]; - -/* maximum message length of mp_msg */ -#define MSGSIZE_MAX 6144 - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <io.h> -#define hSTDOUT GetStdHandle(STD_OUTPUT_HANDLE) -#define hSTDERR GetStdHandle(STD_ERROR_HANDLE) -static short stdoutAttrs = 0; -static const unsigned char ansi2win32[10] = { - 0, - FOREGROUND_RED, - FOREGROUND_GREEN, - FOREGROUND_GREEN | FOREGROUND_RED, - FOREGROUND_BLUE, - FOREGROUND_BLUE | FOREGROUND_RED, - FOREGROUND_BLUE | FOREGROUND_GREEN, - FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED, - FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED, - FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED -}; -#endif +// Protects some (not all) state in mp_log_root +static pthread_mutex_t mp_msg_lock = PTHREAD_MUTEX_INITIALIZER; -int mp_msg_levels[MSGT_MAX]; // verbose level of this module. initialized to -2 -int mp_msg_level_all = MSGL_STATUS; -int verbose = 0; -int mp_msg_color = 1; -int mp_msg_module = 0; -int mp_msg_cancolor = 0; +static const struct mp_log null_log = {0}; +struct mp_log *const mp_null_log = (struct mp_log *)&null_log; -static int mp_msg_docolor(void) { - return mp_msg_cancolor && mp_msg_color; +static bool match_mod(const char *name, bstr mod) +{ + if (bstr_equals0(mod, "all")) + return true; + // Path prefix matches + bstr b = bstr0(name); + return bstr_eatstart(&b, mod) && (bstr_eatstart0(&b, "/") || !b.len); } -static void mp_msg_do_init(void){ -#ifdef _WIN32 - CONSOLE_SCREEN_BUFFER_INFO cinfo; - DWORD cmode = 0; - GetConsoleMode(hSTDOUT, &cmode); - cmode |= (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); - SetConsoleMode(hSTDOUT, cmode); - SetConsoleMode(hSTDERR, cmode); - GetConsoleScreenBufferInfo(hSTDOUT, &cinfo); - stdoutAttrs = cinfo.wAttributes; -#endif - int i; - char *env = getenv("MPV_VERBOSE"); - if (env) - verbose = atoi(env); - for(i=0;i<MSGT_MAX;i++) mp_msg_levels[i] = -2; - mp_msg_cancolor = isatty(fileno(stdout)); - mp_msg_levels[MSGT_IDENTIFY] = -1; // no -identify output by default +static void update_loglevel(struct mp_log *log) +{ + pthread_mutex_lock(&mp_msg_lock); + log->level = MSGL_STATUS + log->root->verbose; // default log level + // Stupid exception for the remains of -identify + if (match_mod(log->verbose_prefix, bstr0("identify"))) + log->level = -1; + bstr s = bstr0(log->root->msglevels); + bstr mod; + int level; + while (mp_msg_split_msglevel(&s, &mod, &level) > 0) { + if (match_mod(log->verbose_prefix, mod)) + log->level = level; + } + log->reload_counter = log->root->reload_counter; + pthread_mutex_unlock(&mp_msg_lock); } -int mp_msg_test(int mod, int lev) +// Return whether the message at this verbosity level would be actually printed. +bool mp_msg_test(struct mp_log *log, int lev) { -#ifndef __MINGW32__ + mp_memory_barrier(); + if (!log->root || log->root->mute) + return false; if (lev == MSGL_STATUS) { // skip status line output if stderr is a tty but in background - if (isatty(2) && tcgetpgrp(2) != getpgrp()) + if (terminal_in_background()) return false; } -#endif - return lev <= (mp_msg_levels[mod] == -2 ? mp_msg_level_all + verbose : mp_msg_levels[mod]); -} - -bool mp_msg_test_log(struct mp_log *log, int lev) -{ - return mp_msg_test(log->legacy_mod, lev); + if (log->reload_counter != log->root->reload_counter) + update_loglevel(log); + return lev <= log->level || (log->root->smode && lev == MSGL_SMODE); } static void set_msg_color(FILE* stream, int lev) { - static const int v_colors[10] = {9, 1, 3, 3, -1, -1, 2, 8, 8, 8}; - int c = v_colors[lev]; -#ifdef MP_ANNOY_ME - /* that's only a silly color test */ - { - int c; - static int flag = 1; - if (flag) - for(c = 0; c < 24; c++) - printf("\033[%d;3%dm*** COLOR TEST %d ***\n", c>7, c&7, c); - flag = 0; - } -#endif - if (mp_msg_docolor()) - { -#if defined(_WIN32) && !defined(__CYGWIN__) - HANDLE *wstream = stream == stderr ? hSTDERR : hSTDOUT; - if (c == -1) - c = 7; - SetConsoleTextAttribute(wstream, ansi2win32[c] | FOREGROUND_INTENSITY); -#else - if (c == -1) { - fprintf(stream, "\033[0m"); - } else { - fprintf(stream, "\033[%d;3%dm", c >> 3, c & 7); - } -#endif - } + static const int v_colors[] = {9, 1, 3, -1, -1, 2, 8, 8, -1}; + terminal_set_foreground_color(stream, v_colors[lev]); } -static void print_msg_module(FILE* stream, struct mp_log *log) +void mp_msg_va(struct mp_log *log, int lev, const char *format, va_list va) { - int mod = log->legacy_mod; - int c2 = (mod + 1) % 15 + 1; - -#ifdef _WIN32 - HANDLE *wstream = stream == stderr ? hSTDERR : hSTDOUT; - if (mp_msg_docolor()) - SetConsoleTextAttribute(wstream, ansi2win32[c2&7] | FOREGROUND_INTENSITY); - fprintf(stream, "%9s", log->verbose_prefix); - if (mp_msg_docolor()) - SetConsoleTextAttribute(wstream, stdoutAttrs); -#else - if (mp_msg_docolor()) - fprintf(stream, "\033[%d;3%dm", c2 >> 3, c2 & 7); - fprintf(stream, "%9s", log->verbose_prefix); - if (mp_msg_docolor()) - fprintf(stream, "\033[0;37m"); -#endif - fprintf(stream, ": "); -} + if (!mp_msg_test(log, lev)) + return; // do not display -static void mp_msg_log_va(struct mp_log *log, int lev, const char *format, - va_list va) -{ - char tmp[MSGSIZE_MAX]; - FILE *stream = - (mp_msg_stdout_in_use || (lev == MSGL_STATUS)) ? stderr : stdout; - static int header = 1; - // indicates if last line printed was a status line - static int statusline; + pthread_mutex_lock(&mp_msg_lock); + + struct mp_log_root *root = log->root; + FILE *stream = (root->force_stderr || lev == MSGL_STATUS) ? stderr : stdout; - if (!mp_msg_test_log(log, lev)) return; // do not display - vsnprintf(tmp, MSGSIZE_MAX, format, va); - tmp[MSGSIZE_MAX-2] = '\n'; - tmp[MSGSIZE_MAX-1] = 0; + char tmp[MSGSIZE_MAX]; + if (vsnprintf(tmp, MSGSIZE_MAX, format, va) < 0) + snprintf(tmp, MSGSIZE_MAX, "[fprintf error]\n"); + tmp[MSGSIZE_MAX - 2] = '\n'; + tmp[MSGSIZE_MAX - 1] = 0; /* A status line is normally intended to be overwritten by the next * status line, and does not end with a '\n'. If we're printing a normal * line instead after the status one print '\n' to change line. */ - if (statusline && lev != MSGL_STATUS) + if (root->statusline && lev != MSGL_STATUS) fprintf(stderr, "\n"); - statusline = lev == MSGL_STATUS; - - set_msg_color(stream, lev); - if (header) { - if (mp_msg_module) { - print_msg_module(stream, log); - set_msg_color(stream, lev); - } else if (lev >= MSGL_V || verbose) { + root->statusline = lev == MSGL_STATUS; + + if (root->color) + set_msg_color(stream, lev); + if (root->header) { + if ((lev >= MSGL_V && lev != MSGL_SMODE) || root->verbose || root->module) { fprintf(stream, "[%s] ", log->verbose_prefix); } else if (log->prefix) { fprintf(stream, "[%s] ", log->prefix); @@ -218,88 +156,17 @@ static void mp_msg_log_va(struct mp_log *log, int lev, const char *format, } size_t len = strlen(tmp); - header = len && (tmp[len-1] == '\n' || tmp[len-1] == '\r'); + root->header = len && (tmp[len - 1] == '\n' || tmp[len - 1] == '\r'); fprintf(stream, "%s", tmp); - if (mp_msg_docolor()) - { -#ifdef _WIN32 - HANDLE *wstream = lev <= MSGL_WARN ? hSTDERR : hSTDOUT; - SetConsoleTextAttribute(wstream, stdoutAttrs); -#else - fprintf(stream, "\033[0m"); -#endif - } + if (root->color) + terminal_set_foreground_color(stream, -1); fflush(stream); -} -void mp_msg_va(int mod, int lev, const char *format, va_list va) -{ - assert(initialized); - assert(mod >= 0 && mod < MSGT_MAX); - mp_msg_log_va(legacy_logs[mod], lev, format, va); -} - -void mp_msg(int mod, int lev, const char *format, ...) -{ - va_list va; - va_start(va, format); - mp_msg_va(mod, lev, format, va); - va_end(va); + pthread_mutex_unlock(&mp_msg_lock); } -// legacy names -static const char *module_text[MSGT_MAX] = { - "global", - "cplayer", - "gplayer", - "vo", - "ao", - "demuxer", - "ds", - "demux", - "header", - "avsync", - "autoq", - "cfgparser", - "decaudio", - "decvideo", - "seek", - "win32", - "open", - "dvd", - "parsees", - "lirc", - "stream", - "cache", - "mencoder", - "xacodec", - "tv", - "osdep", - "spudec", - "playtree", - "input", - "vf", - "osd", - "network", - "cpudetect", - "codeccfg", - "sws", - "vobsub", - "subreader", - "af", - "netst", - "muxer", - "osdmenu", - "identify", - "radio", - "ass", - "loader", - "statusline", - "teletext", -}; - // Create a new log context, which uses talloc_ctx as talloc parent, and parent // as logical parent. // The name is the prefix put before the output. It's usually prefixed by the @@ -312,6 +179,8 @@ struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent, assert(parent); assert(name); struct mp_log *log = talloc_zero(talloc_ctx, struct mp_log); + if (!parent->root) + return log; // same as null_log log->root = parent->root; if (name[0] == '!') { name = &name[1]; @@ -330,53 +199,108 @@ struct mp_log *mp_log_new(void *talloc_ctx, struct mp_log *parent, log->prefix = NULL; if (!log->verbose_prefix[0]) log->verbose_prefix = "global"; - log->legacy_mod = parent->legacy_mod; - for (int n = 0; n < MSGT_MAX; n++) { - if (module_text[n] && strcmp(name, module_text[n]) == 0) { - log->legacy_mod = n; - break; - } - } return log; } void mp_msg_init(struct mpv_global *global) { - assert(!initialized); assert(!global->log); struct mp_log_root *root = talloc_zero(NULL, struct mp_log_root); root->global = global; + root->header = 1; + root->reload_counter = 1; struct mp_log dummy = { .root = root }; struct mp_log *log = mp_log_new(root, &dummy, ""); - for (int n = 0; n < MSGT_MAX; n++) { - char name[80]; - snprintf(name, sizeof(name), "!%s", module_text[n]); - legacy_logs[n] = mp_log_new(root, log, name); - } - mp_msg_do_init(); global->log = log; - initialized = true; + + mp_msg_update_msglevels(global); +} + +void mp_msg_update_msglevels(struct mpv_global *global) +{ + struct mp_log_root *root = global->log->root; + struct MPOpts *opts = global->opts; + + if (!opts) + return; + + pthread_mutex_lock(&mp_msg_lock); + + root->verbose = opts->verbose; + root->module = opts->msg_module; + root->smode = opts->msg_identify; + root->color = opts->msg_color && isatty(fileno(stdout)); + + talloc_free(root->msglevels); + root->msglevels = talloc_strdup(root, global->opts->msglevels); + + mp_atomic_add_and_fetch(&root->reload_counter, 1); + mp_memory_barrier(); + pthread_mutex_unlock(&mp_msg_lock); +} + +void mp_msg_mute(struct mpv_global *global, bool mute) +{ + struct mp_log_root *root = global->log->root; + + root->mute = mute; } -struct mpv_global *mp_log_get_global(struct mp_log *log) +void mp_msg_force_stderr(struct mpv_global *global, bool force_stderr) { - return log->root->global; + struct mp_log_root *root = global->log->root; + + root->force_stderr = force_stderr; } void mp_msg_uninit(struct mpv_global *global) { talloc_free(global->log->root); global->log = NULL; - initialized = false; } -void mp_msg_log(struct mp_log *log, int lev, const char *format, ...) +void mp_msg(struct mp_log *log, int lev, const char *format, ...) { va_list va; va_start(va, format); - mp_msg_log_va(log, lev, format, va); + mp_msg_va(log, lev, format, va); va_end(va); } + +static const char *level_names[] = { + [MSGL_FATAL] = "fatal", + [MSGL_ERR] = "error", + [MSGL_WARN] = "warn", + [MSGL_INFO] = "info", + [MSGL_STATUS] = "status", + [MSGL_V] = "v", + [MSGL_DEBUG] = "debug", + [MSGL_TRACE] = "trace", +}; + +int mp_msg_split_msglevel(struct bstr *s, struct bstr *out_mod, int *out_level) +{ + if (s->len == 0) + return 0; + bstr elem, rest; + bstr_split_tok(*s, ":", &elem, &rest); + bstr mod, level; + if (!bstr_split_tok(elem, "=", &mod, &level) || mod.len == 0) + return -1; + int ilevel = -1; + for (int n = 0; n < MP_ARRAY_SIZE(level_names); n++) { + if (level_names[n] && bstr_equals0(level, level_names[n])) { + ilevel = n; + break; + } + } + if (ilevel < 0 && !bstr_equals0(level, "no")) + return -1; + *s = rest; + *out_mod = mod; + *out_level = ilevel; + return 1; +} |