mirror of
https://github.com/brl/mutter.git
synced 2024-12-29 06:12:14 +00:00
a6d406d7bf
To be enabled with MUTTER_DEBUG=eis Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2628>
601 lines
13 KiB
C
601 lines
13 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#define _POSIX_C_SOURCE 200112L /* for fdopen() */
|
|
|
|
#include "config.h"
|
|
|
|
#include "core/display-private.h"
|
|
#include "core/util-private.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#ifdef HAVE_SYS_PRCTL
|
|
#include <sys/prctl.h>
|
|
#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);
|
|
}
|