[Xfce4-commits] <xfce4-settings:master> Add a rewrite of the xsettings daemon in the helper.

Nick Schermer noreply at xfce.org
Mon Feb 28 17:16:02 CET 2011


Updating branch refs/heads/master
         to b912244abf6fa0824926cb0a39eb5f20bfcdd4a9 (commit)
       from 0122fde699de1c49748d09016c1447bd3eb7e647 (commit)

commit b912244abf6fa0824926cb0a39eb5f20bfcdd4a9
Author: Nick Schermer <nick at xfce.org>
Date:   Tue Feb 22 22:27:53 2011 +0100

    Add a rewrite of the xsettings daemon in the helper.
    
    This version is a lot more flexible since it accepts all
    properties in the /Xft, /Net and /Gtk, so the user can
    add custom properties as well.
    
    The hardcoded defaults have also been removed in favour of a
    complete xsettings.xml file and a xfconf-like. It has also
    been optimized to steal values from xfconf on startup to
    reduce string copying.
    
    The daemon now also does the right thing with serials on properties,
    so listeners can update only the changed properties.
    
    In the process a bunch of leaks have been fixed and comments have
    been added to explain the xsettings data format, including proper
    error trapping of X11 calls.

 po/POTFILES.in                      |    1 +
 xfce4-settings-helper/Makefile.am   |   10 +-
 xfce4-settings-helper/main.c        |   10 +
 xfce4-settings-helper/xsettings.c   |  967 +++++++++++++++++++++++++++++++++++
 xfce4-settings-helper/xsettings.h   |   39 ++
 xfce4-settings-helper/xsettings.xml |   45 ++
 6 files changed, 1070 insertions(+), 2 deletions(-)

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 462ad59..387110c 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -34,6 +34,7 @@ xfce4-settings-helper/keyboard-layout.c
 xfce4-settings-helper/keyboard-shortcuts.c
 xfce4-settings-helper/main.c
 xfce4-settings-helper/workspaces.c
+xfce4-settings-helper/xsettings.c
 
 xfce4-settings-manager/main.c
 xfce4-settings-manager/xfce-settings-manager-dialog.c
diff --git a/xfce4-settings-helper/Makefile.am b/xfce4-settings-helper/Makefile.am
index 3405775..33f6692 100644
--- a/xfce4-settings-helper/Makefile.am
+++ b/xfce4-settings-helper/Makefile.am
@@ -28,7 +28,9 @@ xfce4_settings_helper_SOURCES = \
 	pointers.c \
 	pointers.h \
 	workspaces.c \
-	workspaces.h
+	workspaces.h \
+	xsettings.c \
+	xsettings.h
 
 xfce4_settings_helper_CFLAGS = \
 	-I$(top_builddir) \
@@ -80,10 +82,14 @@ xfce4_settings_helper_LDADD += \
 	$(XRANDR_LIBS)
 endif
 
+settingsdir = $(sysconfdir)/xdg/xfce4/xfconf/xfce-perchannel-xml
+settings_DATA = xsettings.xml
+
 autostartdir = $(sysconfdir)/xdg/autostart
 autostart_DATA = xfce4-settings-helper-autostart.desktop
 
 EXTRA_DIST = \
-	xfce4-settings-helper-autostart.desktop
+	$(settings_DATA) \
+	$(autostart_DATA)
 
 # vi:set ts=8 sw=8 noet ai nocindent syntax=automake:
diff --git a/xfce4-settings-helper/main.c b/xfce4-settings-helper/main.c
index 453fd92..3cec8ce 100644
--- a/xfce4-settings-helper/main.c
+++ b/xfce4-settings-helper/main.c
@@ -57,6 +57,7 @@
 #include "keyboard-shortcuts.h"
 #include "workspaces.h"
 #include "clipboard-manager.h"
+#include "xsettings.h"
 
 #ifdef HAVE_XRANDR
 #include "displays.h"
@@ -69,10 +70,12 @@ static XfceSMClient *sm_client = NULL;
 
 static gboolean opt_version = FALSE;
 static gboolean opt_debug = FALSE;
+static gboolean opt_force_replace = FALSE;
 static GOptionEntry option_entries[] =
 {
     { "version", 'V', 0, G_OPTION_ARG_NONE, &opt_version, N_("Version information"), NULL },
     { "debug", 'd', 0, G_OPTION_ARG_NONE, &opt_debug, N_("Start in debug mode (don't fork to the background)"), NULL },
+    { "force", 'f', 0, G_OPTION_ARG_NONE, &opt_force_replace, N_("Replace running xsettings daemon (if any)"), NULL },
     { NULL }
 };
 
@@ -134,6 +137,7 @@ main (gint argc, gchar **argv)
     GObject              *accessibility_helper;
     GObject              *shortcuts_helper;
     GObject              *keyboard_layout_helper;
+    GObject              *xsettings_helper;
 #ifdef HAVE_XRANDR
     GObject              *displays_helper;
 #endif
@@ -238,6 +242,11 @@ main (gint argc, gchar **argv)
         }
     }
 
