summaryrefslogtreecommitdiffstats
path: root/common/msg.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/msg.c')
-rw-r--r--common/msg.c402
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;
+}