diff --git a/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml b/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml
new file mode 100644
index 000000000..f9b88e9dc
--- /dev/null
+++ b/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/meta-context-main.c b/src/core/meta-context-main.c
index 9c94183b5..023dede2e 100644
--- a/src/core/meta-context-main.c
+++ b/src/core/meta-context-main.c
@@ -72,6 +72,7 @@ typedef struct _MetaContextMainOptions
GList *virtual_monitor_infos;
#endif
char *trace_file;
+ gboolean debug_control;
} MetaContextMainOptions;
struct _MetaContextMain
@@ -301,6 +302,13 @@ meta_context_main_configure (MetaContext *context,
meta_context_set_trace_file (context, context_main->options.trace_file);
#endif
+ if (context_main->options.debug_control)
+ {
+ MetaDebugControl *debug_control = meta_context_get_debug_control (context);
+
+ meta_debug_control_export (debug_control);
+ }
+
g_unsetenv ("DESKTOP_AUTOSTART_ID");
return TRUE;
@@ -671,6 +679,11 @@ meta_context_main_add_option_entries (MetaContextMain *context_main)
N_("Profile performance using trace instrumentation"),
"FILE"
},
+ {
+ "debug-control", 0, 0, G_OPTION_ARG_NONE,
+ &context_main->options.debug_control,
+ N_("Enable debug control D-Bus interface")
+ },
{ NULL }
};
diff --git a/src/core/meta-context-private.h b/src/core/meta-context-private.h
index 11bbf7e29..cf07d3e59 100644
--- a/src/core/meta-context-private.h
+++ b/src/core/meta-context-private.h
@@ -18,6 +18,7 @@
#pragma once
+#include "core/meta-debug-control.h"
#include "core/meta-private-enums.h"
#include "core/meta-service-channel.h"
#include "core/util-private.h"
@@ -84,3 +85,5 @@ meta_context_get_profiler (MetaContext *context);
void meta_context_set_trace_file (MetaContext *context,
const char *trace_file);
#endif
+
+MetaDebugControl * meta_context_get_debug_control (MetaContext *context);
diff --git a/src/core/meta-context.c b/src/core/meta-context.c
index 50a468ad5..6278e240e 100644
--- a/src/core/meta-context.c
+++ b/src/core/meta-context.c
@@ -103,6 +103,8 @@ typedef struct _MetaContextPrivate
#ifdef HAVE_WAYLAND
MetaServiceChannel *service_channel;
#endif
+
+ MetaDebugControl *debug_control;
} MetaContextPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaContext, meta_context, G_TYPE_OBJECT)
@@ -329,7 +331,13 @@ meta_context_real_configure (MetaContext *context,
}
option_context = g_steal_pointer (&priv->option_context);
- return g_option_context_parse (option_context, argc, argv, error);
+ if (!g_option_context_parse (option_context, argc, argv, error))
+ return FALSE;
+
+ priv->debug_control = g_object_new (META_TYPE_DEBUG_CONTROL,
+ "context", context,
+ NULL);
+ return TRUE;
}
/**
@@ -747,6 +755,8 @@ meta_context_dispose (GObject *object)
g_clear_pointer (&priv->backend, meta_backend_destroy);
+ g_clear_object (&priv->debug_control);
+
g_clear_pointer (&priv->option_context, g_option_context_free);
g_clear_pointer (&priv->main_loop, g_main_loop_unref);
@@ -839,3 +849,11 @@ meta_context_init (MetaContext *context)
g_warning ("Failed to save the nofile limit: %s", error->message);
}
}
+
+MetaDebugControl *
+meta_context_get_debug_control (MetaContext *context)
+{
+ MetaContextPrivate *priv = meta_context_get_instance_private (context);
+
+ return priv->debug_control;
+}
diff --git a/src/core/meta-debug-control.c b/src/core/meta-debug-control.c
new file mode 100644
index 000000000..0d71b58cb
--- /dev/null
+++ b/src/core/meta-debug-control.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2023 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ */
+
+#include "config.h"
+
+#include "core/meta-debug-control.h"
+
+#include "core/util-private.h"
+#include "meta/meta-backend.h"
+#include "meta/meta-context.h"
+
+enum
+{
+ PROP_0,
+
+ PROP_CONTEXT,
+
+ N_PROPS
+};
+
+static GParamSpec *obj_props[N_PROPS];
+
+#define META_DEBUG_CONTROL_DBUS_SERVICE "org.gnome.Mutter.DebugControl"
+#define META_DEBUG_CONTROL_DBUS_PATH "/org/gnome/Mutter/DebugControl"
+
+struct _MetaDebugControl
+{
+ MetaDBusDebugControlSkeleton parent;
+
+ MetaContext *context;
+
+ guint dbus_name_id;
+};
+
+static void meta_dbus_debug_control_iface_init (MetaDBusDebugControlIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MetaDebugControl,
+ meta_debug_control,
+ META_DBUS_TYPE_DEBUG_CONTROL_SKELETON,
+ G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_DEBUG_CONTROL,
+ meta_dbus_debug_control_iface_init))
+
+static void
+meta_dbus_debug_control_iface_init (MetaDBusDebugControlIface *iface)
+{
+}
+
+static void
+on_bus_acquired (GDBusConnection *connection,
+ const char *name,
+ gpointer user_data)
+{
+ MetaDebugControl *debug_control = META_DEBUG_CONTROL (user_data);
+ g_autoptr (GError) error = NULL;
+
+ meta_topic (META_DEBUG_BACKEND,
+ "Acquired D-Bus name '%s', exporting service on '%s'",
+ META_DEBUG_CONTROL_DBUS_SERVICE, META_DEBUG_CONTROL_DBUS_PATH);
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (debug_control),
+ connection,
+ META_DEBUG_CONTROL_DBUS_PATH,
+ &error))
+ {
+ g_warning ("Failed to export '%s' object on '%s': %s",
+ META_DEBUG_CONTROL_DBUS_SERVICE,
+ META_DEBUG_CONTROL_DBUS_PATH,
+ error->message);
+ }
+}
+
+static void
+on_enable_hdr_changed (MetaDebugControl *debug_control,
+ GParamSpec *pspec)
+{
+ MetaDBusDebugControl *dbus_debug_control =
+ META_DBUS_DEBUG_CONTROL (debug_control);
+ MetaBackend *backend = meta_context_get_backend (debug_control->context);
+ MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend);
+ gboolean enable;
+
+ enable = meta_dbus_debug_control_get_enable_hdr (dbus_debug_control);
+ g_object_set (G_OBJECT (monitor_manager),
+ "experimental-hdr", enable ? "on" : "off",
+ NULL);
+}
+
+static void
+on_experimental_hdr_changed (MetaMonitorManager *monitor_manager,
+ GParamSpec *pspec,
+ MetaDebugControl *debug_control)
+{
+ MetaDBusDebugControl *dbus_debug_control =
+ META_DBUS_DEBUG_CONTROL (debug_control);
+ g_autofree char *experimental_hdr = NULL;
+ gboolean enable;
+
+ g_object_get (G_OBJECT (monitor_manager),
+ "experimental-hdr", &experimental_hdr,
+ NULL);
+
+ enable = g_strcmp0 (experimental_hdr, "on") == 0;
+ if (enable == meta_dbus_debug_control_get_enable_hdr (dbus_debug_control))
+ return;
+
+ meta_dbus_debug_control_set_enable_hdr (META_DBUS_DEBUG_CONTROL (debug_control),
+ g_strcmp0 (experimental_hdr, "on") == 0);
+}
+
+static void
+on_context_started (MetaContext *context,
+ MetaDebugControl *debug_control)
+{
+ MetaBackend *backend = meta_context_get_backend (context);
+ MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend);
+
+ g_signal_connect (monitor_manager, "notify::experimental-hdr",
+ G_CALLBACK (on_experimental_hdr_changed),
+ debug_control);
+}
+
+static void
+meta_debug_control_constructed (GObject *object)
+{
+ MetaDebugControl *debug_control = META_DEBUG_CONTROL (object);
+
+ g_signal_connect_object (debug_control->context, "started",
+ G_CALLBACK (on_context_started), debug_control,
+ G_CONNECT_DEFAULT);
+
+ g_signal_connect_object (debug_control, "notify::enable-hdr",
+ G_CALLBACK (on_enable_hdr_changed), debug_control,
+ G_CONNECT_DEFAULT);
+
+ G_OBJECT_CLASS (meta_debug_control_parent_class)->constructed (object);
+}
+
+static void
+meta_debug_control_dispose (GObject *object)
+{
+ MetaDebugControl *debug_control = META_DEBUG_CONTROL (object);
+
+ g_clear_handle_id (&debug_control->dbus_name_id, g_bus_unown_name);
+
+ G_OBJECT_CLASS (meta_debug_control_parent_class)->dispose (object);
+}
+
+static void
+meta_debug_control_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MetaDebugControl *debug_control = META_DEBUG_CONTROL (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ debug_control->context = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_debug_control_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MetaDebugControl *debug_control = META_DEBUG_CONTROL (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONTEXT:
+ g_value_set_object (value, debug_control->context);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+meta_debug_control_class_init (MetaDebugControlClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = meta_debug_control_constructed;
+ object_class->dispose = meta_debug_control_dispose;
+ object_class->set_property = meta_debug_control_set_property;
+ object_class->get_property = meta_debug_control_get_property;
+
+ obj_props[PROP_CONTEXT] = g_param_spec_object ("context", NULL, NULL,
+ META_TYPE_CONTEXT,
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+ g_object_class_install_properties (object_class, N_PROPS, obj_props);
+}
+
+static void
+meta_debug_control_init (MetaDebugControl *debug_control)
+{
+}
+
+void
+meta_debug_control_export (MetaDebugControl *debug_control)
+{
+ debug_control->dbus_name_id =
+ g_bus_own_name (G_BUS_TYPE_SESSION,
+ META_DEBUG_CONTROL_DBUS_SERVICE,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ on_bus_acquired,
+ NULL,
+ NULL,
+ debug_control,
+ NULL);
+}
diff --git a/src/core/meta-debug-control.h b/src/core/meta-debug-control.h
new file mode 100644
index 000000000..dcc2217d4
--- /dev/null
+++ b/src/core/meta-debug-control.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 Red Hat
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ *
+ */
+
+#pragma once
+
+#include "meta-dbus-debug-control.h"
+
+#define META_TYPE_DEBUG_CONTROL (meta_debug_control_get_type ())
+G_DECLARE_FINAL_TYPE (MetaDebugControl,
+ meta_debug_control,
+ META, DEBUG_CONTROL,
+ MetaDBusDebugControlSkeleton)
+
+void meta_debug_control_export (MetaDebugControl *debug_control);
diff --git a/src/meson.build b/src/meson.build
index 4be9b2bd4..74fdc2bac 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -365,6 +365,8 @@ mutter_sources = [
'core/meta-context-main.h',
'core/meta-context-private.h',
'core/meta-context.c',
+ 'core/meta-debug-control.c',
+ 'core/meta-debug-control.h',
'core/meta-fraction.c',
'core/meta-fraction.h',
'core/meta-gesture-tracker.c',
@@ -939,6 +941,11 @@ dbus_interfaces = [
'interface': 'org.gnome.Mutter.ServiceChannel.xml',
'prefix': 'org.gnome.Mutter.',
},
+ {
+ 'name': 'meta-dbus-debug-control',
+ 'interface': 'org.gnome.Mutter.DebugControl.xml',
+ 'prefix': 'org.gnome.Mutter',
+ },
]
if have_profiler
diff --git a/tools/debug-control.py b/tools/debug-control.py
new file mode 100755
index 000000000..adbdfc088
--- /dev/null
+++ b/tools/debug-control.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+
+import argparse
+import dbus
+
+NAME = 'org.gnome.Mutter.DebugControl'
+INTERFACE = 'org.gnome.Mutter.DebugControl'
+OBJECT_PATH = '/org/gnome/Mutter/DebugControl'
+
+PROPS_IFACE = 'org.freedesktop.DBus.Properties'
+
+def bool_to_string(value):
+ if value:
+ return "true"
+ else:
+ return "false"
+
+def get_debug_control():
+ bus = dbus.SessionBus()
+ return bus.get_object(NAME, OBJECT_PATH)
+
+def status():
+ debug_control = get_debug_control()
+ props = debug_control.GetAll(INTERFACE, dbus_interface=PROPS_IFACE)
+ for prop in props:
+ print(f"{prop}: {bool_to_string(props[prop])}")
+
+def enable(prop):
+ debug_control = get_debug_control()
+ debug_control.Set(INTERFACE, prop, dbus.Boolean(True, variant_level=1),
+ dbus_interface=PROPS_IFACE)
+
+def disable(prop):
+ debug_control = get_debug_control()
+ debug_control.Set(INTERFACE, prop, dbus.Boolean(False, variant_level=1),
+ dbus_interface=PROPS_IFACE)
+
+def toggle(prop):
+ debug_control = get_debug_control()
+
+ value = debug_control.Get(INTERFACE, prop, dbus_interface=PROPS_IFACE)
+ debug_control.Set(INTERFACE, prop, dbus.Boolean(not value, variant_level=1),
+ dbus_interface=PROPS_IFACE)
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser(description='Get and set debug state')
+
+ parser.add_argument('--status', action='store_true')
+ parser.add_argument('--enable', metavar='PROPERTY', type=str, nargs='?')
+ parser.add_argument('--disable', metavar='PROPERTY', type=str, nargs='?')
+ parser.add_argument('--toggle', metavar='PROPERTY', type=str, nargs='?')
+
+ args = parser.parse_args()
+ if args.status:
+ status()
+ elif args.enable:
+ enable(args.enable)
+ elif args.disable:
+ disable(args.disable)
+ elif args.toggle:
+ toggle(args.toggle)
+ else:
+ parser.print_usage()