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 0000000000000000000000000000000000000000..f9b88e9dcd06ff74be6ffa757ef6039863130019 --- /dev/null +++ b/data/dbus-interfaces/org.gnome.Mutter.DebugControl.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/src/backends/meta-monitor-manager.c b/src/backends/meta-monitor-manager.c index 9853e16f5172f99865f82e5be9af63214cbb6f24..eac51ad1d565ca37298951e5016ffa258cea88dd 100644 --- a/src/backends/meta-monitor-manager.c +++ b/src/backends/meta-monitor-manager.c @@ -116,7 +116,7 @@ typedef struct _MetaMonitorManagerPrivate gboolean has_builtin_panel; gboolean night_light_supported; - const char *experimental_hdr; + char *experimental_hdr; guint reload_monitor_manager_id; guint switch_config_handle_id; @@ -1325,18 +1325,35 @@ meta_monitor_manager_setup (MetaMonitorManager *manager) if (privacy_screen_needs_update (manager)) manager->privacy_screen_change_state = META_PRIVACY_SCREEN_CHANGE_STATE_INIT; + ensure_hdr_settings (manager); + manager->in_init = FALSE; } +static void +on_started (MetaContext *context, + MetaMonitorManager *monitor_manager) +{ + g_signal_connect (monitor_manager, "notify::experimental-hdr", + G_CALLBACK (meta_monitor_manager_reconfigure), + NULL); +} + static void meta_monitor_manager_constructed (GObject *object) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); + MetaMonitorManagerPrivate *priv = + meta_monitor_manager_get_instance_private (manager); MetaBackend *backend = manager->backend; + MetaContext *context = meta_backend_get_context (backend); MetaSettings *settings = meta_backend_get_settings (backend); manager->display_config = meta_dbus_display_config_skeleton_new (); + if (g_strcmp0 (getenv ("MUTTER_DEBUG_ENABLE_HDR"), "1") == 0) + priv->experimental_hdr = g_strdup ("on"); + g_signal_connect_object (settings, "experimental-features-changed", G_CALLBACK (experimental_features_changed), @@ -1368,14 +1385,11 @@ meta_monitor_manager_constructed (GObject *object) G_CALLBACK (lid_is_closed_changed), manager, 0); + g_signal_connect (context, "started", G_CALLBACK (on_started), manager); g_signal_connect (backend, "prepare-shutdown", G_CALLBACK (prepare_shutdown), manager); - g_signal_connect (manager, "notify::experimental-hdr", - G_CALLBACK (ensure_hdr_settings), - NULL); - manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; initialize_dbus_interface (manager); @@ -1388,6 +1402,7 @@ meta_monitor_manager_finalize (GObject *object) MetaMonitorManagerPrivate *priv = meta_monitor_manager_get_instance_private (manager); + g_clear_pointer (&priv->experimental_hdr, g_free); g_list_free_full (manager->logical_monitors, g_object_unref); g_warn_if_fail (!priv->virtual_monitors); @@ -1438,6 +1453,7 @@ meta_monitor_manager_set_property (GObject *object, manager->backend = g_value_get_object (value); break; case PROP_EXPERIMENTAL_HDR: + g_clear_pointer (&priv->experimental_hdr, g_free); priv->experimental_hdr = g_value_dup_string (value); break; case PROP_PANEL_ORIENTATION_MANAGED: @@ -3749,12 +3765,11 @@ meta_monitor_manager_rebuild (MetaMonitorManager *manager, meta_monitor_manager_update_logical_state (manager, config); - meta_monitor_manager_notify_monitors_changed (manager); - ensure_privacy_screen_settings (manager); - ensure_hdr_settings (manager); + meta_monitor_manager_notify_monitors_changed (manager); + g_list_free_full (old_logical_monitors, g_object_unref); } diff --git a/src/core/meta-context-main.c b/src/core/meta-context-main.c index 9c94183b51bfa2df70c68ef4b4f1fe33b3e4044b..023dede2e62e63686dfc39c26ec5aeaa68d73148 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 11bbf7e29ccc6e6eb2be1c8df40dc4e556a4b926..cf07d3e59f0ff86312253da50beec2155e577c37 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 50a468ad5c23c4459e9e5a0a8094469f2682a83a..6278e240e4f6b961f76b310304713158730caa6a 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 0000000000000000000000000000000000000000..0d71b58cb218191f5009c55d5b24067c5bd05bc5 --- /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 0000000000000000000000000000000000000000..dcc2217d404e99b2a4c2b5b5a8c17d97950f8308 --- /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 139b747e0e641314662060ea0468875a9dab36fe..74fdc2bac2d173504e6b864dd53d9eedcbc8c66c 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', @@ -924,6 +926,11 @@ dbus_interfaces = [ 'prefix': 'org.gnome.Mutter.', 'object_manager': true, }, + { + 'name': 'meta-dbus-input-capture', + 'interface': 'org.gnome.Mutter.InputCapture.xml', + 'prefix': 'org.gnome.Mutter', + }, { 'name': 'meta-dbus-input-mapping', 'interface': 'org.gnome.Mutter.InputMapping.xml', @@ -934,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 @@ -1036,13 +1048,6 @@ foreach dbus_interface: dbus_interfaces mutter_built_sources += dbus_sources endforeach -dbus_input_capture_built_sources = gnome.gdbus_codegen('meta-dbus-input-capture', - dbus_interfaces_dir / 'org.gnome.Mutter.InputCapture.xml', - interface_prefix: 'org.gnome.Mutter', - namespace: 'MetaDBus', -) -mutter_built_sources += dbus_input_capture_built_sources - wayland_protocol_server_headers = [] wayland_protocol_client_headers = [] wayland_protocol_sources = [] diff --git a/src/tests/meson.build b/src/tests/meson.build index de9e16efaf92d369ba9de83c56b3b87a8fcbceb3..5581058429e2b293e0f3771407d2386c938e396a 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -322,7 +322,7 @@ if have_native_tests 'input-capture-test-client.c', '../backends/meta-fd-source.c', '../backends/meta-fd-source.h', - dbus_input_capture_built_sources, + built_dbus_sources['meta-dbus-input-capture'], ], include_directories: tests_includes, c_args: [ diff --git a/tools/debug-control.py b/tools/debug-control.py new file mode 100755 index 0000000000000000000000000000000000000000..adbdfc0881dcb3087fbb5ef8c7726be860675ca7 --- /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()