/* -*- 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-settings-private.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "ui/theme-private.h" enum { UI_SCALING_FACTOR_CHANGED, GLOBAL_SCALING_FACTOR_CHANGED, FONT_DPI_CHANGED, EXPERIMENTAL_FEATURES_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaSettings { GObject parent; MetaBackend *backend; GSettings *interface_settings; GSettings *mutter_settings; int ui_scaling_factor; int global_scaling_factor; int font_dpi; MetaExperimentalFeature experimental_features; gboolean experimental_features_overridden; }; 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); GList *logical_monitors; GList *l; float max_scale = 1.0; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; max_scale = MAX (meta_logical_monitor_get_scale (logical_monitor), max_scale); } g_warn_if_fail (fmodf (max_scale, 1.0) == 0.0); return (int) max_scale; } static int get_xsettings_scaling_factor (void) { GdkScreen *screen; GValue value = G_VALUE_INIT; g_value_init (&value, G_TYPE_INT); screen = gdk_screen_get_default (); if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value)) return g_value_get_int (&value); else return 1; } static gboolean update_ui_scaling_factor (MetaSettings *settings) { int ui_scaling_factor; if (meta_is_stage_views_scaled ()) { ui_scaling_factor = 1; } else { if (meta_is_monitor_config_manager_enabled ()) ui_scaling_factor = calculate_ui_scaling_factor (settings); else ui_scaling_factor = get_xsettings_scaling_factor (); } if (settings->ui_scaling_factor != ui_scaling_factor) { settings->ui_scaling_factor = ui_scaling_factor; return TRUE; } else { return FALSE; } } static void xft_dpi_changed (GtkSettings *gtk_settings, GParamSpec *pspec, MetaSettings *settings) { /* This only matters when we rely on XSettings. */ if (meta_is_monitor_config_manager_enabled ()) return; meta_settings_update_ui_scaling_factor (settings); } static void x11_display_opened (MetaBackend *backend, MetaSettings *settings) { /* * gdk-window-scaling-factor is not exported to gtk-settings * because it is handled inside gdk, so we use gtk-xft-dpi instead * which also changes when the scale factor changes. * * TODO: Only rely on our own configured scale when we only have * MetaMonitorConfigManager. */ g_signal_connect (gtk_settings_get_default (), "notify::gtk-xft-dpi", G_CALLBACK (xft_dpi_changed), settings); } 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); } } 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; 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)) { /* So far no experimental features defined. */ if (g_str_equal (feature, "scale-monitor-framebuffer")) features |= META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER; else if (g_str_equal (feature, "monitor-config-manager")) features |= META_EXPERIMENTAL_FEATURE_MONITOR_CONFIG_MANAGER; else g_info ("Unknown experimental feature '%s'\n", 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); } MetaSettings * meta_settings_new (MetaBackend *backend) { MetaSettings *settings; settings = g_object_new (META_TYPE_SETTINGS, NULL); settings->backend = backend; g_signal_connect (backend, "x11-display-opened", G_CALLBACK (x11_display_opened), settings); 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_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->mutter_settings = g_settings_new ("org.gnome.mutter"); g_signal_connect (settings->mutter_settings, "changed", G_CALLBACK (mutter_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); } void meta_settings_post_init (MetaSettings *settings) { update_ui_scaling_factor (settings); update_font_dpi (settings); } 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); }