+    /* launch settings manager */
+    xsettings_helper = g_object_new (XFCE_TYPE_XSETTINGS_HELPER, NULL);
+    xfce_xsettings_helper_register (XFCE_XSETTINGS_HELPER (xsettings_helper),
+                                    gdk_display_get_default (), opt_force_replace);
+
     /* create the sub daemons */
 #ifdef HAVE_XRANDR
     displays_helper = g_object_new (XFCE_TYPE_DISPLAYS_HELPER, NULL);
@@ -279,6 +288,7 @@ main (gint argc, gchar **argv)
     g_object_unref (G_OBJECT (shortcuts_helper));
     g_object_unref (G_OBJECT (keyboard_layout_helper));
     g_object_unref (G_OBJECT (workspaces_helper));
+    g_object_unref (G_OBJECT (xsettings_helper));
 
     if (G_LIKELY (clipboard_daemon != NULL))
     {
diff --git a/xfce4-settings-helper/xsettings.c b/xfce4-settings-helper/xsettings.c
new file mode 100644
index 0000000..3c1a045
--- /dev/null
+++ b/xfce4-settings-helper/xsettings.c
@@ -0,0 +1,967 @@
+/*
+ * Copyright (c) 2008 Stephan Arts <stephan at xfce.org>
+ * Copyright (c) 2011 Nick Schermer <nick at xfce.org>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * See http://standards.freedesktop.org/xsettings-spec/xsettings-spec-0.5.html
+ * for the description of the xsetting specification
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/Xmd.h>
+#include <X11/Xatom.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkx.h>
+#include <xfconf/xfconf.h>
+#include <libxfce4util/libxfce4util.h>
+
+#include "xsettings.h"
+
+#define XSettingsTypeInteger 0
+#define XSettingsTypeString  1
+#define XSettingsTypeColor   2
+
+#define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
+
+#define DPI_FALLBACK        96
+#define DPI_LOW_REASONABLE  50
+#define DPI_HIGH_REASONABLE 500
+
+
+
+typedef struct _XfceXSettingsScreen XfceXSettingsScreen;
+typedef struct _XfceXSetting        XfceXSetting;
+typedef struct _XfceXSettingsNotify XfceXSettingsNotify;
+
+
+
+static void xfce_xsettings_helper_finalize     (GObject             *object);
+static void xfce_xsettings_helper_setting_free (gpointer             data);
+static void xfce_xsettings_helper_prop_changed (XfconfChannel       *channel,
+                                                const gchar         *prop_name,
+                                                const GValue        *value,
+                                                XfceXSettingsHelper *helper);
+static void xfce_xsettings_helper_load         (XfceXSettingsHelper *helper);
+static void xfce_xsettings_helper_screen_free  (XfceXSettingsScreen *screen);
+static void xfce_xsettings_helper_notify_xft   (XfceXSettingsHelper *helper);
+static void xfce_xsettings_helper_notify       (XfceXSettingsHelper *helper);
+
+
+
+struct _XfceXSettingsHelperClass
+{
+    GObjectClass __parent__;
+};
+
+struct _XfceXSettingsHelper
+{
+    GObject  __parent__;
+
+    XfconfChannel *channel;
+
+    /* list of XfceXSettingsScreen we handle */
+    GSList        *screens;
+
+    /* table with xfconf property keyd and XfceXSetting */
+    GHashTable    *settings;
+
+    /* auto increasing serial for each time we notify */
+    gulong         serial;
+
+    /* idle notifications */
+    guint          notify_idle_id;
+    guint          notify_xft_idle_id;
+
+    Atom           xsettings_atom;
+};
+
+struct _XfceXSetting
+{
+    GValue *value;
+    gulong  last_change_serial;
+};
+
+struct _XfceXSettingsNotify
+{
+    guchar *buf;
+    gsize   buf_len;
+    CARD32  n_settings;
+    gsize   dpi_offset;
+};
+
+struct _XfceXSettingsScreen
+{
+    Display *xdisplay;
+    Window   window;
+    Atom     selection_atom;
+    gint     screen_num;
+};
+
+
+
+G_DEFINE_TYPE (XfceXSettingsHelper, xfce_xsettings_helper, G_TYPE_OBJECT);
+
+
+
+static void
+xfce_xsettings_helper_class_init (XfceXSettingsHelperClass *klass)
+{
+    GObjectClass *gobject_class;
+
+    gobject_class = G_OBJECT_CLASS (klass);
+    gobject_class->finalize = xfce_xsettings_helper_finalize;
+}
+
+
+
+static void
+xfce_xsettings_helper_init (XfceXSettingsHelper *helper)
+{
+    helper->channel = xfconf_channel_new ("xsettings");
+
+    helper->settings = g_hash_table_new_full (g_str_hash, g_str_equal,
+        g_free, xfce_xsettings_helper_setting_free);
+
+    xfce_xsettings_helper_load (helper);
+
+    g_signal_connect (G_OBJECT (helper->channel), "property-changed",
+        G_CALLBACK (xfce_xsettings_helper_prop_changed), helper);
+}
+
+
+
+static void
+xfce_xsettings_helper_finalize (GObject *object)
+{
+    XfceXSettingsHelper *helper = XFCE_XSETTINGS_HELPER (object);
+    GSList              *li;
+
+    /* stop pending update */
+    if (helper->notify_idle_id != 0)
+        g_source_remove (helper->notify_idle_id);
+
+    if (helper->notify_xft_idle_id != 0)
+        g_source_remove (helper->notify_xft_idle_id);
+
+    g_object_unref (G_OBJECT (helper->channel));
+
+    /* remove screens */
+    for (li = helper->screens; li != NULL; li = li->next)
+        xfce_xsettings_helper_screen_free (li->data);
+    g_slist_free (helper->screens);
+
+    g_hash_table_destroy (helper->settings);
+
+    (*G_OBJECT_CLASS (xfce_xsettings_helper_parent_class)->finalize) (object);
+}
+
+
+
+static gboolean
+xfce_xsettings_helper_notify_idle (gpointer data)
+{
+    XfceXSettingsHelper *helper = XFCE_XSETTINGS_HELPER (data);
+
+    /* only update if there are screen registered */
+    if (helper->screens != NULL)
+        xfce_xsettings_helper_notify (helper);
+
+    helper->notify_idle_id = 0;
+
+    return FALSE;
+}
+
+
+
+static gboolean
+xfce_xsettings_helper_notify_xft_idle (gpointer data)
+{
+    XfceXSettingsHelper *helper = XFCE_XSETTINGS_HELPER (data);
+
+    /* only update if there are screen registered */
+    if (helper->screens != NULL)
+        xfce_xsettings_helper_notify_xft (helper);
+
+    helper->notify_xft_idle_id = 0;
+
+    return FALSE;
+}
+
+
+
+static gboolean
+xfce_xsettings_helper_prop_valid (const gchar  *prop_name,
+                                  const GValue *value)
+{
+    /* only accept properties in valid domains */
+    if (!g_str_has_prefix (prop_name, "/Net/")
+        && !g_str_has_prefix (prop_name, "/Xft/")
+        && !g_str_has_prefix (prop_name, "/Gtk/"))
+        return FALSE;
+
+    /* notify if the property has an unsupported type */
+    if (!G_VALUE_HOLDS_BOOLEAN (value)
+        && !G_VALUE_HOLDS_INT (value)
+        && !G_VALUE_HOLDS_STRING (value))
+    {
+        g_warning ("Property \"%s\" has an unsupported type \"%s\".",
+                   prop_name, G_VALUE_TYPE_NAME (value));
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+
+static gboolean
+xfce_xsettings_helper_prop_load (gchar               *prop_name,
+                                 GValue              *value,
+                                 XfceXSettingsHelper *helper)
+{
+    XfceXSetting *setting;
+
+    /* check if the property is valid */
+    if (!xfce_xsettings_helper_prop_valid (prop_name, value))
+        return FALSE;
+
+    setting = g_slice_new0 (XfceXSetting);
+    setting->value = value;
+    setting->last_change_serial = helper->serial;
+
+    g_hash_table_insert (helper->settings, prop_name, setting);
+
+    /* we've stolen the value */
+    return TRUE;
+}
+
+
+
+static void
+xfce_xsettings_helper_prop_changed (XfconfChannel       *channel,
+                                    const gchar         *prop_name,
+                                    const GValue        *value,
+                                    XfceXSettingsHelper *helper)
+{
+    XfceXSetting *setting;
+
+    g_return_if_fail (helper->channel == channel);
+
+    if (G_LIKELY (value != NULL))
+    {
+        setting = g_hash_table_lookup (helper->settings, prop_name);
+        if (G_LIKELY (setting != NULL))
+        {
+            /* update the value, assuming the types match because
+             * you cannot changes types in xfconf without removing
+             * it first */
+            g_value_reset (setting->value);
+            g_value_copy (value, setting->value);
+
+            /* update the serial */
+            setting->last_change_serial = helper->serial;
+        }
+        else if (xfce_xsettings_helper_prop_valid (prop_name, value))
+        {
+            /* insert a new setting */
+            setting = g_slice_new0 (XfceXSetting);
+            setting->value = g_new0 (GValue, 1);
+            setting->last_change_serial = helper->serial;
+
+            g_value_init (setting->value, G_VALUE_TYPE (value));
+            g_value_copy (value, setting->value);
+
+            g_hash_table_insert (helper->settings, g_strdup (prop_name), setting);
+        }
+        else
+        {
+           /* leave, so not notification is scheduled */
+           return;
+        }
+    }
+    else
+    {
+        /* maybe the value is not found, because we haven't
+         * checked if the property is valid, but that's not
+         * a problem */
+        g_hash_table_remove (helper->settings, prop_name);
+    }
+
+    if (helper->notify_idle_id == 0)
+    {
+        /* schedule an update */
+        helper->notify_idle_id = g_idle_add (xfce_xsettings_helper_notify_idle, helper);
+    }
+
+    if (helper->notify_xft_idle_id == 0
+        && g_str_has_prefix (prop_name, "/Xft/"))
+    {
+        helper->notify_xft_idle_id = g_idle_add (xfce_xsettings_helper_notify_xft_idle, helper);
+    }
+}
+
+
+
+static void
+xfce_xsettings_helper_load (XfceXSettingsHelper *helper)
+{
+    GHashTable *props;
+
+    props = xfconf_channel_get_properties (helper->channel, NULL);
+    if (G_LIKELY (props != NULL))
+      {
+        /* steal properties and put them in the settings table */
+        g_hash_table_foreach_steal (props,
+            (GHRFunc) xfce_xsettings_helper_prop_load, helper);
+
+        /* destroy the remaining properties */
+        g_hash_table_destroy (props);
+      }
+}
+
+
+
+static void
+xfce_xsettings_helper_setting_free (gpointer data)
+{
+    XfceXSetting *setting = data;
+
+    g_value_unset (setting->value);
+    g_free (setting->value);
+    g_slice_free (XfceXSetting, setting);
+}
+
+
+
+static gint
+xfce_xsettings_helper_screen_dpi (XfceXSettingsScreen *screen)
+{
+    Screen *xscreen;
+    gint    width_mm, width_dpi;
+    gint    height_mm, height_dpi;
+    gint    dpi = DPI_FALLBACK;
+
+    xscreen = ScreenOfDisplay (screen->xdisplay, screen->screen_num);
+    if (G_LIKELY (xscreen != NULL))
+    {
+        width_mm = WidthMMOfScreen (xscreen);
+        height_mm = HeightMMOfScreen (xscreen);
+
+        if (G_LIKELY (width_mm > 0 && height_mm > 0))
+        {
+            width_dpi = 25.4 * WidthOfScreen (xscreen) / width_mm;
+            height_dpi = 25.4 * HeightOfScreen (xscreen) / height_mm;
+
+            /* both values need to be reasonable */
+            if (width_dpi > DPI_LOW_REASONABLE && width_dpi < DPI_HIGH_REASONABLE
+                && height_dpi > DPI_LOW_REASONABLE && height_dpi < DPI_HIGH_REASONABLE)
+            {
+                /* gnome takes the average between the two, however the
+                 * minimin seems to result in sharper font in more cases */
+                dpi = MIN (width_dpi, height_dpi);
+            }
+        }
+    }
+
+    return dpi;
+}
+
+
+
+static void
+xfce_xsettings_helper_notify_xft_update (GString      *resource,
+                                         const gchar  *name,
+                                         const GValue *value)
+{
+    gchar       *found;
+    gchar       *end;
+    const gchar *str = NULL;
+    gchar        s[64];
+    gint         num;
+
+    /* remove the old property */
+    found = strstr (resource->str, name);
+    if (found != NULL)
+    {
+        end = strchr (found, '\n');
+        g_string_erase (resource, found - resource->str,
+                        end != NULL ? end - found + 1 : -1);
+    }
+
+    switch (G_VALUE_TYPE (value))
+    {
+        case G_TYPE_STRING:
+            str = g_value_get_string (value);
+            break;
+
+        case G_TYPE_BOOLEAN:
+            str = g_value_get_boolean (value) ? "1" : "0";
+            break;
+
+        case G_TYPE_INT:
+            num = g_value_get_int (value);
+
+            /* -1 means default in xft, so only remove it */
+            if (num == -1)
+                return;
+
+            /* special case for dpi */
+            if (strcmp (name, "Xft.dpi:") == 0)
+                num = CLAMP (num, DPI_LOW_REASONABLE, DPI_HIGH_REASONABLE) * 1024;
+
+            g_snprintf  (s, sizeof (s), "%d", num);
+            str = s;
+            break;
+
+        default:
+            g_assert_not_reached ();
+    }
+
+    if (str != NULL)
+    {
+        /* append a new line if required */
+        if (!g_str_has_suffix (resource->str, "\n"))
+            g_string_append_c (resource, '\n');
+
+        g_string_append_printf (resource, "%s\t%s\n", name, str);
+    }
+}
+
+
+
+static void
+xfce_xsettings_helper_notify_xft (XfceXSettingsHelper *helper)
+{
+    Display      *xdisplay;
+    gchar        *str;
+    GString      *resource;
+    XfceXSetting *setting;
+    guint         i;
+    const gchar  *props[][2] =
+    {
+        /* { xfconf name}, { xft name } */
+        { "/Xft/Antialias", "Xft.antialias:" },
+        { "/Xft/Hinting", "Xft.hinting:" },
+        { "/Xft/HintStyle", "Xft.hintstyle" },
+        { "/Xft/RGBA", "Xft.rgba:" },
+        { "/Xft/Lcdfilter", "Xft.lcdfilter:" },
+        { "/Xft/DPI", "Xft.dpi:" }
+    };
+
+    g_return_if_fail (XFCE_IS_XSETTINGS_HELPER (helper));
+
+    if (G_LIKELY (helper->screens == NULL))
+        return;
+
+    xdisplay = XOpenDisplay (NULL);
+    g_return_if_fail (xdisplay != NULL);
+
+    /* get the resource string from this display from screen zero */
+    str = XResourceManagerString (xdisplay);
+    resource = g_string_new (str);
+
+    /* update/insert the properties */
+    for (i = 0; i < G_N_ELEMENTS (props); i++)
+    {
+        setting = g_hash_table_lookup (helper->settings, props[i][0]);
+        if (G_LIKELY (setting != NULL))
+        {
+            xfce_xsettings_helper_notify_xft_update (resource, props[i][1],
+                                                     setting->value);
+        }
+    }
+
+    gdk_error_trap_push ();
+
+    /* set the new resource manager string */
+    XChangeProperty (xdisplay,
+                     RootWindow (xdisplay, 0),
+                     XA_RESOURCE_MANAGER, XA_STRING, 8,
+                     PropModeReplace,
+                     (guchar *) resource->str,
+                     resource->len);
+
+    XCloseDisplay (xdisplay);
+
+    if (gdk_error_trap_pop () != 0)
+        g_critical ("Failed to update the resource manager string");
+
+    g_string_free (resource, TRUE);
+}
+
+
+
+static void
+xfce_xsettings_helper_setting_append (const gchar         *name,
+                                      XfceXSetting        *setting,
+                                      XfceXSettingsNotify *notify)
+{
+    gsize        buf_len, new_len;
+    gsize        name_len, name_len_pad;
+    gsize        value_len, value_len_pad;
+    const gchar *str = NULL;
+    guchar      *needle;
+    CARD16       type = -1;
+    gint         num;
+
+    name_len = strlen (name) - 1 /* -1 for the xfconf slash */;
+    name_len_pad = XSETTINGS_PAD (name_len, 4);
+
+    buf_len = 8 + name_len_pad;
+    value_len_pad = value_len = 0;
+
+    /* get the total size of this setting */
+    switch (G_VALUE_TYPE (setting->value))
+    {
+        case G_TYPE_INT:
+        case G_TYPE_BOOLEAN:
+            type = XSettingsTypeInteger;
+            buf_len += 4;
+            break;
+
+        case G_TYPE_STRING:
+            type = XSettingsTypeString;
+            buf_len += 4;
+            str = g_value_get_string (setting->value);
+            if (str != NULL)
+            {
+                value_len = strlen (str);
+                value_len_pad = XSETTINGS_PAD (value_len, 4);
+                buf_len += value_len_pad;
+            }
+            break;
+
+        case G_TYPE_INT64 /* TODO */:
+            type = XSettingsTypeColor;
+            buf_len += 8;
+            break;
+
+        default:
+            g_assert_not_reached ();
+            break;
+    }
+
+    /* additional length for this setting */
+    new_len = notify->buf_len + buf_len;
+
+    /* resize the buffer to fit this setting */
+    notify->buf = g_renew (guchar, notify->buf, new_len);
+    if (G_UNLIKELY (notify->buf == NULL))
+      return;
+    needle = notify->buf + notify->buf_len;
+    notify->buf_len = new_len;
+
+    /* setting record:
+     *
+     * 1  SETTING_TYPE  type
+     * 1                unused
+     * 2  n             name-len
+     * n  STRING8       name
+     * P                unused, p=pad(n)
+     * 4  CARD32        last-change-serial
+     */
+
+    /* setting type */
+    *(CARD16 *)needle++ = type;
+
+    /* unused */
+    *needle++ = 0;
+
+    /* name length */
+    *(CARD16 *)needle = name_len;
+    needle += 2;
+
+    /* name */
+    memcpy (needle, name + 1 /* +1 for the xfconf slash */, name_len);
+    needle += name_len;
+
+    /* zero the padding */
+    for (; name_len_pad > name_len; name_len_pad--)
+        *needle++ = 0;
+
+    /* setting's last change serial */
+    *(CARD32 *)needle = setting->last_change_serial;
+    needle += 4;
+
+    /* set setting value */
+    switch (type)
+    {
+        case XSettingsTypeString:
+            /* body for XSettingsTypeString:
+             *
+             * 4  n        value-len
+             * n  STRING8  value
+             * P           unused, p=pad(n)
+             */
+            if (G_LIKELY (value_len > 0 && str != NULL))
+            {
+                /* value length */
+                *(CARD32 *)needle = value_len;
+                needle += 4;
+
+                /* value */
+                memcpy (needle, str, value_len);
+                needle += value_len;
+
+                /* zero the padding */
+                for (; value_len_pad > value_len; value_len_pad--)
+                    *needle++ = 0;
+            }
+            else
+            {
+                /* value length */
+                *(CARD32 *)needle = 0;
+                needle += 4;
+            }
+            break;
+
+        case XSettingsTypeInteger:
+            /* Body for XSettingsTypeInteger:
+             *
+             * 4  INT32  value
+             */
+            if (G_VALUE_TYPE (setting->value) == G_TYPE_INT)
+            {
+                num = g_value_get_int (setting->value);
+
+                /* special case handling for DPI */
+                if (strcmp (name, "/Xft/DPI") == 0)
+                {
+                    /* remember the offset for screen dependend dpi
+                     * or clamp the value and set 1/1024ths of an inch
+                     * for Xft */
+                    if (num < 1)
+                        notify->dpi_offset = needle - notify->buf;
+                    else
+                        num = CLAMP (num, DPI_LOW_REASONABLE, DPI_HIGH_REASONABLE) * 1024;
+                }
+            }
+            else
+            {
+                num = g_value_get_boolean (setting->value);
+            }
+
+            *(INT32 *)needle = num;
+            needle += 4;
+            break;
+
+        /* TODO */
+        case XSettingsTypeColor:
+            /* body for XSettingsTypeColor:
+            *
+            * 2  CARD16  red
+            * 2  CARD16  blue
+            * 2  CARD16  green
+            * 2  CARD16  alpha
+            */
+            *(CARD16 *)needle = 0;
+            *(CARD16 *)(needle + 2) = 0;
+            *(CARD16 *)(needle + 4) = 0;
+            *(CARD16 *)(needle + 6) = 0;
+            needle += 8;
+            break;
+
+        default:
+            g_assert_not_reached ();
+            break;
+    }
+
+    notify->n_settings++;
+}
+
+
+
+static void
+xfce_xsettings_helper_notify (XfceXSettingsHelper *helper)
+{
+    XfceXSettingsNotify *notify;
+    CARD32               orderint = 0x01020304;
+    guchar              *needle;
+    XfceXSettingsScreen *screen;
+    GSList              *li;
+    gint                 dpi;
+
+    g_return_if_fail (XFCE_IS_XSETTINGS_HELPER (helper));
+
+    notify = g_slice_new0 (XfceXSettingsNotify);
+    notify->buf = g_new0 (guchar, 12);
+    if (G_UNLIKELY (notify->buf == NULL))
+      goto errnomem;
+    notify->buf_len = 12;
+    needle = notify->buf;
+
+    /* general notification form:
+     *
+     * 1  CARD8   byte-order
+     * 3          unused
+     * 4  CARD32  SERIAL
+     * 4  CARD32  N_SETTINGS
+     */
+
+    /* byte-order */
+    *(CARD8 *)needle = (*(char *)&orderint == 1) ? MSBFirst : LSBFirst;
+    needle += 4;
+
+    /* serial for this notification */
+    *(CARD32 *)needle = helper->serial++;
+
+    /* add all the settings */
+    g_hash_table_foreach (helper->settings,
+        (GHFunc) xfce_xsettings_helper_setting_append, notify);
+
+    if (G_UNLIKELY (notify->buf == NULL))
+      goto errnomem;
+
+    /* number of settings */
+    needle = notify->buf + 8;
+    *(CARD32 *)needle = notify->n_settings;
+
+    gdk_error_trap_push ();
+
+    /* set new xsettings buffer to the screens */
+    for (li = helper->screens; li != NULL; li = li->next)
+    {
+        screen = li->data;
+
+        /* set the accurate dpi for this screen */
+        if (notify->dpi_offset > 0)
+        {
+            dpi = xfce_xsettings_helper_screen_dpi (screen);
+            needle = notify->buf + notify->dpi_offset;
+            *(INT32 *)needle = dpi * 1024;
+        }
+
+        XChangeProperty (screen->xdisplay, screen->window,
+                         helper->xsettings_atom, helper->xsettings_atom,
+                         8, PropModeReplace, notify->buf, notify->buf_len);
+    }
+
+    if (gdk_error_trap_pop () != 0)
+    {
+        g_critical ("Failed to set properties");
+    }
+
+    g_free (notify->buf);
+  errnomem:
+    g_slice_free (XfceXSettingsNotify, notify);
+}
+
+
+
+static void
+xfce_xsettings_helper_screen_free (XfceXSettingsScreen *screen)
+{
+    XDestroyWindow (screen->xdisplay, screen->window);
+    g_slice_free (XfceXSettingsScreen, screen);
+}
+
+
+
+static GdkFilterReturn
+xfce_xsettings_helper_event_filter (GdkXEvent *gdkxevent,
+                                    GdkEvent  *gdkevent,
+                                    gpointer   data)
+{
+    XfceXSettingsHelper *helper = XFCE_XSETTINGS_HELPER (data);
+    GSList              *li;
+    XfceXSettingsScreen *screen;
+    XEvent              *xevent = gdkxevent;
+
+    /* check if another settings manager took over the selection
+     * of one of the windows */
+    if (xevent->xany.type == SelectionClear)
+    {
+        for (li = helper->screens; li != NULL; li = li->next)
+        {
+            screen = li->data;
+
+            if (xevent->xany.window == screen->window
+                && xevent->xselectionclear.selection == screen->selection_atom)
+            {
+                /* remove the screen */
+                helper->screens = g_slist_delete_link (helper->screens, li);
+                xfce_xsettings_helper_screen_free (screen);
+
+                /* remove this filter if there are no screens */
+                if (helper->screens == NULL)
+                    gdk_window_remove_filter (NULL, xfce_xsettings_helper_event_filter, data);
+
+                return GDK_FILTER_REMOVE;
+            }
+        }
+    }
+
+    return GDK_FILTER_CONTINUE;
+}
+
+
+
+static Bool
+xfce_xsettings_helper_timestamp_predicate (Display  *xdisplay,
+                                           XEvent   *xevent,
+                                           XPointer  arg)
+{
+    Window window = GPOINTER_TO_UINT (arg);
+
+    return (xevent->type == PropertyNotify
+            && xevent->xproperty.window == window
+            && xevent->xproperty.atom == XInternAtom (xdisplay, "_TIMESTAMP_PROP", False));
+}
+
+
+
+gboolean
+xfce_xsettings_helper_register (XfceXSettingsHelper *helper,
+                                GdkDisplay          *gdkdisplay,
+                                gboolean             force_replace)
+{
+    Display             *xdisplay;
+    Window               root_window;
+    Window               window;
+    gchar                atom_name[64];
+    Atom                 selection_atom;
+    Atom                 timestamp_atom;
+    gint                 n_screens, n;
+    guchar               c = 'a';
+    XfceXSettingsScreen *screen;
+    XEvent               xevent;
+    Time                 timestamp;
+    XClientMessageEvent  xev;
+    gboolean             succeed;
+
+    g_return_val_if_fail (GDK_IS_DISPLAY (gdkdisplay), FALSE);
+    g_return_val_if_fail (XFCE_IS_XSETTINGS_HELPER (helper), FALSE);
+    g_return_val_if_fail (helper->screens == NULL, FALSE);
+
+    xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+    helper->xsettings_atom = XInternAtom (xdisplay, "_XSETTINGS_SETTINGS", False);
+
+    gdk_error_trap_push ();
+
+    n_screens = gdk_display_get_n_screens (gdkdisplay);
+    for (n = 0; n < n_screens; n++)
+    {
+        g_snprintf (atom_name, sizeof (atom_name), "_XSETTINGS_S%d", n);
+        selection_atom = XInternAtom (xdisplay, atom_name, False);
+
+        if (!force_replace
+            && XGetSelectionOwner (xdisplay, selection_atom) != None)
+        {
+            g_message ("Skipping screen %d, it already has an xsettings manager...", n);
+            continue;
+        }
+
+        succeed = FALSE;
+
+        /* create new window */
+        root_window = RootWindow (xdisplay, n);
+        window = XCreateSimpleWindow (xdisplay, root_window, -1, -1, 1, 1, 0, 0, 0);
+        g_assert (window != 0);
+        XSelectInput (xdisplay, window, PropertyChangeMask);
+
+        /* get the current xserver timestamp */
+        timestamp_atom = XInternAtom (xdisplay, "_TIMESTAMP_PROP", False);
+        XChangeProperty (xdisplay, window, timestamp_atom, timestamp_atom,
+                         8, PropModeReplace, &c, 1);
+        XIfEvent (xdisplay, &xevent, xfce_xsettings_helper_timestamp_predicate,
+                  GUINT_TO_POINTER (window));
+        timestamp = xevent.xproperty.time;
+
+        /* request ownership of the xsettings selection on this screen */
+        XSetSelectionOwner (xdisplay, selection_atom, window, timestamp);
+
+        /* check if the have the selection */
+        if (G_LIKELY (XGetSelectionOwner (xdisplay, selection_atom) == window))
+        {
+            /* register this xsettings window for this screen */
+            xev.type = ClientMessage;
+            xev.window = root_window;
+            xev.message_type = XInternAtom (xdisplay, "MANAGER", True);
+            xev.format = 32;
+            xev.data.l[0] = timestamp;
+            xev.data.l[1] = selection_atom;
+            xev.data.l[2] = window;
+            xev.data.l[3] = 0;  /* manager specific data */
+            xev.data.l[4] = 0;  /* manager specific data */
+
+            if (XSendEvent (xdisplay, root_window, False,
+                            StructureNotifyMask, (XEvent *)&xev) != 0)
+            {
+                /* the window was successfully registered as the new
+                 * xsettings window for this screen */
+                succeed = TRUE;
+            }
+            else
+            {
+                g_warning ("Failed to register the xsettings window for screen %d", n);
+            }
+        }
+        else
+        {
+            g_warning ("Unable to get the xsettings selection for screen %d", n);
+        }
+
+        if (G_LIKELY (succeed))
+        {
+            /* add the window to the internal list */
+            screen = g_slice_new0 (XfceXSettingsScreen);
+            screen->window = window;
+            screen->selection_atom = selection_atom;
+            screen->xdisplay = xdisplay;
+            screen->screen_num = n;
+
+            helper->screens = g_slist_prepend (helper->screens, screen);
+        }
+        else
+        {
+            XDestroyWindow (xdisplay, window);
+        }
+    }
+
+    if (gdk_error_trap_pop () != 0)
+        g_critical ("Failed to initialize screens");
+
+    if (helper->screens != NULL)
+    {
+        /* watch for selection changes */
+        gdk_window_add_filter (NULL, xfce_xsettings_helper_event_filter, helper);
+
+        /* send notifications */
+        xfce_xsettings_helper_notify (helper);
+        xfce_xsettings_helper_notify_xft (helper);
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
diff --git a/xfce4-settings-helper/xsettings.h b/xfce4-settings-helper/xsettings.h
new file mode 100644
index 0000000..76371a6
--- /dev/null
+++ b/xfce4-settings-helper/xsettings.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2008 Stephan Arts <stephan at xfce.org>
+ * Copyright (c) 2011 Nick Schermer <nick at xfce.org>
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __XSETTINGS_H__
+#define __XSETTINGS_H__
+
+typedef struct _XfceXSettingsHelperClass XfceXSettingsHelperClass;
+typedef struct _XfceXSettingsHelper      XfceXSettingsHelper;
+
+#define XFCE_TYPE_XSETTINGS_HELPER            (xfce_xsettings_helper_get_type ())
+#define XFCE_XSETTINGS_HELPER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), XFCE_TYPE_XSETTINGS_HELPER, XfceXSettingsHelper))
+#define XFCE_XSETTINGS_HELPER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), XFCE_TYPE_XSETTINGS_HELPER, XfceXSettingsHelperClass))
+#define XFCE_IS_XSETTINGS_HELPER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XFCE_TYPE_XSETTINGS_HELPER))
+#define XFCE_IS_XSETTINGS_HELPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XFCE_TYPE_XSETTINGS_HELPER))
+#define XFCE_XSETTINGS_HELPER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), XFCE_TYPE_XSETTINGS_HELPER, XfceXSettingsHelperClass))
+
+GType    xfce_xsettings_helper_get_type (void) G_GNUC_CONST;
+
+gboolean xfce_xsettings_helper_register (XfceXSettingsHelper *helper,
+                                         GdkDisplay          *gdkdisplay,
+                                         gboolean             force_replace);
+
+#endif /* !__XSETTINGS_H__ */
diff --git a/xfce4-settings-helper/xsettings.xml b/xfce4-settings-helper/xsettings.xml
new file mode 100644
index 0000000..8f9e2eb
--- /dev/null
+++ b/xfce4-settings-helper/xsettings.xml
@@ -0,0 +1,45 @@
+<!--
+  Default values for the X settings registry as described in
+  http://www.freedesktop.org/wiki/Specifications/XSettingsRegistry
+-->
+
+<?xml version="1.0" encoding="UTF-8"?>
+<channel name="xsettings" version="1.0">
+  <property name="Net" type="empty">
+    <property name="ThemeName" type="empty"/>
+    <property name="IconThemeName" type="empty"/>
+    <property name="DoubleClickTime" type="int" value="250"/>
+    <property name="DoubleClickDistance" type="int" value="5"/>
+    <property name="DndDragThreshold" type="int" value="8"/>
+    <property name="CursorBlink" type="bool" value="true"/>
+    <property name="CursorBlinkTime" type="int" value="1200"/>
+    <property name="SoundThemeName" type="string" value="default"/>
+    <property name="EnableEventSounds" type="bool" value="false"/>
+    <property name="EnableInputFeedbackSounds" type="bool" value="false"/>
+  </property>
+  <property name="Xft" type="empty">
+    <property name="DPI" type="empty"/>
+    <property name="Antialias" type="int" value="-1"/>
+    <property name="Hinting" type="int" value="-1"/>
+    <property name="HintStyle" type="string" value="hintnone"/>
+    <property name="RGBA" type="string" value="none"/>
+    <!-- <property name="Lcdfilter" type="string" value="none"/> -->
+  </property>
+  <property name="Gtk" type="empty">
+    <property name="CanChangeAccels" type="bool" value="false"/>
+    <property name="ColorPalette" type="string" value="black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90"/>
+    <property name="FontName" type="string" value="Sans 10"/>
+    <property name="IconSizes" type="string" value=""/>
+    <property name="KeyThemeName" type="string" value=""/>
+    <property name="ToolbarStyle" type="string" value="icons"/>
+    <property name="ToolbarIconSize" type="int" value="3"/>
+    <property name="IMPreeditStyle" type="string" value=""/>
+    <property name="IMStatusStyle" type="string" value=""/>
+    <property name="MenuImages" type="bool" value="true"/>
+    <property name="ButtonImages" type="bool" value="true"/>
+    <property name="MenuBarAccel" type="string" value="F10"/>
+    <property name="CursorThemeName" type="string" value=""/>
+    <property name="CursorThemeSize" type="int" value="0"/>
+    <property name="IMModule" type="string" value=""/>
+  </property>
+</channel>



More information about the Xfce4-commits mailing list