summaryrefslogtreecommitdiffstats
path: root/plugins/xsettings/gsd-xsettings-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/xsettings/gsd-xsettings-manager.c')
-rw-r--r--plugins/xsettings/gsd-xsettings-manager.c1804
1 files changed, 1804 insertions, 0 deletions
diff --git a/plugins/xsettings/gsd-xsettings-manager.c b/plugins/xsettings/gsd-xsettings-manager.c
new file mode 100644
index 0000000..68b6ed7
--- /dev/null
+++ b/plugins/xsettings/gsd-xsettings-manager.c
@@ -0,0 +1,1804 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Rodrigo Moya
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#include <X11/Xatom.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gdesktop-enums.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gnome-settings-profile.h"
+#include "gsd-enums.h"
+#include "gsd-xsettings-manager.h"
+#include "gsd-xsettings-gtk.h"
+#include "gnome-settings-bus.h"
+#include "gsd-settings-migrate.h"
+#include "xsettings-manager.h"
+#include "fc-monitor.h"
+#include "gsd-remote-display-manager.h"
+#include "wm-button-layout-translation.h"
+
+#define MOUSE_SETTINGS_SCHEMA "org.gnome.desktop.peripherals.mouse"
+#define BACKGROUND_SETTINGS_SCHEMA "org.gnome.desktop.background"
+#define INTERFACE_SETTINGS_SCHEMA "org.gnome.desktop.interface"
+#define SOUND_SETTINGS_SCHEMA "org.gnome.desktop.sound"
+#define PRIVACY_SETTINGS_SCHEMA "org.gnome.desktop.privacy"
+#define WM_SETTINGS_SCHEMA "org.gnome.desktop.wm.preferences"
+#define A11Y_SCHEMA "org.gnome.desktop.a11y"
+#define A11Y_INTERFACE_SCHEMA "org.gnome.desktop.a11y.interface"
+#define A11Y_APPLICATIONS_SCHEMA "org.gnome.desktop.a11y.applications"
+#define INPUT_SOURCES_SCHEMA "org.gnome.desktop.input-sources"
+#define CLASSIC_WM_SETTINGS_SCHEMA "org.gnome.shell.extensions.classic-overrides"
+
+#define XSETTINGS_PLUGIN_SCHEMA "org.gnome.settings-daemon.plugins.xsettings"
+#define XSETTINGS_OVERRIDE_KEY "overrides"
+
+#define GTK_MODULES_DISABLED_KEY "disabled-gtk-modules"
+#define GTK_MODULES_ENABLED_KEY "enabled-gtk-modules"
+
+#define TEXT_SCALING_FACTOR_KEY "text-scaling-factor"
+#define CURSOR_SIZE_KEY "cursor-size"
+#define CURSOR_THEME_KEY "cursor-theme"
+
+#define FONT_ANTIALIASING_KEY "font-antialiasing"
+#define FONT_HINTING_KEY "font-hinting"
+#define FONT_RGBA_ORDER_KEY "font-rgba-order"
+
+#define HIGH_CONTRAST_KEY "high-contrast"
+
+#define INPUT_SOURCES_KEY "sources"
+#define OSK_ENABLED_KEY "screen-keyboard-enabled"
+#define GTK_IM_MODULE_KEY "gtk-im-module"
+
+#define GTK_SETTINGS_DBUS_PATH "/org/gtk/Settings"
+#define GTK_SETTINGS_DBUS_NAME "org.gtk.Settings"
+
+#define INPUT_SOURCE_TYPE_IBUS "ibus"
+
+#define GTK_IM_MODULE_SIMPLE "gtk-im-context-simple"
+#define GTK_IM_MODULE_IBUS "ibus"
+
+static const gchar introspection_xml[] =
+"<node name='/org/gtk/Settings'>"
+" <interface name='org.gtk.Settings'>"
+" <property name='FontconfigTimestamp' type='x' access='read'/>"
+" <property name='Modules' type='s' access='read'/>"
+" <property name='EnableAnimations' type='b' access='read'/>"
+" </interface>"
+"</node>";
+
+/* As we cannot rely on the X server giving us good DPI information, and
+ * that we don't want multi-monitor screens to have different DPIs (thus
+ * different text sizes), we'll hard-code the value of the DPI
+ *
+ * See also:
+ * https://bugzilla.novell.com/show_bug.cgi?id=217790•
+ * https://bugzilla.gnome.org/show_bug.cgi?id=643704
+ *
+ * http://lists.fedoraproject.org/pipermail/devel/2011-October/157671.html
+ * Why EDID is not trustworthy for DPI
+ * Adam Jackson ajax at redhat.com
+ * Tue Oct 4 17:54:57 UTC 2011
+ *
+ * Previous message: GNOME 3 - font point sizes now scaled?
+ * Next message: Why EDID is not trustworthy for DPI
+ * Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
+ *
+ * On Tue, 2011-10-04 at 11:46 -0400, Kaleb S. KEITHLEY wrote:
+ *
+ * > Grovelling around in the F15 xorg-server sources and reviewing the Xorg
+ * > log file on my F15 box, I see, with _modern hardware_ at least, that we
+ * > do have the monitor geometry available from DDC or EDIC, and obviously
+ * > it is trivial to compute the actual, correct DPI for each screen.
+ *
+ * I am clearly going to have to explain this one more time, forever.
+ * Let's see if I can't write it authoritatively once and simply answer
+ * with a URL from here out. (As always, use of the second person "you"
+ * herein is plural, not singular.)
+ *
+ * EDID does not reliably give you the size of the display.
+ *
+ * Base EDID has at least two different places where you can give a
+ * physical size (before considering extensions that aren't widely deployed
+ * so whatever). The first is a global property, measured in centimeters,
+ * of the physical size of the glass. The second is attached to your (zero
+ * or more) detailed timing specifications, and reflects the size of the
+ * mode, in millimeters.
+ *
+ * So, how does this screw you?
+ *
+ * a) Glass size is too coarse. On a large display that cm roundoff isn't
+ * a big deal, but on subnotebooks it's a different game. The 11" MBA is
+ * 25.68x14.44 cm, so that gives you a range of 52.54-54.64 dpcm horizontal
+ * and 51.20-54.86 dpcm vertical (133.4-138.8 dpi h and 130.0-139.3 dpi v).
+ * Which is optimistic, because that's doing the math forward from knowing
+ * the actual size, and you as the EDID parser can't know which way the
+ * manufacturer rounded.
+ *
+ * b) Glass size need not be non-zero. This is in fact the usual case for
+ * projectors, which don't have a fixed display size since it's a function
+ * of how far away the wall is from the lens.
+ *
+ * c) Glass size could be partially non-zero. Yes, really. EDID 1.4
+ * defines a method of using these two bytes to encode aspect ratio, where
+ * if vertical size is 0 then the aspect ratio is computed as (horizontal
+ * value + 99) / 100 in portrait mode (and the obvious reverse thing if
+ * horizontal is zero). Admittedly, unlike every other item in this list,
+ * I've never seen this in the wild. But it's legal.
+ *
+ * d) Glass size could be a direct encoding of the aspect ratio. Base EDID
+ * doesn't condone this behaviour, but the CEA spec (to which all HDMI
+ * monitors must conform) does allow-but-not-require it, which means your
+ * 1920x1080 TV could claim to be 16 "cm" by 9 "cm". So of course that's
+ * what TV manufacturers do because that way they don't have to modify the
+ * EDID info when physical construction changes, and that's cheaper.
+ *
+ * e) You could use mode size to get size in millimeters, but you might not
+ * have any detailed timings.
+ *
+ * f) You could use mode size, but mode size is explicitly _not_ glass
+ * size. It's the size that the display chooses to present that mode.
+ * Sometimes those are the same, and sometimes they're not. You could be
+ * scaled or {letter,pillar}boxed, and that's not necessarily something you
+ * can control from the host side.
+ *
+ * g) You could use mode size, but it could be an encoded aspect ratio, as
+ * in case d above, because CEA says that's okay.
+ *
+ * h) You could use mode size, but it could be the aspect ratio from case d
+ * multiplied by 10 in each direction (because, of course, you gave size in
+ * centimeters and so your authoring tool just multiplied it up).
+ *
+ * i) Any or all of the above could be complete and utter garbage, because
+ * - and I really, really need you to understand this - there is no
+ * requirements program for any commercial OS or industry standard that
+ * requires honesty here, as far as I'm aware. There is every incentive
+ * for there to _never_ be one, because it would make the manufacturing
+ * process more expensive.
+ *
+ * So from this point the suggestion is usually "well come up with some
+ * heuristic to make a good guess assuming there's some correlation between
+ * the various numbers you're given". I have in fact written heuristics
+ * for this, and they're in your kernel and your X server, and they still
+ * encounter a huge number of cases where we simply _cannot_ know from EDID
+ * anything like a physical size, because - to pick only one example - the
+ * consumer electronics industry are cheap bastards, because you the
+ * consumer demanded that they be cheap.
+ *
+ * And then your only recourse is to an external database, and now you're
+ * up the creek again because the identifying information here is a
+ * vendor/model/serial tuple, and the vendor can and does change physical
+ * construction without changing model number. Now you get to play the
+ * guessing game of how big the serial number range is for each subvariant,
+ * assuming they bothered to encode a serial number - and they didn't. Or,
+ * if they bothered to encode week/year of manufacturer correctly - and
+ * they didn't - which weeks meant which models. And then you still have
+ * to go out and buy one of every TV at Fry's, and that covers you for one
+ * market, for three months.
+ *
+ * If someone wants to write something better, please, by all means. If
+ * it's kernel code, send it to dri-devel at lists.freedesktop.org and cc me
+ * and I will happily review it. Likewise xorg-devel@ for X server
+ * changes.
+ *
+ * I gently suggest that doing so is a waste of time.
+ *
+ * But if there's one thing free software has taught me, it's that you can
+ * not tell people something is a bad idea and have any expectation they
+ * will believe you.
+ *
+ * > Obviously in a multi-screen set-up using Xinerama this has the potential
+ * > to be a Hard Problem if the monitors differ greatly in their DPI.
+ * >
+ * > If the major resistance is over what to do with older hardware that
+ * > doesn't have this data available, then yes, punt; use a hard-coded
+ * > default. Likewise, if the two monitors really differ greatly, then punt.
+ *
+ * I'm going to limit myself to observing that "greatly" is a matter of
+ * opinion, and that in order to be really useful you'd need some way of
+ * communicating "I punted" to the desktop.
+ *
+ * Beyond that, sure, pick a heuristic, accept that it's going to be
+ * insufficient for someone, and then sit back and wait to get
+ * second-guessed on it over and over.
+ *
+ * > And it wouldn't be so hard to to add something like -dpi:0, -dpi:1,
+ * > -dpi:2 command line options to specify per-screen dpi. I kinda thought I
+ * > did that a long, long time ago, but maybe I only thought about doing it
+ * > and never actually got around to it.
+ *
+ * The RANDR extension as of version 1.2 does allow you to override
+ * physical size on a per-output basis at runtime. We even try pretty hard
+ * to set them as honestly as we can up front. The 96dpi thing people
+ * complain about is from the per-screen info, which is simply a default
+ * because of all the tl;dr above; because you have N outputs per screen
+ * which means a single number is in general useless; and because there is
+ * no way to refresh the per-screen info at runtime, as it's only ever sent
+ * in the initial connection handshake.
+ *
+ * - ajax
+ *
+ */
+#define DPI_FALLBACK 96
+
+typedef struct _TranslationEntry TranslationEntry;
+typedef void (* TranslationFunc) (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value);
+
+struct _TranslationEntry {
+ const char *gsettings_schema;
+ const char *gsettings_key;
+ const char *xsetting_name;
+
+ TranslationFunc translate;
+};
+
+typedef struct _FixedEntry FixedEntry;
+typedef void (* FixedFunc) (GsdXSettingsManager *manager,
+ FixedEntry *fixed);
+typedef union {
+ const char *str;
+ int num;
+} FixedEntryValue;
+
+struct _FixedEntry {
+ const char *xsetting_name;
+ FixedFunc func;
+ FixedEntryValue val;
+};
+
+struct _GsdXSettingsManager
+{
+ GObject parent;
+
+ guint start_idle_id;
+ XSettingsManager *manager;
+ GHashTable *settings;
+
+ GSettings *plugin_settings;
+ FcMonitor *fontconfig_monitor;
+ gint64 fontconfig_timestamp;
+
+ GSettings *interface_settings;
+ GSettings *input_sources_settings;
+ GSettings *a11y_settings;
+ GdkSeat *user_seat;
+
+ GsdXSettingsGtk *gtk;
+
+ guint introspect_properties_changed_id;
+ guint shell_introspect_watch_id;
+ gboolean enable_animations;
+
+ guint display_config_watch_id;
+ guint monitors_changed_id;
+
+ guint device_added_id;
+ guint device_removed_id;
+
+ guint shell_name_watch_id;
+ gboolean have_shell;
+
+ guint notify_idle_id;
+
+ GDBusNodeInfo *introspection_data;
+ GDBusConnection *dbus_connection;
+ guint gtk_settings_name_id;
+};
+
+#define GSD_XSETTINGS_ERROR gsd_xsettings_error_quark ()
+
+enum {
+ GSD_XSETTINGS_ERROR_INIT
+};
+
+static void gsd_xsettings_manager_class_init (GsdXSettingsManagerClass *klass);
+static void gsd_xsettings_manager_init (GsdXSettingsManager *xsettings_manager);
+static void gsd_xsettings_manager_finalize (GObject *object);
+
+static void register_manager_dbus (GsdXSettingsManager *manager);
+
+G_DEFINE_TYPE (GsdXSettingsManager, gsd_xsettings_manager, G_TYPE_OBJECT)
+
+static gpointer manager_object = NULL;
+
+static GQuark
+gsd_xsettings_error_quark (void)
+{
+ return g_quark_from_static_string ("gsd-xsettings-error-quark");
+}
+
+static void
+translate_bool_int (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value)
+{
+ xsettings_manager_set_int (manager->manager, trans->xsetting_name,
+ g_variant_get_boolean (value));
+}
+
+static void
+translate_int_int (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value)
+{
+ xsettings_manager_set_int (manager->manager, trans->xsetting_name,
+ g_variant_get_int32 (value));
+}
+
+static void
+translate_string_string (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value)
+{
+ xsettings_manager_set_string (manager->manager,
+ trans->xsetting_name,
+ g_variant_get_string (value, NULL));
+}
+
+static void
+translate_button_layout (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value)
+{
+ GSettings *classic_settings;
+ GVariant *classic_value = NULL;
+ char *layout;
+
+ /* Hack: until we get session-dependent defaults in GSettings,
+ * swap out the usual schema for the "classic" one when
+ * running in classic mode
+ */
+ classic_settings = g_hash_table_lookup (manager->settings,
+ CLASSIC_WM_SETTINGS_SCHEMA);
+ if (classic_settings) {
+ classic_value = g_settings_get_value (classic_settings, "button-layout");
+ layout = g_variant_dup_string (classic_value, NULL);
+ } else {
+ layout = g_variant_dup_string (value, NULL);
+ }
+
+ translate_wm_button_layout_to_gtk (layout);
+
+ xsettings_manager_set_string (manager->manager,
+ trans->xsetting_name,
+ layout);
+
+ if (classic_value)
+ g_variant_unref (classic_value);
+ g_free (layout);
+}
+
+static void
+translate_theme_name (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value)
+{
+ GSettings *settings;
+ gboolean hc = FALSE;
+
+ settings = g_hash_table_lookup (manager->settings, A11Y_INTERFACE_SCHEMA);
+
+ if (settings)
+ hc = g_settings_get_boolean (settings, HIGH_CONTRAST_KEY);
+
+ xsettings_manager_set_string (manager->manager,
+ trans->xsetting_name,
+ hc ? "HighContrast"
+ : g_variant_get_string (value, NULL));
+}
+
+static void
+fixed_false_int (GsdXSettingsManager *manager,
+ FixedEntry *fixed)
+{
+ xsettings_manager_set_int (manager->manager, fixed->xsetting_name, FALSE);
+}
+
+static void
+fixed_true_int (GsdXSettingsManager *manager,
+ FixedEntry *fixed)
+{
+ xsettings_manager_set_int (manager->manager, fixed->xsetting_name, TRUE);
+}
+
+static void
+fixed_bus_id (GsdXSettingsManager *manager,
+ FixedEntry *fixed)
+{
+ const gchar *id;
+ GDBusConnection *bus;
+ GVariant *res;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ res = g_dbus_connection_call_sync (bus,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetId",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ NULL);
+
+ if (res) {
+ g_variant_get (res, "(&s)", &id);
+
+ xsettings_manager_set_string (manager->manager, fixed->xsetting_name, id);
+ g_variant_unref (res);
+ }
+
+ g_object_unref (bus);
+}
+
+static void
+fixed_string (GsdXSettingsManager *manager,
+ FixedEntry *fixed)
+{
+ xsettings_manager_set_string (manager->manager,
+ fixed->xsetting_name,
+ fixed->val.str);
+}
+
+static void
+fixed_int (GsdXSettingsManager *manager,
+ FixedEntry *fixed)
+{
+ xsettings_manager_set_int (manager->manager,
+ fixed->xsetting_name,
+ fixed->val.num);
+}
+
+#define DEFAULT_COLOR_PALETTE "black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90"
+
+static FixedEntry fixed_entries [] = {
+ { "Gtk/MenuImages", fixed_false_int },
+ { "Gtk/ButtonImages", fixed_false_int },
+ { "Gtk/ShowInputMethodMenu", fixed_false_int },
+ { "Gtk/ShowUnicodeMenu", fixed_false_int },
+ { "Gtk/AutoMnemonics", fixed_true_int },
+ { "Gtk/DialogsUseHeader", fixed_true_int },
+ { "Gtk/SessionBusId", fixed_bus_id },
+ { "Gtk/ShellShowsAppMenu", fixed_false_int },
+ { "Gtk/ColorPalette", fixed_string, { .str = DEFAULT_COLOR_PALETTE } },
+ { "Net/FallbackIconTheme", fixed_string, { .str = "gnome" } },
+ { "Gtk/ToolbarStyle", fixed_string, { .str = "both-horiz" } },
+ { "Gtk/ToolbarIconSize", fixed_string, { .str = "large" } },
+ { "Gtk/CanChangeAccels", fixed_false_int },
+ { "Gtk/TimeoutInitial", fixed_int, { .num = 200 } },
+ { "Gtk/TimeoutRepeat", fixed_int, { .num = 20 } },
+ { "Gtk/ColorScheme", fixed_string, { .str = "" } },
+ { "Gtk/IMPreeditStyle", fixed_string, { .str = "callback" } },
+ { "Gtk/IMStatusStyle", fixed_string, { .str = "callback" } },
+ { "Gtk/MenuBarAccel", fixed_string, { .str = "F10" } }
+};
+
+static TranslationEntry translations [] = {
+ { "org.gnome.desktop.peripherals.mouse", "double-click", "Net/DoubleClickTime", translate_int_int },
+ { "org.gnome.desktop.peripherals.mouse", "drag-threshold", "Net/DndDragThreshold", translate_int_int },
+
+ { "org.gnome.desktop.background", "show-desktop-icons", "Gtk/ShellShowsDesktop", translate_bool_int },
+
+ { "org.gnome.desktop.interface", "font-name", "Gtk/FontName", translate_string_string },
+ { "org.gnome.desktop.interface", "gtk-key-theme", "Gtk/KeyThemeName", translate_string_string },
+ { "org.gnome.desktop.interface", "cursor-blink", "Net/CursorBlink", translate_bool_int },
+ { "org.gnome.desktop.interface", "cursor-blink-time", "Net/CursorBlinkTime", translate_int_int },
+ { "org.gnome.desktop.interface", "cursor-blink-timeout", "Gtk/CursorBlinkTimeout", translate_int_int },
+ { "org.gnome.desktop.interface", "gtk-theme", "Net/ThemeName", translate_theme_name },
+ { "org.gnome.desktop.interface", "icon-theme", "Net/IconThemeName", translate_theme_name },
+ { "org.gnome.desktop.interface", "cursor-theme", "Gtk/CursorThemeName", translate_string_string },
+ { "org.gnome.desktop.interface", "gtk-enable-primary-paste", "Gtk/EnablePrimaryPaste", translate_bool_int },
+ { "org.gnome.desktop.interface", "overlay-scrolling", "Gtk/OverlayScrolling", translate_bool_int },
+ /* cursor-size is handled via the Xft side as it needs the scaling factor */
+
+ { "org.gnome.desktop.sound", "theme-name", "Net/SoundThemeName", translate_string_string },
+ { "org.gnome.desktop.sound", "event-sounds", "Net/EnableEventSounds" , translate_bool_int },
+ { "org.gnome.desktop.sound", "input-feedback-sounds", "Net/EnableInputFeedbackSounds", translate_bool_int },
+
+ { "org.gnome.desktop.privacy", "recent-files-max-age", "Gtk/RecentFilesMaxAge", translate_int_int },
+ { "org.gnome.desktop.privacy", "remember-recent-files", "Gtk/RecentFilesEnabled", translate_bool_int },
+ { "org.gnome.desktop.wm.preferences", "button-layout", "Gtk/DecorationLayout", translate_button_layout },
+ { "org.gnome.desktop.wm.preferences", "action-double-click-titlebar", "Gtk/TitlebarDoubleClick", translate_string_string },
+ { "org.gnome.desktop.wm.preferences", "action-middle-click-titlebar", "Gtk/TitlebarMiddleClick", translate_string_string },
+ { "org.gnome.desktop.wm.preferences", "action-right-click-titlebar", "Gtk/TitlebarRightClick", translate_string_string },
+ { "org.gnome.desktop.a11y", "always-show-text-caret", "Gtk/KeynavUseCaret", translate_bool_int }
+};
+
+static gboolean
+notify_idle (gpointer data)
+{
+ GsdXSettingsManager *manager = data;
+
+ xsettings_manager_notify (manager->manager);
+
+ manager->notify_idle_id = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static void
+queue_notify (GsdXSettingsManager *manager)
+{
+ if (manager->notify_idle_id != 0)
+ return;
+
+ manager->notify_idle_id = g_idle_add (notify_idle, manager);
+ g_source_set_name_by_id (manager->notify_idle_id, "[gnome-settings-daemon] notify_idle");
+}
+
+typedef enum {
+ GTK_SETTINGS_FONTCONFIG_TIMESTAMP = 1 << 0,
+ GTK_SETTINGS_MODULES = 1 << 1,
+ GTK_SETTINGS_ENABLE_ANIMATIONS = 1 << 2
+} GtkSettingsMask;
+
+static void
+send_dbus_event (GsdXSettingsManager *manager,
+ GtkSettingsMask mask)
+{
+ GVariantBuilder props_builder;
+ GVariant *props_changed = NULL;
+
+ g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (mask & GTK_SETTINGS_FONTCONFIG_TIMESTAMP) {
+ g_variant_builder_add (&props_builder, "{sv}", "FontconfigTimestamp",
+ g_variant_new_int64 (manager->fontconfig_timestamp));
+ }
+
+ if (mask & GTK_SETTINGS_MODULES) {
+ const char *modules = gsd_xsettings_gtk_get_modules (manager->gtk);
+ g_variant_builder_add (&props_builder, "{sv}", "Modules",
+ g_variant_new_string (modules ? modules : ""));
+ }
+
+ if (mask & GTK_SETTINGS_ENABLE_ANIMATIONS) {
+ g_variant_builder_add (&props_builder, "{sv}", "EnableAnimations",
+ g_variant_new_boolean (manager->enable_animations));
+ }
+
+ props_changed = g_variant_new ("(s@a{sv}@as)", GTK_SETTINGS_DBUS_NAME,
+ g_variant_builder_end (&props_builder),
+ g_variant_new_strv (NULL, 0));
+
+ g_dbus_connection_emit_signal (manager->dbus_connection,
+ NULL,
+ GTK_SETTINGS_DBUS_PATH,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ props_changed, NULL);
+}
+
+static double
+get_dpi_from_gsettings (GsdXSettingsManager *manager)
+{
+ GSettings *interface_settings;
+ double dpi;
+ double factor;
+
+ interface_settings = g_hash_table_lookup (manager->settings, INTERFACE_SETTINGS_SCHEMA);
+ factor = g_settings_get_double (interface_settings, TEXT_SCALING_FACTOR_KEY);
+
+ dpi = DPI_FALLBACK;
+
+ return dpi * factor;
+}
+
+static gboolean
+get_legacy_ui_scale (GVariantIter *properties,
+ int *scale)
+{
+ const char *key;
+ GVariant *value;
+
+ *scale = 0;
+
+ while (g_variant_iter_loop (properties, "{&sv}", &key, &value)) {
+ if (!g_str_equal (key, "legacy-ui-scaling-factor"))
+ continue;
+
+ *scale = g_variant_get_int32 (value);
+ break;
+ }
+
+ if (*scale < 1) {
+ g_warning ("Failed to get current UI legacy scaling factor");
+ *scale = 1;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#define MODE_FORMAT "(siiddada{sv})"
+#define MODES_FORMAT "a" MODE_FORMAT
+
+#define MONITOR_SPEC_FORMAT "(ssss)"
+#define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})"
+#define MONITORS_FORMAT "a" MONITOR_FORMAT
+
+#define LOGICAL_MONITOR_FORMAT "(iiduba" MONITOR_SPEC_FORMAT "a{sv})"
+#define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT
+
+#define CURRENT_STATE_FORMAT "(u" MONITORS_FORMAT LOGICAL_MONITORS_FORMAT "a{sv})"
+
+static int
+get_window_scale (GsdXSettingsManager *manager)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GVariant) current_state = NULL;
+ g_autoptr(GVariantIter) properties = NULL;
+ int scale = 1;
+
+ current_state =
+ g_dbus_connection_call_sync (manager->dbus_connection,
+ "org.gnome.Mutter.DisplayConfig",
+ "/org/gnome/Mutter/DisplayConfig",
+ "org.gnome.Mutter.DisplayConfig",
+ "GetCurrentState",
+ NULL,
+ NULL,
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+ if (!current_state) {
+ g_warning ("Failed to get current display configuration state: %s",
+ error->message);
+ return 1;
+ }
+
+ g_variant_get (current_state,
+ CURRENT_STATE_FORMAT,
+ NULL,
+ NULL,
+ NULL,
+ &properties);
+
+ if (!get_legacy_ui_scale (properties, &scale))
+ g_warning ("Failed to get current UI legacy scaling factor");
+
+ return scale;
+}
+
+typedef struct {
+ gboolean antialias;
+ gboolean hinting;
+ int scaled_dpi;
+ int dpi;
+ int window_scale;
+ int cursor_size;
+ char *cursor_theme;
+ const char *rgba;
+ const char *hintstyle;
+} GsdXftSettings;
+
+/* Read GSettings and determine the appropriate Xft settings based on them. */
+static void
+xft_settings_get (GsdXSettingsManager *manager,
+ GsdXftSettings *settings)
+{
+ GSettings *interface_settings;
+ GDesktopFontAntialiasingMode antialiasing;
+ GDesktopFontHinting hinting;
+ GDesktopFontRgbaOrder order;
+ gboolean use_rgba = FALSE;
+ double dpi;
+ int cursor_size;
+
+ interface_settings = g_hash_table_lookup (manager->settings, INTERFACE_SETTINGS_SCHEMA);
+
+ antialiasing = g_settings_get_enum (interface_settings, FONT_ANTIALIASING_KEY);
+ hinting = g_settings_get_enum (interface_settings, FONT_HINTING_KEY);
+ order = g_settings_get_enum (interface_settings, FONT_RGBA_ORDER_KEY);
+
+ settings->antialias = (antialiasing != G_DESKTOP_FONT_ANTIALIASING_MODE_NONE);
+ settings->hinting = (hinting != G_DESKTOP_FONT_HINTING_NONE);
+ settings->window_scale = get_window_scale (manager);
+ dpi = get_dpi_from_gsettings (manager);
+ settings->dpi = dpi * 1024; /* Xft wants 1/1024ths of an inch */
+ settings->scaled_dpi = dpi * settings->window_scale * 1024;
+ cursor_size = g_settings_get_int (interface_settings, CURSOR_SIZE_KEY);
+ settings->cursor_size = cursor_size * settings->window_scale;
+ settings->cursor_theme = g_settings_get_string (interface_settings, CURSOR_THEME_KEY);
+ settings->rgba = "rgb";
+ settings->hintstyle = "hintfull";
+
+ switch (hinting) {
+ case G_DESKTOP_FONT_HINTING_NONE:
+ settings->hintstyle = "hintnone";
+ break;
+ case G_DESKTOP_FONT_HINTING_SLIGHT:
+ settings->hintstyle = "hintslight";
+ break;
+ case G_DESKTOP_FONT_HINTING_MEDIUM:
+ settings->hintstyle = "hintmedium";
+ break;
+ case G_DESKTOP_FONT_HINTING_FULL:
+ settings->hintstyle = "hintfull";
+ break;
+ }
+
+ switch (order) {
+ case G_DESKTOP_FONT_RGBA_ORDER_RGBA:
+ settings->rgba = "rgba";
+ break;
+ case G_DESKTOP_FONT_RGBA_ORDER_RGB:
+ settings->rgba = "rgb";
+ break;
+ case G_DESKTOP_FONT_RGBA_ORDER_BGR:
+ settings->rgba = "bgr";
+ break;
+ case G_DESKTOP_FONT_RGBA_ORDER_VRGB:
+ settings->rgba = "vrgb";
+ break;
+ case G_DESKTOP_FONT_RGBA_ORDER_VBGR:
+ settings->rgba = "vbgr";
+ break;
+ }
+
+ switch (antialiasing) {
+ case G_DESKTOP_FONT_ANTIALIASING_MODE_NONE:
+ settings->antialias = 0;
+ break;
+ case G_DESKTOP_FONT_ANTIALIASING_MODE_GRAYSCALE:
+ settings->antialias = 1;
+ break;
+ case G_DESKTOP_FONT_ANTIALIASING_MODE_RGBA:
+ settings->antialias = 1;
+ use_rgba = TRUE;
+ }
+
+ if (!use_rgba) {
+ settings->rgba = "none";
+ }
+}
+
+static void
+xft_settings_clear (GsdXftSettings *settings)
+{
+ g_free (settings->cursor_theme);
+}
+
+static void
+xft_settings_set_xsettings (GsdXSettingsManager *manager,
+ GsdXftSettings *settings)
+{
+ gnome_settings_profile_start (NULL);
+
+ xsettings_manager_set_int (manager->manager, "Xft/Antialias", settings->antialias);
+ xsettings_manager_set_int (manager->manager, "Xft/Hinting", settings->hinting);
+ xsettings_manager_set_string (manager->manager, "Xft/HintStyle", settings->hintstyle);
+ xsettings_manager_set_int (manager->manager, "Gdk/WindowScalingFactor", settings->window_scale);
+ xsettings_manager_set_int (manager->manager, "Gdk/UnscaledDPI", settings->dpi);
+ xsettings_manager_set_int (manager->manager, "Xft/DPI", settings->scaled_dpi);
+ xsettings_manager_set_string (manager->manager, "Xft/RGBA", settings->rgba);
+ xsettings_manager_set_int (manager->manager, "Gtk/CursorThemeSize", settings->cursor_size);
+ xsettings_manager_set_string (manager->manager, "Gtk/CursorThemeName", settings->cursor_theme);
+
+ gnome_settings_profile_end (NULL);
+}
+
+static void
+update_property (GString *props, const gchar* key, const gchar* value)
+{
+ gchar* needle;
+ size_t needle_len;
+ gchar* found = NULL;
+
+ /* update an existing property */
+ needle = g_strconcat (key, ":", NULL);
+ needle_len = strlen (needle);
+ if (g_str_has_prefix (props->str, needle))
+ found = props->str;
+ else
+ found = strstr (props->str, needle);
+
+ if (found) {
+ size_t value_index;
+ gchar* end;
+
+ end = strchr (found, '\n');
+ value_index = (found - props->str) + needle_len + 1;
+ g_string_erase (props, value_index, end ? (end - found - needle_len) : -1);
+ g_string_insert (props, value_index, "\n");
+ g_string_insert (props, value_index, value);
+ } else {
+ g_string_append_printf (props, "%s:\t%s\n", key, value);
+ }
+
+ g_free (needle);
+}
+
+static void
+xft_settings_set_xresources (GsdXftSettings *settings)
+{
+ GString *add_string;
+ char dpibuf[G_ASCII_DTOSTR_BUF_SIZE];
+ Display *dpy;
+
+ gnome_settings_profile_start (NULL);
+
+ /* get existing properties */
+ dpy = XOpenDisplay (NULL);
+ g_return_if_fail (dpy != NULL);
+ add_string = g_string_new (XResourceManagerString (dpy));
+
+ g_debug("xft_settings_set_xresources: orig res '%s'", add_string->str);
+
+ g_snprintf (dpibuf, sizeof (dpibuf), "%d", (int) (settings->scaled_dpi / 1024.0 + 0.5));
+ update_property (add_string, "Xft.dpi", dpibuf);
+ update_property (add_string, "Xft.antialias",
+ settings->antialias ? "1" : "0");
+ update_property (add_string, "Xft.hinting",
+ settings->hinting ? "1" : "0");
+ update_property (add_string, "Xft.hintstyle",
+ settings->hintstyle);
+ update_property (add_string, "Xft.rgba",
+ settings->rgba);
+ update_property (add_string, "Xcursor.size",
+ g_ascii_dtostr (dpibuf, sizeof (dpibuf), (double) settings->cursor_size));
+ update_property (add_string, "Xcursor.theme",
+ settings->cursor_theme);
+
+ g_debug("xft_settings_set_xresources: new res '%s'", add_string->str);
+
+ /* Set the new X property */
+ XChangeProperty(dpy, RootWindow (dpy, 0),
+ XA_RESOURCE_MANAGER, XA_STRING, 8, PropModeReplace, (const unsigned char *) add_string->str, add_string->len);
+ XCloseDisplay (dpy);
+
+ g_string_free (add_string, TRUE);
+
+ gnome_settings_profile_end (NULL);
+}
+
+/* We mirror the Xft properties both through XSETTINGS and through
+ * X resources
+ */
+static void
+update_xft_settings (GsdXSettingsManager *manager)
+{
+ GsdXftSettings settings;
+
+ gnome_settings_profile_start (NULL);
+
+ xft_settings_get (manager, &settings);
+ xft_settings_set_xsettings (manager, &settings);
+ xft_settings_set_xresources (&settings);
+ xft_settings_clear (&settings);
+
+ gnome_settings_profile_end (NULL);
+}
+
+static void
+xft_callback (GSettings *settings,
+ const gchar *key,
+ GsdXSettingsManager *manager)
+{
+ update_xft_settings (manager);
+ queue_notify (manager);
+}
+
+static void
+override_callback (GSettings *settings,
+ const gchar *key,
+ GsdXSettingsManager *manager)
+{
+ GVariant *value;
+
+ value = g_settings_get_value (settings, XSETTINGS_OVERRIDE_KEY);
+
+ xsettings_manager_set_overrides (manager->manager, value);
+ queue_notify (manager);
+
+ g_variant_unref (value);
+}
+
+static void
+plugin_callback (GSettings *settings,
+ const char *key,
+ GsdXSettingsManager *manager)
+{
+ if (g_str_equal (key, GTK_MODULES_DISABLED_KEY) ||
+ g_str_equal (key, GTK_MODULES_ENABLED_KEY)) {
+ /* Do nothing, as GsdXsettingsGtk will handle it */
+ } else if (g_str_equal (key, XSETTINGS_OVERRIDE_KEY)) {
+ override_callback (settings, key, manager);
+ }
+}
+
+static void
+gtk_modules_callback (GsdXSettingsGtk *gtk,
+ GParamSpec *spec,
+ GsdXSettingsManager *manager)
+{
+ const char *modules = gsd_xsettings_gtk_get_modules (manager->gtk);
+
+ if (modules == NULL) {
+ xsettings_manager_delete_setting (manager->manager, "Gtk/Modules");
+ } else {
+ g_debug ("Setting GTK modules '%s'", modules);
+ xsettings_manager_set_string (manager->manager,
+ "Gtk/Modules",
+ modules);
+ }
+
+ queue_notify (manager);
+ send_dbus_event (manager, GTK_SETTINGS_MODULES);
+}
+
+static void
+fontconfig_callback (FcMonitor *monitor,
+ GsdXSettingsManager *manager)
+{
+ gint64 timestamp = g_get_real_time ();
+ gint timestamp_sec = (int)(timestamp / G_TIME_SPAN_SECOND);
+
+ gnome_settings_profile_start (NULL);
+
+ xsettings_manager_set_int (manager->manager, "Fontconfig/Timestamp", timestamp_sec);
+
+ manager->fontconfig_timestamp = timestamp;
+
+ queue_notify (manager);
+ send_dbus_event (manager, GTK_SETTINGS_FONTCONFIG_TIMESTAMP);
+ gnome_settings_profile_end (NULL);
+}
+
+static gboolean
+start_fontconfig_monitor_idle_cb (GsdXSettingsManager *manager)
+{
+ gnome_settings_profile_start (NULL);
+
+ fc_monitor_start (manager->fontconfig_monitor);
+
+ gnome_settings_profile_end (NULL);
+
+ manager->start_idle_id = 0;
+
+ return FALSE;
+}
+
+static void
+start_fontconfig_monitor (GsdXSettingsManager *manager)
+{
+ gnome_settings_profile_start (NULL);
+
+ manager->fontconfig_monitor = fc_monitor_new ();
+ g_signal_connect (manager->fontconfig_monitor, "updated", G_CALLBACK (fontconfig_callback), manager);
+
+ manager->start_idle_id = g_idle_add ((GSourceFunc) start_fontconfig_monitor_idle_cb, manager);
+ g_source_set_name_by_id (manager->start_idle_id, "[gnome-settings-daemon] start_fontconfig_monitor_idle_cb");
+
+ gnome_settings_profile_end (NULL);
+}
+
+static void
+process_value (GsdXSettingsManager *manager,
+ TranslationEntry *trans,
+ GVariant *value)
+{
+ (* trans->translate) (manager, trans, value);
+}
+
+static TranslationEntry *
+find_translation_entry (GSettings *settings, const char *key)
+{
+ guint i;
+ char *schema;
+
+ g_object_get (settings, "schema-id", &schema, NULL);
+
+ if (g_str_equal (schema, CLASSIC_WM_SETTINGS_SCHEMA)) {
+ g_free (schema);
+ schema = g_strdup (WM_SETTINGS_SCHEMA);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (translations); i++) {
+ if (g_str_equal (schema, translations[i].gsettings_schema) &&
+ g_str_equal (key, translations[i].gsettings_key)) {
+ g_free (schema);
+ return &translations[i];
+ }
+ }
+
+ g_free (schema);
+
+ return NULL;
+}
+
+static void
+xsettings_callback (GSettings *settings,
+ const char *key,
+ GsdXSettingsManager *manager)
+{
+ TranslationEntry *trans;
+ GVariant *value;
+
+ if (g_str_equal (key, TEXT_SCALING_FACTOR_KEY) ||
+ g_str_equal (key, FONT_ANTIALIASING_KEY) ||
+ g_str_equal (key, FONT_HINTING_KEY) ||
+ g_str_equal (key, FONT_RGBA_ORDER_KEY) ||
+ g_str_equal (key, CURSOR_SIZE_KEY) ||
+ g_str_equal (key, CURSOR_THEME_KEY)) {
+ xft_callback (NULL, key, manager);
+ return;
+ }
+
+ if (g_str_equal (key, HIGH_CONTRAST_KEY)) {
+ GSettings *iface_settings;
+
+ iface_settings = g_hash_table_lookup (manager->settings,
+ INTERFACE_SETTINGS_SCHEMA);
+ xsettings_callback (iface_settings, "gtk-theme", manager);
+ xsettings_callback (iface_settings, "icon-theme", manager);
+ return;
+ }
+
+ trans = find_translation_entry (settings, key);
+ if (trans == NULL) {
+ return;
+ }
+
+ value = g_settings_get_value (settings, key);
+
+ process_value (manager, trans, value);
+
+ g_variant_unref (value);
+
+ queue_notify (manager);
+}
+
+static void
+terminate_cb (void *data)
+{
+ gboolean *terminated = data;
+
+ if (*terminated) {
+ return;
+ }
+
+ *terminated = TRUE;
+ g_warning ("X Settings Manager is terminating");
+ gtk_main_quit ();
+}
+
+static gboolean
+setup_xsettings_managers (GsdXSettingsManager *manager)
+{
+ GdkDisplay *display;
+ gboolean res;
+ gboolean terminated;
+
+ display = gdk_display_get_default ();
+
+ res = xsettings_manager_check_running (gdk_x11_display_get_xdisplay (display),
+ gdk_x11_screen_get_screen_number (gdk_screen_get_default ()));
+
+ if (res) {
+ g_warning ("You can only run one xsettings manager at a time; exiting");
+ return FALSE;
+ }
+
+ terminated = FALSE;
+ manager->manager = xsettings_manager_new (gdk_x11_display_get_xdisplay (display),
+ gdk_x11_screen_get_screen_number (gdk_screen_get_default ()),
+ terminate_cb,
+ &terminated);
+ if (! manager->manager) {
+ g_warning ("Could not create xsettings manager!");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+monitors_changed (GsdXSettingsManager *manager)
+{
+ update_xft_settings (manager);
+ queue_notify (manager);
+}
+
+static void
+on_monitors_changed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer data)
+{
+ GsdXSettingsManager *manager = data;
+ monitors_changed (manager);
+}
+
+static void
+on_display_config_name_appeared_handler (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer data)
+{
+ GsdXSettingsManager *manager = data;
+ monitors_changed (manager);
+}
+
+static void
+animations_enabled_changed (GsdXSettingsManager *manager)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GVariant) res = NULL;
+ g_autoptr(GVariant) animations_enabled_variant = NULL;
+ gboolean animations_enabled;
+
+ res = g_dbus_connection_call_sync (manager->dbus_connection,
+ "org.gnome.Shell.Introspect",
+ "/org/gnome/Shell/Introspect",
+ "org.freedesktop.DBus.Properties",
+ "Get",
+ g_variant_new ("(ss)",
+ "org.gnome.Shell.Introspect",
+ "AnimationsEnabled"),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (!res) {
+ g_warning ("Failed to get animations-enabled state: %s",
+ error->message);
+ return;
+ }
+
+ g_variant_get (res, "(v)", &animations_enabled_variant);
+ g_variant_get (animations_enabled_variant, "b", &animations_enabled);
+
+ if (manager->enable_animations == animations_enabled)
+ return;
+
+ manager->enable_animations = animations_enabled;
+ xsettings_manager_set_int (manager->manager, "Gtk/EnableAnimations",
+ animations_enabled);
+ queue_notify (manager);
+ send_dbus_event (manager, GTK_SETTINGS_ENABLE_ANIMATIONS);
+}
+
+static void
+on_introspect_properties_changed (GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer data)
+{
+ GsdXSettingsManager *manager = data;
+ animations_enabled_changed (manager);
+}
+
+static void
+on_shell_introspect_name_appeared_handler (GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer data)
+{
+ GsdXSettingsManager *manager = data;
+ animations_enabled_changed (manager);
+}
+
+static void
+launch_xwayland_services_on_dir (const gchar *path)
+{
+ GFileEnumerator *enumerator;
+ GError *error = NULL;
+ GList *l, *scripts = NULL;
+ GFile *dir;
+
+ g_debug ("launch_xwayland_services_on_dir: %s", path);
+
+ dir = g_file_new_for_path (path);
+ enumerator = g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, &error);
+ g_object_unref (dir);
+
+ if (!enumerator) {
+ if (!g_error_matches (error,
+ G_IO_ERROR,
+ G_IO_ERROR_NOT_FOUND)) {
+ g_warning ("Error opening '%s': %s",
+ path, error->message);
+ }
+
+ g_error_free (error);
+ return;
+ }
+
+ while (TRUE) {
+ GFileInfo *info;
+ GFile *child;
+
+ if (!g_file_enumerator_iterate (enumerator,
+ &info, &child,
+ NULL, &error)) {
+ g_warning ("Error iterating on '%s': %s",
+ path, error->message);
+ g_error_free (error);
+ break;
+ }
+
+ if (!info)
+ break;
+
+ if (g_file_info_get_file_type (info) != G_FILE_TYPE_REGULAR ||
+ !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
+ continue;
+
+ scripts = g_list_prepend (scripts, g_file_get_path (child));
+ }
+
+ scripts = g_list_sort (scripts, (GCompareFunc) strcmp);
+
+ for (l = scripts; l; l = l->next) {
+ gchar *args[2] = { l->data, NULL };
+
+ g_debug ("launch_xwayland_services_on_dir: Spawning '%s'", args[0]);
+ if (!g_spawn_sync (NULL, args, NULL,
+ G_SPAWN_DEFAULT,
+ NULL, NULL,
+ NULL, NULL, NULL,
+ &error)) {
+ g_warning ("Error when spawning '%s': %s",
+ args[0], error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ g_object_unref (enumerator);
+ g_list_free_full (scripts, g_free);
+}
+
+static void
+launch_xwayland_services (void)
+{
+ const gchar * const * config_dirs;
+ gint i;
+
+ config_dirs = g_get_system_config_dirs ();
+
+ for (i = 0; config_dirs[i] != NULL; i++) {
+ gchar *config_dir;
+
+ config_dir = g_build_filename (config_dirs[i],
+ "Xwayland-session.d",
+ NULL);
+
+ launch_xwayland_services_on_dir (config_dir);
+ g_free (config_dir);
+ }
+}
+
+static void
+migrate_settings (void)
+{
+ GsdSettingsMigrateEntry xsettings_entries[] = {
+ { "antialiasing", "font-antialiasing", NULL },
+ { "hinting", "font-hinting", NULL },
+ { "rgba-order", "font-rgba-order", NULL },
+ };
+ GsdSettingsMigrateEntry mouse_entries[] = {
+ { "double-click", "double-click", NULL },
+ { "drag-threshold", "drag-threshold", NULL },
+ };
+
+ gsd_settings_migrate_check ("org.gnome.settings-daemon.plugins.xsettings.deprecated",
+ "/org/gnome/settings-daemon/plugins/xsettings/",
+ "org.gnome.desktop.interface",
+ "/org/gnome/desktop/interface/",
+ xsettings_entries, G_N_ELEMENTS (xsettings_entries));
+
+ gsd_settings_migrate_check ("org.gnome.settings-daemon.peripherals.mouse.deprecated",
+ "/org/gnome/settings-daemon/peripherals/mouse/",
+ "org.gnome.desktop.peripherals.mouse",
+ "/org/gnome/desktop/peripherals/mouse/",
+ mouse_entries, G_N_ELEMENTS (mouse_entries));
+}
+
+static gboolean
+need_ibus (GsdXSettingsManager *manager)
+{
+ GVariant *sources;
+ GVariantIter iter;
+ const gchar *type;
+ gboolean needs_ibus = FALSE;
+
+ sources = g_settings_get_value (manager->input_sources_settings,
+ INPUT_SOURCES_KEY);
+
+ g_variant_iter_init (&iter, sources);
+ while (g_variant_iter_next (&iter, "(&s&s)", &type, NULL)) {
+ if (g_str_equal (type, INPUT_SOURCE_TYPE_IBUS)) {
+ needs_ibus = TRUE;
+ break;
+ }
+ }
+
+ g_variant_unref (sources);
+
+ return needs_ibus;
+}
+
+static gboolean
+need_osk (GsdXSettingsManager *manager)
+{
+ gboolean has_touchscreen = FALSE;
+ GList *devices;
+ GdkSeat *seat;
+
+ if (g_settings_get_boolean (manager->a11y_settings,
+ OSK_ENABLED_KEY))
+ return TRUE;
+
+ seat = gdk_display_get_default_seat (gdk_display_get_default ());
+ devices = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_TOUCH);
+
+ has_touchscreen = devices != NULL;
+
+ g_list_free (devices);
+
+ return has_touchscreen;
+}
+
+static void
+update_gtk_im_module (GsdXSettingsManager *manager)
+{
+ const gchar *module;
+ gchar *setting;
+
+ setting = g_settings_get_string (manager->interface_settings,
+ GTK_IM_MODULE_KEY);
+ if (setting && *setting)
+ module = setting;
+ else if (need_ibus (manager) || need_osk (manager))
+ module = GTK_IM_MODULE_IBUS;
+ else
+ module = GTK_IM_MODULE_SIMPLE;
+
+ xsettings_manager_set_string (manager->manager, "Gtk/IMModule", module);
+ g_free (setting);
+}
+
+static void
+device_added_cb (GdkSeat *user_seat,
+ GdkDevice *device,
+ GsdXSettingsManager *manager)
+{
+ GdkInputSource source;
+
+ source = gdk_device_get_source (device);
+ if (source == GDK_SOURCE_TOUCHSCREEN) {
+ update_gtk_im_module (manager);
+ }
+}
+
+static void
+device_removed_cb (GdkSeat *user_seat,
+ GdkDevice *device,
+ GsdXSettingsManager *manager)
+{
+ GdkInputSource source;
+
+ source = gdk_device_get_source (device);
+ if (source == GDK_SOURCE_TOUCHSCREEN)
+ update_gtk_im_module (manager);
+}
+
+static void
+set_devicepresence_handler (GsdXSettingsManager *manager)
+{
+ GdkSeat *user_seat;
+
+ if (gnome_settings_is_wayland ())
+ return;
+
+ user_seat = gdk_display_get_default_seat (gdk_display_get_default ());
+
+ manager->device_added_id = g_signal_connect (G_OBJECT (user_seat), "device-added",
+ G_CALLBACK (device_added_cb), manager);
+ manager->device_removed_id = g_signal_connect (G_OBJECT (user_seat), "device-removed",
+ G_CALLBACK (device_removed_cb), manager);
+ manager->user_seat = user_seat;
+}
+
+gboolean
+gsd_xsettings_manager_start (GsdXSettingsManager *manager,
+ GError **error)
+{
+ GVariant *overrides;
+ guint i;
+ GList *list, *l;
+ const char *session;
+
+ g_debug ("Starting xsettings manager");
+ gnome_settings_profile_start (NULL);
+
+ migrate_settings ();
+
+ if (!setup_xsettings_managers (manager)) {
+ g_set_error (error, GSD_XSETTINGS_ERROR,
+ GSD_XSETTINGS_ERROR_INIT,
+ "Could not initialize xsettings manager.");
+ return FALSE;
+ }
+
+ set_devicepresence_handler (manager);
+ manager->interface_settings = g_settings_new (INTERFACE_SETTINGS_SCHEMA);
+ g_signal_connect_swapped (manager->interface_settings,
+ "changed::" GTK_IM_MODULE_KEY,
+ G_CALLBACK (update_gtk_im_module), manager);
+
+ manager->input_sources_settings = g_settings_new (INPUT_SOURCES_SCHEMA);
+ g_signal_connect_swapped (manager->input_sources_settings,
+ "changed::" INPUT_SOURCES_KEY,
+ G_CALLBACK (update_gtk_im_module), manager);
+
+ manager->a11y_settings = g_settings_new (A11Y_APPLICATIONS_SCHEMA);
+ g_signal_connect_swapped (manager->a11y_settings,
+ "changed::" OSK_ENABLED_KEY,
+ G_CALLBACK (update_gtk_im_module), manager);
+ update_gtk_im_module (manager);
+
+ manager->monitors_changed_id =
+ g_dbus_connection_signal_subscribe (manager->dbus_connection,
+ "org.gnome.Mutter.DisplayConfig",
+ "org.gnome.Mutter.DisplayConfig",
+ "MonitorsChanged",
+ "/org/gnome/Mutter/DisplayConfig",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_monitors_changed,
+ manager,
+ NULL);
+ manager->display_config_watch_id =
+ g_bus_watch_name_on_connection (manager->dbus_connection,
+ "org.gnome.Mutter.DisplayConfig",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_display_config_name_appeared_handler,
+ NULL,
+ manager,
+ NULL);
+
+ manager->introspect_properties_changed_id =
+ g_dbus_connection_signal_subscribe (manager->dbus_connection,
+ "org.gnome.Shell.Introspect",
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ "/org/gnome/Shell/Introspect",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ on_introspect_properties_changed,
+ manager,
+ NULL);
+ manager->shell_introspect_watch_id =
+ g_bus_watch_name_on_connection (manager->dbus_connection,
+ "org.gnome.Shell.Introspect",
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ on_shell_introspect_name_appeared_handler,
+ NULL,
+ manager,
+ NULL);
+
+ manager->settings = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify) g_object_unref);
+
+ g_hash_table_insert (manager->settings,
+ MOUSE_SETTINGS_SCHEMA, g_settings_new (MOUSE_SETTINGS_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ BACKGROUND_SETTINGS_SCHEMA, g_settings_new (BACKGROUND_SETTINGS_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ INTERFACE_SETTINGS_SCHEMA, g_settings_new (INTERFACE_SETTINGS_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ SOUND_SETTINGS_SCHEMA, g_settings_new (SOUND_SETTINGS_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ PRIVACY_SETTINGS_SCHEMA, g_settings_new (PRIVACY_SETTINGS_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ WM_SETTINGS_SCHEMA, g_settings_new (WM_SETTINGS_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ A11Y_SCHEMA, g_settings_new (A11Y_SCHEMA));
+ g_hash_table_insert (manager->settings,
+ A11Y_INTERFACE_SCHEMA, g_settings_new (A11Y_INTERFACE_SCHEMA));
+
+ session = g_getenv ("XDG_CURRENT_DESKTOP");
+ if (session && strstr (session, "GNOME-Classic")) {
+ GSettingsSchema *schema;
+
+ schema = g_settings_schema_source_lookup (g_settings_schema_source_get_default (),
+ CLASSIC_WM_SETTINGS_SCHEMA, FALSE);
+ if (schema) {
+ g_hash_table_insert (manager->settings,
+ CLASSIC_WM_SETTINGS_SCHEMA,
+ g_settings_new_full (schema, NULL, NULL));
+ g_settings_schema_unref (schema);
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (fixed_entries); i++) {
+ FixedEntry *fixed = &fixed_entries[i];
+ (* fixed->func) (manager, fixed);
+ }
+
+ list = g_hash_table_get_values (manager->settings);
+ for (l = list; l != NULL; l = l->next) {
+ g_signal_connect_object (G_OBJECT (l->data), "changed", G_CALLBACK (xsettings_callback), manager, 0);
+ }
+ g_list_free (list);
+
+ for (i = 0; i < G_N_ELEMENTS (translations); i++) {
+ GVariant *val;
+ GSettings *settings;
+
+ settings = g_hash_table_lookup (manager->settings,
+ translations[i].gsettings_schema);
+ if (settings == NULL) {
+ g_warning ("Schemas '%s' has not been setup", translations[i].gsettings_schema);
+ continue;
+ }
+
+ val = g_settings_get_value (settings, translations[i].gsettings_key);
+
+ process_value (manager, &translations[i], val);
+ g_variant_unref (val);
+ }
+
+ /* Plugin settings (GTK modules and Xft) */
+ manager->plugin_settings = g_settings_new (XSETTINGS_PLUGIN_SCHEMA);
+ g_signal_connect_object (manager->plugin_settings, "changed", G_CALLBACK (plugin_callback), manager, 0);
+
+ manager->gtk = gsd_xsettings_gtk_new ();
+ g_signal_connect (G_OBJECT (manager->gtk), "notify::gtk-modules",
+ G_CALLBACK (gtk_modules_callback), manager);
+ gtk_modules_callback (manager->gtk, NULL, manager);
+
+ /* Xft settings */
+ update_xft_settings (manager);
+
+ /* Launch Xwayland services */
+ if (gnome_settings_is_wayland ())
+ launch_xwayland_services ();
+
+ register_manager_dbus (manager);
+
+ start_fontconfig_monitor (manager);
+
+ overrides = g_settings_get_value (manager->plugin_settings, XSETTINGS_OVERRIDE_KEY);
+ xsettings_manager_set_overrides (manager->manager, overrides);
+ queue_notify (manager);
+ g_variant_unref (overrides);
+
+
+ gnome_settings_profile_end (NULL);
+
+ return TRUE;
+}
+
+void
+gsd_xsettings_manager_stop (GsdXSettingsManager *manager)
+{
+ g_debug ("Stopping xsettings manager");
+
+ if (manager->introspect_properties_changed_id) {
+ g_dbus_connection_signal_unsubscribe (manager->dbus_connection,
+ manager->introspect_properties_changed_id);
+ manager->introspect_properties_changed_id = 0;
+ }
+
+ if (manager->shell_introspect_watch_id) {
+ g_bus_unwatch_name (manager->shell_introspect_watch_id);
+ manager->shell_introspect_watch_id = 0;
+ }
+
+ if (manager->monitors_changed_id) {
+ g_dbus_connection_signal_unsubscribe (manager->dbus_connection,
+ manager->monitors_changed_id);
+ manager->monitors_changed_id = 0;
+ }
+
+ if (manager->display_config_watch_id) {
+ g_bus_unwatch_name (manager->display_config_watch_id);
+ manager->display_config_watch_id = 0;
+ }
+
+ if (manager->shell_name_watch_id > 0) {
+ g_bus_unwatch_name (manager->shell_name_watch_id);
+ manager->shell_name_watch_id = 0;
+ }
+
+ if (manager->manager != NULL) {
+ xsettings_manager_destroy (manager->manager);
+ manager->manager = NULL;
+ }
+
+ if (manager->plugin_settings != NULL) {
+ g_signal_handlers_disconnect_by_data (manager->plugin_settings, manager);
+ g_object_unref (manager->plugin_settings);
+ manager->plugin_settings = NULL;
+ }
+
+ if (manager->gtk_settings_name_id > 0) {
+ g_bus_unown_name (manager->gtk_settings_name_id);
+ manager->gtk_settings_name_id = 0;
+ }
+
+ if (manager->fontconfig_monitor != NULL) {
+ g_signal_handlers_disconnect_by_data (manager->fontconfig_monitor, manager);
+ fc_monitor_stop (manager->fontconfig_monitor);
+ g_object_unref (manager->fontconfig_monitor);
+ manager->fontconfig_monitor = NULL;
+ }
+
+ if (manager->settings != NULL) {
+ g_hash_table_destroy (manager->settings);
+ manager->settings = NULL;
+ }
+
+ if (manager->gtk != NULL) {
+ g_object_unref (manager->gtk);
+ manager->gtk = NULL;
+ }
+
+ if (manager->user_seat != NULL) {
+ g_signal_handler_disconnect (manager->user_seat, manager->device_added_id);
+ g_signal_handler_disconnect (manager->user_seat, manager->device_removed_id);
+ manager->user_seat = NULL;
+ }
+
+ g_clear_object (&manager->a11y_settings);
+ g_clear_object (&manager->input_sources_settings);
+ g_clear_object (&manager->interface_settings);
+}
+
+static void
+gsd_xsettings_manager_class_init (GsdXSettingsManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gsd_xsettings_manager_finalize;
+}
+
+static void
+gsd_xsettings_manager_init (GsdXSettingsManager *manager)
+{
+ GError *error = NULL;
+
+ manager->dbus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION,
+ NULL, &error);
+ if (!manager->dbus_connection)
+ g_error ("Failed to get session bus: %s", error->message);
+}
+
+static void
+gsd_xsettings_manager_finalize (GObject *object)
+{
+ GsdXSettingsManager *xsettings_manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GSD_IS_XSETTINGS_MANAGER (object));
+
+ xsettings_manager = GSD_XSETTINGS_MANAGER (object);
+
+ g_return_if_fail (xsettings_manager != NULL);
+
+ gsd_xsettings_manager_stop (xsettings_manager);
+
+ if (xsettings_manager->start_idle_id != 0)
+ g_source_remove (xsettings_manager->start_idle_id);
+
+ g_clear_object (&xsettings_manager->dbus_connection);
+
+ G_OBJECT_CLASS (gsd_xsettings_manager_parent_class)->finalize (object);
+}
+
+static GVariant *
+handle_get_property (GDBusConnection *connection,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *property_name,
+ GError **error,
+ gpointer user_data)
+{
+ GsdXSettingsManager *manager = user_data;
+
+ if (g_strcmp0 (property_name, "FontconfigTimestamp") == 0) {
+ return g_variant_new_int64 (manager->fontconfig_timestamp);
+ } else if (g_strcmp0 (property_name, "Modules") == 0) {
+ const char *modules = gsd_xsettings_gtk_get_modules (manager->gtk);
+ return g_variant_new_string (modules ? modules : "");
+ } else if (g_strcmp0 (property_name, "EnableAnimations") == 0) {
+ return g_variant_new_boolean (manager->enable_animations);
+ } else {
+ g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+ "No such interface: %s", interface_name);
+ return NULL;
+ }
+}
+
+static const GDBusInterfaceVTable interface_vtable =
+{
+ NULL,
+ handle_get_property,
+ NULL
+};
+
+static void
+register_manager_dbus (GsdXSettingsManager *manager)
+{
+ g_assert (manager->dbus_connection != NULL);
+
+ manager->introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+ g_assert (manager->introspection_data != NULL);
+
+ g_dbus_connection_register_object (manager->dbus_connection,
+ GTK_SETTINGS_DBUS_PATH,
+ manager->introspection_data->interfaces[0],
+ &interface_vtable,
+ manager,
+ NULL,
+ NULL);
+
+ manager->gtk_settings_name_id = g_bus_own_name_on_connection (manager->dbus_connection,
+ GTK_SETTINGS_DBUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL, NULL, NULL, NULL);
+}
+
+GsdXSettingsManager *
+gsd_xsettings_manager_new (void)
+{
+ if (manager_object != NULL) {
+ g_object_ref (manager_object);
+ } else {
+ manager_object = g_object_new (GSD_TYPE_XSETTINGS_MANAGER, NULL);
+ g_object_add_weak_pointer (manager_object,
+ (gpointer *) &manager_object);
+ }
+
+ return GSD_XSETTINGS_MANAGER (manager_object);
+}