/* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #define _POSIX_C_SOURCE 200112L /* for fdopen() */ #include "config.h" #include "core/display-private.h" #include "core/util-private.h" #include #include #include #include #include #ifdef HAVE_SYS_PRCTL #include #endif #include "clutter/clutter-mutter.h" #include "cogl/cogl.h" #include "meta/common.h" #include "meta/main.h" static const GDebugKey meta_debug_keys[] = { { "focus", META_DEBUG_FOCUS }, { "workarea", META_DEBUG_WORKAREA }, { "stack", META_DEBUG_STACK }, { "sm", META_DEBUG_SM }, { "events", META_DEBUG_EVENTS }, { "window-state", META_DEBUG_WINDOW_STATE }, { "window-ops", META_DEBUG_WINDOW_OPS }, { "geometry", META_DEBUG_GEOMETRY }, { "placement", META_DEBUG_PLACEMENT }, { "ping", META_DEBUG_PING }, { "keybindings", META_DEBUG_KEYBINDINGS }, { "sync", META_DEBUG_SYNC }, { "startup", META_DEBUG_STARTUP }, { "prefs", META_DEBUG_PREFS }, { "groups", META_DEBUG_GROUPS }, { "resizing", META_DEBUG_RESIZING }, { "shapes", META_DEBUG_SHAPES }, { "edge-resistance", META_DEBUG_EDGE_RESISTANCE }, { "dbus", META_DEBUG_DBUS }, { "input", META_DEBUG_INPUT }, { "wayland", META_DEBUG_WAYLAND }, { "kms", META_DEBUG_KMS }, { "screen-cast", META_DEBUG_SCREEN_CAST }, { "remote-desktop", META_DEBUG_REMOTE_DESKTOP }, { "backend", META_DEBUG_BACKEND }, { "render", META_DEBUG_RENDER }, { "color", META_DEBUG_COLOR }, { "input-events", META_DEBUG_INPUT_EVENTS }, { "eis", META_DEBUG_EIS }, }; static gint verbose_topics = 0; static gboolean is_wayland_compositor = FALSE; static int debug_paint_flags = 0; static GLogLevelFlags mutter_log_level = G_LOG_LEVEL_MESSAGE; #ifdef WITH_VERBOSE_MODE static FILE* logfile = NULL; static void ensure_logfile (void) { if (logfile == NULL && g_getenv ("MUTTER_USE_LOGFILE")) { char *filename = NULL; char *tmpl; int fd; GError *err; tmpl = g_strdup_printf ("mutter-%d-debug-log-XXXXXX", (int) getpid ()); err = NULL; fd = g_file_open_tmp (tmpl, &filename, &err); g_free (tmpl); if (err != NULL) { meta_warning ("Failed to open debug log: %s", err->message); g_error_free (err); return; } logfile = fdopen (fd, "w"); if (logfile == NULL) { meta_warning ("Failed to fdopen() log file %s: %s", filename, strerror (errno)); close (fd); } else { g_printerr ("Opened log file %s", filename); } g_free (filename); } } #endif gboolean meta_is_verbose (void) { return verbose_topics != 0; } void meta_set_verbose (gboolean setting) { #ifndef WITH_VERBOSE_MODE if (setting) meta_fatal (_("Mutter was compiled without support for verbose mode")); #endif if (setting) meta_add_verbose_topic (META_DEBUG_VERBOSE); else meta_remove_verbose_topic (META_DEBUG_VERBOSE); } /** * meta_add_verbose_topic: * @topic: Topic for which logging will be started * * Ensure log messages for the given topic @topic * will be printed. */ void meta_add_verbose_topic (MetaDebugTopic topic) { if (verbose_topics == META_DEBUG_VERBOSE) return; #ifdef WITH_VERBOSE_MODE ensure_logfile (); #endif if (topic == META_DEBUG_VERBOSE) verbose_topics = META_DEBUG_VERBOSE; else verbose_topics |= topic; } /** * meta_remove_verbose_topic: * @topic: Topic for which logging will be stopped * * Stop printing log messages for the given topic @topic. * * Note that this method does not stack with [func@Meta.add_verbose_topic]; * i.e. if two calls to [func@Meta.add_verbose_topic] for the same * topic are made, one call to [func@Meta.remove_verbose_topic] will * remove it. */ void meta_remove_verbose_topic (MetaDebugTopic topic) { if (topic == META_DEBUG_VERBOSE) verbose_topics = 0; else verbose_topics &= ~topic; } void meta_init_debug_utils (void) { const char *debug_env; #ifdef HAVE_SYS_PRCTL prctl (PR_SET_DUMPABLE, 1); #endif if (g_getenv ("MUTTER_VERBOSE")) meta_set_verbose (TRUE); debug_env = g_getenv ("MUTTER_DEBUG"); if (debug_env) { MetaDebugTopic topics; topics = g_parse_debug_string (debug_env, meta_debug_keys, G_N_ELEMENTS (meta_debug_keys)); meta_add_verbose_topic (topics); } if (g_test_initialized ()) mutter_log_level = G_LOG_LEVEL_DEBUG; } gboolean meta_is_wayland_compositor (void) { return is_wayland_compositor; } void meta_set_is_wayland_compositor (gboolean value) { is_wayland_compositor = value; } char * meta_g_utf8_strndup (const gchar *src, gsize n) { const gchar *s = src; while (n && *s) { s = g_utf8_next_char (s); n--; } return g_strndup (src, s - src); } static int utf8_fputs (const char *str, FILE *f) { char *l; int retval; l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); if (l == NULL) retval = fputs (str, f); /* just print it anyway, better than nothing */ else retval = fputs (l, f); g_free (l); return retval; } #ifdef WITH_VERBOSE_MODE const char * meta_topic_to_string (MetaDebugTopic topic) { switch (topic) { case META_DEBUG_FOCUS: return "FOCUS"; case META_DEBUG_WORKAREA: return "WORKAREA"; case META_DEBUG_STACK: return "STACK"; case META_DEBUG_SM: return "SM"; case META_DEBUG_EVENTS: return "EVENTS"; case META_DEBUG_WINDOW_STATE: return "WINDOW_STATE"; case META_DEBUG_WINDOW_OPS: return "WINDOW_OPS"; case META_DEBUG_PLACEMENT: return "PLACEMENT"; case META_DEBUG_GEOMETRY: return "GEOMETRY"; case META_DEBUG_PING: return "PING"; case META_DEBUG_KEYBINDINGS: return "KEYBINDINGS"; case META_DEBUG_SYNC: return "SYNC"; case META_DEBUG_STARTUP: return "STARTUP"; case META_DEBUG_PREFS: return "PREFS"; case META_DEBUG_GROUPS: return "GROUPS"; case META_DEBUG_RESIZING: return "RESIZING"; case META_DEBUG_SHAPES: return "SHAPES"; case META_DEBUG_EDGE_RESISTANCE: return "EDGE_RESISTANCE"; case META_DEBUG_DBUS: return "DBUS"; case META_DEBUG_INPUT: return "INPUT"; case META_DEBUG_WAYLAND: return "WAYLAND"; case META_DEBUG_KMS: return "KMS"; case META_DEBUG_SCREEN_CAST: return "SCREEN_CAST"; case META_DEBUG_REMOTE_DESKTOP: return "REMOTE_DESKTOP"; case META_DEBUG_BACKEND: return "BACKEND"; case META_DEBUG_RENDER: return "RENDER"; case META_DEBUG_COLOR: return "COLOR"; case META_DEBUG_VERBOSE: return "VERBOSE"; case META_DEBUG_INPUT_EVENTS: return "INPUT_EVENTS"; case META_DEBUG_EIS: return "EIS"; } return "WM"; } gboolean meta_is_topic_enabled (MetaDebugTopic topic) { if (verbose_topics == 0) return FALSE; if (topic == META_DEBUG_VERBOSE && verbose_topics != META_DEBUG_VERBOSE) return FALSE; return !!(verbose_topics & topic); } #endif /* WITH_VERBOSE_MODE */ void meta_bug (const char *format, ...) { va_list args; gchar *str; FILE *out; g_return_if_fail (format != NULL); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); #ifdef WITH_VERBOSE_MODE out = logfile ? logfile : stderr; #else out = stderr; #endif utf8_fputs ("Bug in window manager: ", out); utf8_fputs (str, out); utf8_fputs ("\n", out); fflush (out); g_free (str); /* stop us in a debugger */ abort (); } void meta_warning (const char *format, ...) { va_list args; gchar *str; FILE *out; g_return_if_fail (format != NULL); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); #ifdef WITH_VERBOSE_MODE out = logfile ? logfile : stderr; #else out = stderr; #endif utf8_fputs ("Window manager warning: ", out); utf8_fputs (str, out); utf8_fputs ("\n", out); fflush (out); g_free (str); } void meta_fatal (const char *format, ...) { va_list args; gchar *str; FILE *out; g_warn_if_fail (format); if (!format) meta_exit (META_EXIT_ERROR); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); #ifdef WITH_VERBOSE_MODE out = logfile ? logfile : stderr; #else out = stderr; #endif utf8_fputs ("Window manager error: ", out); utf8_fputs (str, out); utf8_fputs ("\n", out); fflush (out); g_free (str); meta_exit (META_EXIT_ERROR); } void meta_exit (MetaExitCode code) { exit (code); } gint meta_unsigned_long_equal (gconstpointer v1, gconstpointer v2) { return *((const gulong*) v1) == *((const gulong*) v2); } guint meta_unsigned_long_hash (gconstpointer v) { gulong val = * (const gulong *) v; /* I'm not sure this works so well. */ #if GLIB_SIZEOF_LONG > 4 return (guint) (val ^ (val >> 32)); #else return val; #endif } const char* meta_gravity_to_string (MetaGravity gravity) { switch (gravity) { case META_GRAVITY_NORTH_WEST: return "META_GRAVITY_NORTH_WEST"; break; case META_GRAVITY_NORTH: return "META_GRAVITY_NORTH"; break; case META_GRAVITY_NORTH_EAST: return "META_GRAVITY_NORTH_EAST"; break; case META_GRAVITY_WEST: return "META_GRAVITY_WEST"; break; case META_GRAVITY_CENTER: return "META_GRAVITY_CENTER"; break; case META_GRAVITY_EAST: return "META_GRAVITY_EAST"; break; case META_GRAVITY_SOUTH_WEST: return "META_GRAVITY_SOUTH_WEST"; break; case META_GRAVITY_SOUTH: return "META_GRAVITY_SOUTH"; break; case META_GRAVITY_SOUTH_EAST: return "META_GRAVITY_SOUTH_EAST"; break; case META_GRAVITY_STATIC: return "META_GRAVITY_STATIC"; break; default: return "META_GRAVITY_NORTH_WEST"; break; } } char* meta_external_binding_name_for_action (guint keybinding_action) { return g_strdup_printf ("external-grab-%u", keybinding_action); } MetaLocaleDirection meta_get_locale_direction (void) { switch (clutter_get_text_direction ()) { case CLUTTER_TEXT_DIRECTION_LTR: return META_LOCALE_DIRECTION_LTR; case CLUTTER_TEXT_DIRECTION_RTL: return META_LOCALE_DIRECTION_RTL; default: g_assert_not_reached (); return 0; } } char * meta_generate_random_id (GRand *rand, int length) { char *id; int i; /* Generate a random string of printable ASCII characters. */ id = g_new0 (char, length + 1); for (i = 0; i < length; i++) id[i] = (char) g_rand_int_range (rand, 32, 127); return id; } void meta_add_clutter_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags) { clutter_add_debug_flags (debug_flags, draw_flags, pick_flags); } void meta_remove_clutter_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags) { clutter_remove_debug_flags (debug_flags, draw_flags, pick_flags); } /** * meta_get_clutter_debug_flags: * @debug_flags: (out) (optional): return location for debug flags * @draw_flags: (out) (optional): return location for draw debug flags * @pick_flags: (out) (optional): return location for pick debug flags */ void meta_get_clutter_debug_flags (ClutterDebugFlag *debug_flags, ClutterDrawDebugFlag *draw_flags, ClutterPickDebugFlag *pick_flags) { clutter_get_debug_flags (debug_flags, draw_flags, pick_flags); } void meta_add_debug_paint_flag (MetaDebugPaintFlag flag) { debug_paint_flags |= flag; } void meta_remove_debug_paint_flag (MetaDebugPaintFlag flag) { debug_paint_flags &= ~flag; } MetaDebugPaintFlag meta_get_debug_paint_flags (void) { return debug_paint_flags; } void meta_log (const char *format, ...) { va_list args; va_start (args, format); g_logv (G_LOG_DOMAIN, mutter_log_level, format, args); va_end (args); }