mutter/src/backends/meta-settings.c
Dor Askayo e72baac97d settings: Add experimental feature for variable refresh rate
Require the "variable-refresh-rate" keyword under the
"experimental-features" gsetting to enable the feature for now.

It would no longer be required once the experience with variable
refresh rate is good enough for general use and handles all common
use cases well.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1154>
2024-03-02 12:08:21 +00:00

641 lines
18 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2017 Red Hat
*
* 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 "backends/meta-settings-private.h"
#include <gio/gio.h>
#include "backends/meta-backend-private.h"
#include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor-manager-private.h"
#ifndef XWAYLAND_GRAB_DEFAULT_ACCESS_RULES
# warning "XWAYLAND_GRAB_DEFAULT_ACCESS_RULES is not set"
# define XWAYLAND_GRAB_DEFAULT_ACCESS_RULES ""
#endif
enum
{
UI_SCALING_FACTOR_CHANGED,
GLOBAL_SCALING_FACTOR_CHANGED,
FONT_DPI_CHANGED,
EXPERIMENTAL_FEATURES_CHANGED,
PRIVACY_SCREEN_CHANGED,
N_SIGNALS
};
static guint signals[N_SIGNALS];
struct _MetaSettings
{
GObject parent;
MetaBackend *backend;
GSettings *interface_settings;
GSettings *mutter_settings;
GSettings *privacy_settings;
GSettings *wayland_settings;
int ui_scaling_factor;
int global_scaling_factor;
int font_dpi;
gboolean privacy_screen;
MetaExperimentalFeature experimental_features;
gboolean experimental_features_overridden;
gboolean xwayland_allow_grabs;
GPtrArray *xwayland_grab_allow_list_patterns;
GPtrArray *xwayland_grab_deny_list_patterns;
/* A bitmask of MetaXwaylandExtension enum */
int xwayland_disable_extensions;
/* Whether Xwayland should allow X11 clients from different endianness */
gboolean xwayland_allow_byte_swapped_clients;
};
G_DEFINE_TYPE (MetaSettings, meta_settings, G_TYPE_OBJECT)
static int
calculate_ui_scaling_factor (MetaSettings *settings)
{
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (settings->backend);
MetaLogicalMonitor *primary_logical_monitor;
primary_logical_monitor =
meta_monitor_manager_get_primary_logical_monitor (monitor_manager);
if (!primary_logical_monitor)
return 1;
return (int) meta_logical_monitor_get_scale (primary_logical_monitor);
}
static gboolean
update_ui_scaling_factor (MetaSettings *settings)
{
int ui_scaling_factor;
if (meta_backend_is_stage_views_scaled (settings->backend))
ui_scaling_factor = 1;
else
ui_scaling_factor = calculate_ui_scaling_factor (settings);
if (settings->ui_scaling_factor != ui_scaling_factor)
{
settings->ui_scaling_factor = ui_scaling_factor;
return TRUE;
}
else
{
return FALSE;
}
}
void
meta_settings_update_ui_scaling_factor (MetaSettings *settings)
{
if (update_ui_scaling_factor (settings))
g_signal_emit (settings, signals[UI_SCALING_FACTOR_CHANGED], 0);
}
int
meta_settings_get_ui_scaling_factor (MetaSettings *settings)
{
g_assert (settings->ui_scaling_factor != 0);
return settings->ui_scaling_factor;
}
static gboolean
update_global_scaling_factor (MetaSettings *settings)
{
int global_scaling_factor;
global_scaling_factor =
(int) g_settings_get_uint (settings->interface_settings,
"scaling-factor");
if (settings->global_scaling_factor != global_scaling_factor)
{
settings->global_scaling_factor = global_scaling_factor;
return TRUE;
}
else
{
return FALSE;
}
}
gboolean
meta_settings_get_global_scaling_factor (MetaSettings *settings,
int *out_scaling_factor)
{
if (settings->global_scaling_factor == 0)
return FALSE;
*out_scaling_factor = settings->global_scaling_factor;
return TRUE;
}
static gboolean
update_font_dpi (MetaSettings *settings)
{
double text_scaling_factor;
/* Number of logical pixels on an inch when unscaled */
const double dots_per_inch = 96;
/* Being based on Xft, API users expect the DPI to be 1/1024th of an inch. */
const double xft_factor = 1024;
int font_dpi;
text_scaling_factor = g_settings_get_double (settings->interface_settings,
"text-scaling-factor");
font_dpi = (int) (text_scaling_factor *
dots_per_inch *
xft_factor *
settings->ui_scaling_factor);
if (font_dpi != settings->font_dpi)
{
settings->font_dpi = font_dpi;
g_object_set (clutter_settings_get_default (),
"font-dpi", font_dpi,
NULL);
return TRUE;
}
else
{
return FALSE;
}
}
static void
meta_settings_update_font_dpi (MetaSettings *settings)
{
if (update_font_dpi (settings))
g_signal_emit (settings, signals[FONT_DPI_CHANGED], 0);
}
int
meta_settings_get_font_dpi (MetaSettings *settings)
{
g_assert (settings->font_dpi != 0);
return settings->font_dpi;
}
static void
interface_settings_changed (GSettings *interface_settings,
const char *key,
MetaSettings *settings)
{
if (g_str_equal (key, "scaling-factor"))
{
if (update_global_scaling_factor (settings))
g_signal_emit (settings, signals[GLOBAL_SCALING_FACTOR_CHANGED], 0);
}
else if (g_str_equal (key, "text-scaling-factor"))
{
meta_settings_update_font_dpi (settings);
}
}
static void
privacy_settings_changed (GSettings *privacy_settings,
const char *key,
MetaSettings *settings)
{
if (g_str_equal (key, "privacy-screen"))
{
gboolean privacy_screen;
privacy_screen = g_settings_get_boolean (privacy_settings, key);
if (settings->privacy_screen != privacy_screen)
{
settings->privacy_screen = privacy_screen;
g_signal_emit (settings, signals[PRIVACY_SCREEN_CHANGED], 0);
}
}
}
gboolean
meta_settings_is_experimental_feature_enabled (MetaSettings *settings,
MetaExperimentalFeature feature)
{
return !!(settings->experimental_features & feature);
}
void
meta_settings_override_experimental_features (MetaSettings *settings)
{
settings->experimental_features = META_EXPERIMENTAL_FEATURE_NONE;
settings->experimental_features_overridden = TRUE;
}
void
meta_settings_enable_experimental_feature (MetaSettings *settings,
MetaExperimentalFeature feature)
{
g_assert (settings->experimental_features_overridden);
settings->experimental_features |= feature;
}
static gboolean
experimental_features_handler (GVariant *features_variant,
gpointer *result,
gpointer data)
{
MetaSettings *settings = data;
GVariantIter features_iter;
char *feature_str;
MetaExperimentalFeature features = META_EXPERIMENTAL_FEATURE_NONE;
if (settings->experimental_features_overridden)
{
*result = GINT_TO_POINTER (FALSE);
return TRUE;
}
g_variant_iter_init (&features_iter, features_variant);
while (g_variant_iter_loop (&features_iter, "s", &feature_str))
{
MetaExperimentalFeature feature = META_EXPERIMENTAL_FEATURE_NONE;
if (g_str_equal (feature_str, "scale-monitor-framebuffer"))
feature = META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER;
else if (g_str_equal (feature_str, "kms-modifiers"))
feature = META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS;
else if (g_str_equal (feature_str, "autoclose-xwayland"))
feature = META_EXPERIMENTAL_FEATURE_AUTOCLOSE_XWAYLAND;
else if (g_str_equal (feature_str, "variable-refresh-rate"))
feature = META_EXPERIMENTAL_FEATURE_VARIABLE_REFRESH_RATE;
if (feature)
g_message ("Enabling experimental feature '%s'", feature_str);
else
g_warning ("Unknown experimental feature '%s'", feature_str);
features |= feature;
}
if (features != settings->experimental_features)
{
settings->experimental_features = features;
*result = GINT_TO_POINTER (TRUE);
}
else
{
*result = GINT_TO_POINTER (FALSE);
}
return TRUE;
}
static gboolean
update_experimental_features (MetaSettings *settings)
{
return GPOINTER_TO_INT (g_settings_get_mapped (settings->mutter_settings,
"experimental-features",
experimental_features_handler,
settings));
}
static void
mutter_settings_changed (GSettings *mutter_settings,
gchar *key,
MetaSettings *settings)
{
MetaExperimentalFeature old_experimental_features;
if (!g_str_equal (key, "experimental-features"))
return;
old_experimental_features = settings->experimental_features;
if (update_experimental_features (settings))
g_signal_emit (settings, signals[EXPERIMENTAL_FEATURES_CHANGED], 0,
(unsigned int) old_experimental_features);
}
static void
xwayland_grab_list_add_item (MetaSettings *settings,
char *item)
{
/* If first character is '!', it's a denied value */
if (item[0] != '!')
g_ptr_array_add (settings->xwayland_grab_allow_list_patterns,
g_pattern_spec_new (item));
else if (item[1] != 0)
g_ptr_array_add (settings->xwayland_grab_deny_list_patterns,
g_pattern_spec_new (&item[1]));
}
static gboolean
xwayland_grab_access_rules_handler (GVariant *variant,
gpointer *result,
gpointer data)
{
MetaSettings *settings = data;
GVariantIter iter;
char *item;
/* Create a GPatternSpec for each element */
g_variant_iter_init (&iter, variant);
while (g_variant_iter_loop (&iter, "s", &item))
xwayland_grab_list_add_item (settings, item);
*result = GINT_TO_POINTER (TRUE);
return TRUE;
}
static void
update_xwayland_grab_access_rules (MetaSettings *settings)
{
gchar **system_defaults;
int i;
/* Free previous patterns and create new arrays */
g_clear_pointer (&settings->xwayland_grab_allow_list_patterns,
g_ptr_array_unref);
settings->xwayland_grab_allow_list_patterns =
g_ptr_array_new_with_free_func ((GDestroyNotify) g_pattern_spec_free);
g_clear_pointer (&settings->xwayland_grab_deny_list_patterns,
g_ptr_array_unref);
settings->xwayland_grab_deny_list_patterns =
g_ptr_array_new_with_free_func ((GDestroyNotify) g_pattern_spec_free);
/* Add system defaults values */
system_defaults = g_strsplit (XWAYLAND_GRAB_DEFAULT_ACCESS_RULES, ",", -1);
for (i = 0; system_defaults[i]; i++)
xwayland_grab_list_add_item (settings, system_defaults[i]);
g_strfreev (system_defaults);
/* Then add gsettings values */
g_settings_get_mapped (settings->wayland_settings,
"xwayland-grab-access-rules",
xwayland_grab_access_rules_handler,
settings);
}
static void
update_xwayland_allow_grabs (MetaSettings *settings)
{
settings->xwayland_allow_grabs =
g_settings_get_boolean (settings->wayland_settings,
"xwayland-allow-grabs");
}
static void
update_xwayland_disable_extensions (MetaSettings *settings)
{
settings->xwayland_disable_extensions =
g_settings_get_flags (settings->wayland_settings,
"xwayland-disable-extension");
}
static void
update_privacy_settings (MetaSettings *settings)
{
privacy_settings_changed (settings->privacy_settings,
"privacy-screen",
settings);
}
static void
update_xwayland_allow_byte_swapped_clients (MetaSettings *settings)
{
settings->xwayland_allow_byte_swapped_clients =
g_settings_get_boolean (settings->wayland_settings,
"xwayland-allow-byte-swapped-clients");
}
static void
wayland_settings_changed (GSettings *wayland_settings,
gchar *key,
MetaSettings *settings)
{
if (g_str_equal (key, "xwayland-allow-grabs"))
{
update_xwayland_allow_grabs (settings);
}
else if (g_str_equal (key, "xwayland-grab-access-rules"))
{
update_xwayland_grab_access_rules (settings);
}
else if (g_str_equal (key, "xwayland-disable-extension"))
{
update_xwayland_disable_extensions (settings);
}
else if (g_str_equal (key, "xwayland-allow-byte-swapped-clients"))
{
update_xwayland_allow_byte_swapped_clients (settings);
}
}
void
meta_settings_get_xwayland_grab_patterns (MetaSettings *settings,
GPtrArray **allow_list_patterns,
GPtrArray **deny_list_patterns)
{
*allow_list_patterns = settings->xwayland_grab_allow_list_patterns;
*deny_list_patterns = settings->xwayland_grab_deny_list_patterns;
}
gboolean
meta_settings_are_xwayland_grabs_allowed (MetaSettings *settings)
{
return (settings->xwayland_allow_grabs);
}
int
meta_settings_get_xwayland_disable_extensions (MetaSettings *settings)
{
return (settings->xwayland_disable_extensions);
}
gboolean
meta_settings_are_xwayland_byte_swapped_clients_allowed (MetaSettings *settings)
{
return settings->xwayland_allow_byte_swapped_clients;
}
gboolean
meta_settings_is_privacy_screen_enabled (MetaSettings *settings)
{
return settings->privacy_screen;
}
void
meta_settings_set_privacy_screen_enabled (MetaSettings *settings,
gboolean enabled)
{
if (settings->privacy_screen == enabled)
return;
settings->privacy_screen = enabled;
g_settings_set_boolean (settings->privacy_settings, "privacy-screen",
enabled);
}
MetaSettings *
meta_settings_new (MetaBackend *backend)
{
MetaSettings *settings;
settings = g_object_new (META_TYPE_SETTINGS, NULL);
settings->backend = backend;
return settings;
}
static void
meta_settings_dispose (GObject *object)
{
MetaSettings *settings = META_SETTINGS (object);
g_clear_object (&settings->mutter_settings);
g_clear_object (&settings->interface_settings);
g_clear_object (&settings->privacy_settings);
g_clear_object (&settings->wayland_settings);
g_clear_pointer (&settings->xwayland_grab_allow_list_patterns,
g_ptr_array_unref);
g_clear_pointer (&settings->xwayland_grab_deny_list_patterns,
g_ptr_array_unref);
G_OBJECT_CLASS (meta_settings_parent_class)->dispose (object);
}
static void
meta_settings_init (MetaSettings *settings)
{
settings->interface_settings = g_settings_new ("org.gnome.desktop.interface");
g_signal_connect (settings->interface_settings, "changed",
G_CALLBACK (interface_settings_changed),
settings);
settings->privacy_settings = g_settings_new ("org.gnome.desktop.privacy");
g_signal_connect (settings->privacy_settings, "changed",
G_CALLBACK (privacy_settings_changed),
settings);
settings->mutter_settings = g_settings_new ("org.gnome.mutter");
g_signal_connect (settings->mutter_settings, "changed",
G_CALLBACK (mutter_settings_changed),
settings);
settings->wayland_settings = g_settings_new ("org.gnome.mutter.wayland");
g_signal_connect (settings->wayland_settings, "changed",
G_CALLBACK (wayland_settings_changed),
settings);
/* Chain up inter-dependent settings. */
g_signal_connect (settings, "global-scaling-factor-changed",
G_CALLBACK (meta_settings_update_ui_scaling_factor), NULL);
g_signal_connect (settings, "ui-scaling-factor-changed",
G_CALLBACK (meta_settings_update_font_dpi), NULL);
update_global_scaling_factor (settings);
update_experimental_features (settings);
update_xwayland_grab_access_rules (settings);
update_xwayland_allow_grabs (settings);
update_xwayland_disable_extensions (settings);
update_privacy_settings (settings);
update_xwayland_allow_byte_swapped_clients (settings);
}
static void
on_monitors_changed (MetaMonitorManager *monitor_manager,
MetaSettings *settings)
{
meta_settings_update_ui_scaling_factor (settings);
}
void
meta_settings_post_init (MetaSettings *settings)
{
MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (settings->backend);
update_ui_scaling_factor (settings);
update_font_dpi (settings);
g_signal_connect_object (monitor_manager, "monitors-changed-internal",
G_CALLBACK (on_monitors_changed),
settings, G_CONNECT_AFTER);
}
static void
meta_settings_class_init (MetaSettingsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = meta_settings_dispose;
signals[UI_SCALING_FACTOR_CHANGED] =
g_signal_new ("ui-scaling-factor-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[GLOBAL_SCALING_FACTOR_CHANGED] =
g_signal_new ("global-scaling-factor-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[FONT_DPI_CHANGED] =
g_signal_new ("font-dpi-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
signals[EXPERIMENTAL_FEATURES_CHANGED] =
g_signal_new ("experimental-features-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_UINT);
signals[PRIVACY_SCREEN_CHANGED] =
g_signal_new ("privacy-screen-changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}