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()