[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