1
0
Fork 0

monitor-manager: Add new backlight D-Bus API

It is intended to replace using GetResources() and ChangeBacklight().

It moves from a normalized 1-100 numbers, to directly exposing the
hardware. This more closely maps to how gsd-backlight.c in
gnome-settings-daemon normally works, and simplifies the API a bit to
not have to deal with rounding issues.

There is still no KMS uAPI for this, so it still only hooks up to
XRANDR. Being private API, it doesn't try very hard to predict how the
KMS uAPI will look. When that day comes, it will likely need some
adaptations.

Part of the motivation here is to get something for gsd-backlight.c to
use where it can work more similarly to how the current common case
(sysefs) works, while attempting to migrate away from libgnome-rr.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3861>
This commit is contained in:
Jonas Ådahl 2024-07-01 15:40:37 +02:00
parent 332c4a1bf0
commit cc0ec14712
5 changed files with 423 additions and 1 deletions

View file

@ -210,12 +210,42 @@
Returns the new value after rounding. Returns the new value after rounding.
--> -->
<method name="ChangeBacklight"> <method name="ChangeBacklight">
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
<arg name="serial" direction="in" type="u" /> <arg name="serial" direction="in" type="u" />
<arg name="output" direction="in" type="u" /> <arg name="output" direction="in" type="u" />
<arg name="value" direction="in" type="i" /> <arg name="value" direction="in" type="i" />
<arg name="new_value" direction="out" type="i" /> <arg name="new_value" direction="out" type="i" />
</method> </method>
<!--
SetBacklight:
@serial: configuration serial
@connector: the connector name
@value: the new backlight value
Changes the backlight of @output to @value.
-->
<method name="SetBacklight">
<arg name="serial" direction="in" type="u" />
<arg name="connector" direction="in" type="s" />
<arg name="value" direction="in" type="i" />
</method>
<!--
Backlight:
A set of backlights. Each backlight is a dictionary with the following
entries. If an entry is only a connector, it means there is no way to
control it via this D-Bus interface.
* 'connector' (s) - An associated monitor connector
* 'active' (s) - True if the monitor is active
* 'value' (i) - Current value (optional)
The initial 'u' is a serial number used when setting the backlight.
-->
<property name="Backlight" type="(uaa{sv})" access="read" />
<!-- <!--
GetCrtcGamma: GetCrtcGamma:
@serial: configuration serial @serial: configuration serial

View file

@ -120,6 +120,8 @@ typedef struct _MetaMonitorManagerPrivate
guint reload_monitor_manager_id; guint reload_monitor_manager_id;
guint switch_config_handle_id; guint switch_config_handle_id;
uint32_t backlight_serial;
} MetaMonitorManagerPrivate; } MetaMonitorManagerPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (MetaMonitorManager, meta_monitor_manager, G_DEFINE_TYPE_WITH_PRIVATE (MetaMonitorManager, meta_monitor_manager,
@ -139,6 +141,9 @@ static gboolean
is_global_scale_matching_in_config (MetaMonitorsConfig *config, is_global_scale_matching_in_config (MetaMonitorsConfig *config,
float scale); float scale);
static void update_backlight (MetaMonitorManager *manager,
gboolean bump_serial);
MetaBackend * MetaBackend *
meta_monitor_manager_get_backend (MetaMonitorManager *manager) meta_monitor_manager_get_backend (MetaMonitorManager *manager)
{ {
@ -1205,6 +1210,8 @@ meta_monitor_manager_notify_monitors_changed (MetaMonitorManager *manager)
{ {
meta_backend_monitors_changed (manager->backend); meta_backend_monitors_changed (manager->backend);
update_backlight (manager, TRUE);
g_signal_emit (manager, signals[MONITORS_CHANGED_INTERNAL], 0); g_signal_emit (manager, signals[MONITORS_CHANGED_INTERNAL], 0);
g_signal_emit (manager, signals[MONITORS_CHANGED], 0); g_signal_emit (manager, signals[MONITORS_CHANGED], 0);
@ -1240,6 +1247,8 @@ meta_monitor_manager_setup (MetaMonitorManager *manager)
meta_monitor_manager_notify_monitors_changed (manager); meta_monitor_manager_notify_monitors_changed (manager);
update_backlight (manager, TRUE);
manager->in_init = FALSE; manager->in_init = FALSE;
} }
@ -2365,7 +2374,7 @@ meta_monitor_manager_is_config_complete (MetaMonitorManager *manager,
static MetaMonitor * static MetaMonitor *
find_monitor_from_connector (MetaMonitorManager *manager, find_monitor_from_connector (MetaMonitorManager *manager,
char *connector) const char *connector)
{ {
GList *monitors; GList *monitors;
GList *l; GList *l;
@ -2910,9 +2919,72 @@ meta_monitor_manager_handle_change_backlight (MetaDBusDisplayConfig *skeleton,
meta_output_set_backlight (output, value); meta_output_set_backlight (output, value);
renormalized_value = normalize_backlight (output, value); renormalized_value = normalize_backlight (output, value);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
meta_dbus_display_config_complete_change_backlight (skeleton, meta_dbus_display_config_complete_change_backlight (skeleton,
invocation, invocation,
renormalized_value); renormalized_value);
G_GNUC_END_IGNORE_DEPRECATIONS
update_backlight (manager, FALSE);
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean
meta_monitor_manager_handle_set_backlight (MetaDBusDisplayConfig *skeleton,
GDBusMethodInvocation *invocation,
uint32_t serial,
const char * connector,
int value,
MetaMonitorManager *monitor_manager)
{
MetaMonitorManagerPrivate *priv =
meta_monitor_manager_get_instance_private (monitor_manager);
MetaMonitor *monitor;
int backlight_min;
int backlight_max;
if (serial != priv->backlight_serial)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid backlight serial");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
monitor = find_monitor_from_connector (monitor_manager, connector);
if (!monitor)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Unknown monitor");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (!meta_monitor_get_backlight_info (monitor,
&backlight_min,
&backlight_max))
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Monitor doesn't support changing backlight");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
if (value < backlight_min || value > backlight_max)
{
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid backlight value");
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
meta_monitor_set_backlight (monitor, value);
meta_dbus_display_config_complete_set_backlight (skeleton, invocation);
update_backlight (monitor_manager, FALSE);
return G_DBUS_METHOD_INVOCATION_HANDLED; return G_DBUS_METHOD_INVOCATION_HANDLED;
} }
@ -3099,6 +3171,9 @@ monitor_manager_setup_dbus_config_handlers (MetaMonitorManager *manager)
g_signal_connect_object (manager->display_config, "handle-change-backlight", g_signal_connect_object (manager->display_config, "handle-change-backlight",
G_CALLBACK (meta_monitor_manager_handle_change_backlight), G_CALLBACK (meta_monitor_manager_handle_change_backlight),
manager, 0); manager, 0);
g_signal_connect_object (manager->display_config, "handle-set-backlight",
G_CALLBACK (meta_monitor_manager_handle_set_backlight),
manager, 0);
g_signal_connect_object (manager->display_config, "handle-get-crtc-gamma", g_signal_connect_object (manager->display_config, "handle-get-crtc-gamma",
G_CALLBACK (meta_monitor_manager_handle_get_crtc_gamma), G_CALLBACK (meta_monitor_manager_handle_get_crtc_gamma),
manager, 0); manager, 0);
@ -3594,6 +3669,68 @@ meta_monitor_manager_read_current_state (MetaMonitorManager *manager)
manager_class->read_current_state (manager); manager_class->read_current_state (manager);
} }
static void
update_backlight (MetaMonitorManager *manager,
gboolean bump_serial)
{
MetaMonitorManagerPrivate *priv =
meta_monitor_manager_get_instance_private (manager);
GList *l;
GVariantBuilder backlight_builder;
g_autoptr (GVariant) backlight = NULL;
if (bump_serial)
priv->backlight_serial++;
g_variant_builder_init (&backlight_builder, G_VARIANT_TYPE ("(uaa{sv})"));
g_variant_builder_add (&backlight_builder, "u", priv->backlight_serial);
g_variant_builder_open (&backlight_builder, G_VARIANT_TYPE ("aa{sv}"));
for (l = manager->monitors; l; l = l->next)
{
MetaMonitor *monitor = META_MONITOR (l->data);
const char *connector;
gboolean active;
int value;
if (!meta_monitor_is_laptop_panel (monitor))
continue;
g_variant_builder_open (&backlight_builder,
G_VARIANT_TYPE_VARDICT);
connector = meta_monitor_get_connector (monitor);
active = meta_monitor_is_active (monitor);
g_variant_builder_add (&backlight_builder, "{sv}",
"connector", g_variant_new_string (connector));
g_variant_builder_add (&backlight_builder, "{sv}",
"active", g_variant_new_boolean (active));
if (meta_monitor_get_backlight (monitor, &value))
{
int min, max;
meta_monitor_get_backlight_info (monitor, &min, &max);
g_variant_builder_add (&backlight_builder, "{sv}",
"min", g_variant_new_int32 (min));
g_variant_builder_add (&backlight_builder, "{sv}",
"max", g_variant_new_int32 (max));
g_variant_builder_add (&backlight_builder, "{sv}",
"value", g_variant_new_int32 (value));
}
g_variant_builder_close (&backlight_builder);
}
g_variant_builder_close (&backlight_builder);
backlight = g_variant_builder_end (&backlight_builder);
meta_dbus_display_config_set_backlight (manager->display_config,
g_steal_pointer (&backlight));
}
static void static void
set_logical_monitor_modes (MetaMonitorManager *manager, set_logical_monitor_modes (MetaMonitorManager *manager,
MetaLogicalMonitorConfig *logical_monitor_config) MetaLogicalMonitorConfig *logical_monitor_config)

View file

@ -335,6 +335,23 @@ screen_cast_client = executable('mutter-screen-cast-client',
install_rpath: pkglibdir, install_rpath: pkglibdir,
) )
backlight_client = executable('monitor-backlight-client',
sources: [
'monitor-backlight-client.c',
built_dbus_sources['meta-dbus-display-config'],
],
include_directories: tests_includes,
c_args: [
tests_c_args,
'-DG_LOG_DOMAIN="mutter-monitor-backlight-test-client"',
],
dependencies: [
gio_dep,
],
install: have_installed_tests,
install_dir: mutter_installed_tests_libexecdir,
)
input_capture_client = executable('mutter-input-capture-test-client', input_capture_client = executable('mutter-input-capture-test-client',
sources: [ sources: [
'input-capture-test-client.c', 'input-capture-test-client.c',

View file

@ -0,0 +1,179 @@
/*
* Copyright (C) 2025 Red Hat, Inc.
*
* 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/>.
*/
#include "config.h"
#include <glib.h>
#include <gio/gio.h>
#include <stdint.h>
#include <stdio.h>
#include "meta-dbus-display-config.h"
static void
backlight_changed_cb (MetaDBusDisplayConfig *proxy,
GParamSpec *pspec,
int *new_value)
{
GVariant *backlight_variant = NULL;
uint32_t serial;
g_autoptr (GVariant) backlights = NULL;
g_autoptr (GVariant) backlight_monitor = NULL;
int value;
backlight_variant = meta_dbus_display_config_get_backlight (proxy);
g_variant_get (backlight_variant, "(u@aa{sv})",
&serial, &backlights);
g_variant_get_child (backlights, 0, "@a{sv}",
&backlight_monitor);
g_variant_lookup (backlight_monitor, "value", "i", &value);
*new_value = value;
}
static void
test_legacy_backlight (MetaDBusDisplayConfig *proxy)
{
g_autoptr (GVariant) resources_variant = NULL;
g_autoptr (GError) error = NULL;
unsigned int serial;
g_autoptr (GVariant) crtcs = NULL;
g_autoptr (GVariant) outputs = NULL;
g_autoptr (GVariant) modes = NULL;
int max_screen_width, max_screen_height;
int output_id;
g_autoptr (GVariant) output_properties = NULL;
int normalized_backlight;
gulong handler_id;
int new_value = -1;
g_debug ("Running %s test", __func__);
meta_dbus_display_config_call_get_resources_sync (proxy,
&serial,
&crtcs,
&outputs,
&modes,
&max_screen_width,
&max_screen_height,
NULL,
&error);
g_assert_no_error (error);
g_assert_cmpint (g_variant_n_children (outputs), ==, 2);
g_variant_get_child (outputs, 0,
"(uxiausauau@a{sv})",
&output_id,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&output_properties);
g_variant_lookup (output_properties,
"backlight", "i",
&normalized_backlight);
g_assert_cmpint (normalized_backlight, >=, 0);
g_assert_cmpint (normalized_backlight, <=, 100);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
meta_dbus_display_config_call_change_backlight_sync (proxy,
serial,
output_id,
1,
NULL,
NULL, &error);
G_GNUC_END_IGNORE_DEPRECATIONS
g_debug ("Checking denormalization");
handler_id = g_signal_connect (proxy, "notify::backlight",
G_CALLBACK (backlight_changed_cb), &new_value);
while (new_value == -1)
g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (new_value, ==, 3);
g_signal_handler_disconnect (proxy, handler_id);
}
static void
test_set_backlight (MetaDBusDisplayConfig *proxy)
{
g_autoptr (GError) error = NULL;
GVariant *backlight_variant;
uint32_t serial;
g_autoptr (GVariant) backlights = NULL;
g_autoptr (GVariant) backlight_monitor = NULL;
g_autofree char *connector = NULL;
int min;
int max;
int value;
gulong handler_id;
int new_value = -1;
g_debug ("Running %s test", __func__);
backlight_variant = meta_dbus_display_config_get_backlight (proxy);
g_variant_get (backlight_variant, "(u@aa{sv})",
&serial, &backlights);
g_variant_get_child (backlights, 0, "@a{sv}",
&backlight_monitor);
g_variant_lookup (backlight_monitor, "connector", "s", &connector);
g_variant_lookup (backlight_monitor, "min", "i", &min);
g_variant_lookup (backlight_monitor, "max", "i", &max);
g_variant_lookup (backlight_monitor, "value", "i", &value);
g_assert_cmpint (min, ==, 0);
g_assert_cmpint (max, ==, 300);
handler_id = g_signal_connect (proxy, "notify::backlight",
G_CALLBACK (backlight_changed_cb), &new_value);
meta_dbus_display_config_call_set_backlight_sync (proxy,
serial,
connector,
value - 10,
NULL, &error);
g_assert_no_error (error);
while (new_value == -1)
g_main_context_iteration (NULL, TRUE);
g_assert_cmpint (new_value, ==, value - 10);
g_signal_handler_disconnect (proxy, handler_id);
}
int
main (int argc,
char **argv)
{
g_autoptr (MetaDBusDisplayConfig) proxy = NULL;
g_autoptr (GError) error = NULL;
proxy = meta_dbus_display_config_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
"org.gnome.Mutter.DisplayConfig",
"/org/gnome/Mutter/DisplayConfig",
NULL, &error);
g_assert_no_error (error);
g_assert_nonnull (proxy);
test_set_backlight (proxy);
test_legacy_backlight (proxy);
return EXIT_SUCCESS;
}

View file

@ -22,6 +22,8 @@
#include "tests/meta-test/meta-context-test.h" #include "tests/meta-test/meta-context-test.h"
#include "tests/meta-monitor-test-utils.h" #include "tests/meta-monitor-test-utils.h"
#include "meta-dbus-display-config.h"
static MonitorTestCaseSetup initial_test_case_setup = { static MonitorTestCaseSetup initial_test_case_setup = {
.modes = { .modes = {
{ {
@ -129,6 +131,62 @@ meta_test_backlight_sanity (void)
g_assert_cmpint (output_info->backlight_max, ==, 0); g_assert_cmpint (output_info->backlight_max, ==, 0);
} }
static char *
get_test_client_path (const char *test_client_name)
{
return g_test_build_filename (G_TEST_BUILT,
test_client_name,
NULL);
}
static void
subprocess_wait_check_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) error = NULL;
gboolean *exited = user_data;
g_assert_true (g_subprocess_wait_check_finish (G_SUBPROCESS (source_object),
res,
&error));
g_assert_no_error (error);
*exited = TRUE;
}
static void
meta_test_backlight_api (void)
{
g_autoptr (GSubprocessLauncher) launcher = NULL;
g_autoptr (GSubprocess) subprocess = NULL;
g_autoptr (GError) error = NULL;
g_autofree char *test_client_path = NULL;
gboolean exited = FALSE;
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE);
g_subprocess_launcher_setenv (launcher,
"G_MESSAGES_DEBUG", "all",
TRUE);
g_subprocess_launcher_setenv (launcher,
"G_DEBUG", "fatal-warnings",
TRUE);
test_client_path = get_test_client_path ("monitor-backlight-client");
subprocess =
g_subprocess_launcher_spawn (launcher,
&error,
test_client_path,
NULL);
g_assert_no_error (error);
g_assert_nonnull (subprocess);
g_subprocess_wait_check_async (subprocess, NULL,
subprocess_wait_check_cb,
&exited);
while (!exited)
g_main_context_iteration (NULL, TRUE);
}
int int
main (int argc, main (int argc,
char *argv[]) char *argv[])
@ -143,6 +201,7 @@ main (int argc,
meta_init_monitor_test_setup (create_test_setup); meta_init_monitor_test_setup (create_test_setup);
g_test_add_func ("/backends/backlight/sanity", meta_test_backlight_sanity); g_test_add_func ("/backends/backlight/sanity", meta_test_backlight_sanity);
g_test_add_func ("/backends/backlight/api", meta_test_backlight_api);
return meta_context_test_run_tests (META_CONTEXT_TEST (context), return meta_context_test_run_tests (META_CONTEXT_TEST (context),
META_TEST_RUN_FLAG_NONE); META_TEST_RUN_FLAG_NONE);