summaryrefslogtreecommitdiffstats
path: root/plugins/xsettings/xsettings-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/xsettings/xsettings-manager.c')
-rw-r--r--plugins/xsettings/xsettings-manager.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/plugins/xsettings/xsettings-manager.c b/plugins/xsettings/xsettings-manager.c
new file mode 100644
index 0000000..89edef2
--- /dev/null
+++ b/plugins/xsettings/xsettings-manager.c
@@ -0,0 +1,396 @@
+/*
+ * Copyright © 2001 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Red Hat not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. Red Hat makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Owen Taylor, Red Hat, Inc.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <X11/Xmd.h> /* For CARD16 */
+
+#include "xsettings-manager.h"
+
+#define XSETTINGS_VARIANT_TYPE_COLOR (G_VARIANT_TYPE ("(qqqq)"))
+
+struct _XSettingsManager
+{
+ Display *display;
+ int screen;
+
+ Window window;
+ Atom manager_atom;
+ Atom selection_atom;
+ Atom xsettings_atom;
+
+ XSettingsTerminateFunc terminate;
+ void *cb_data;
+
+ GHashTable *settings;
+ unsigned long serial;
+
+ GVariant *overrides;
+};
+
+typedef struct
+{
+ Window window;
+ Atom timestamp_prop_atom;
+} TimeStampInfo;
+
+static Bool
+timestamp_predicate (Display *display,
+ XEvent *xevent,
+ XPointer arg)
+{
+ TimeStampInfo *info = (TimeStampInfo *)arg;
+
+ if (xevent->type == PropertyNotify &&
+ xevent->xproperty.window == info->window &&
+ xevent->xproperty.atom == info->timestamp_prop_atom)
+ return True;
+
+ return False;
+}
+
+/**
+ * get_server_time:
+ * @display: display from which to get the time
+ * @window: a #Window, used for communication with the server.
+ * The window must have PropertyChangeMask in its
+ * events mask or a hang will result.
+ *
+ * Routine to get the current X server time stamp.
+ *
+ * Return value: the time stamp.
+ **/
+static Time
+get_server_time (Display *display,
+ Window window)
+{
+ unsigned char c = 'a';
+ XEvent xevent;
+ TimeStampInfo info;
+
+ info.timestamp_prop_atom = XInternAtom (display, "_TIMESTAMP_PROP", False);
+ info.window = window;
+
+ XChangeProperty (display, window,
+ info.timestamp_prop_atom, info.timestamp_prop_atom,
+ 8, PropModeReplace, &c, 1);
+
+ XIfEvent (display, &xevent,
+ timestamp_predicate, (XPointer)&info);
+
+ return xevent.xproperty.time;
+}
+
+Bool
+xsettings_manager_check_running (Display *display,
+ int screen)
+{
+ char buffer[256];
+ Atom selection_atom;
+
+ sprintf(buffer, "_XSETTINGS_S%d", screen);
+ selection_atom = XInternAtom (display, buffer, False);
+
+ if (XGetSelectionOwner (display, selection_atom))
+ return True;
+ else
+ return False;
+}
+
+XSettingsManager *
+xsettings_manager_new (Display *display,
+ int screen,
+ XSettingsTerminateFunc terminate,
+ void *cb_data)
+{
+ XSettingsManager *manager;
+ Time timestamp;
+ XClientMessageEvent xev;
+
+ char buffer[256];
+
+
+ XFixesSetClientDisconnectMode (display, XFixesClientDisconnectFlagTerminate);
+
+ manager = g_new (XSettingsManager, 1);
+
+ manager->display = display;
+ manager->screen = screen;
+
+ sprintf(buffer, "_XSETTINGS_S%d", screen);
+ manager->selection_atom = XInternAtom (display, buffer, False);
+ manager->xsettings_atom = XInternAtom (display, "_XSETTINGS_SETTINGS", False);
+ manager->manager_atom = XInternAtom (display, "MANAGER", False);
+
+ manager->terminate = terminate;
+ manager->cb_data = cb_data;
+
+ manager->settings = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) xsettings_setting_free);
+ manager->serial = 0;
+ manager->overrides = NULL;
+
+ manager->window = XCreateSimpleWindow (display,
+ RootWindow (display, screen),
+ 0, 0, 10, 10, 0,
+ WhitePixel (display, screen),
+ WhitePixel (display, screen));
+
+ XSelectInput (display, manager->window, PropertyChangeMask);
+ timestamp = get_server_time (display, manager->window);
+
+ XSetSelectionOwner (display, manager->selection_atom,
+ manager->window, timestamp);
+
+ /* Check to see if we managed to claim the selection. If not,
+ * we treat it as if we got it then immediately lost it
+ */
+
+ if (XGetSelectionOwner (display, manager->selection_atom) ==
+ manager->window)
+ {
+ xev.type = ClientMessage;
+ xev.window = RootWindow (display, screen);
+ xev.message_type = manager->manager_atom;
+ xev.format = 32;
+ xev.data.l[0] = timestamp;
+ xev.data.l[1] = manager->selection_atom;
+ xev.data.l[2] = manager->window;
+ xev.data.l[3] = 0; /* manager specific data */
+ xev.data.l[4] = 0; /* manager specific data */
+
+ XSendEvent (display, RootWindow (display, screen),
+ False, StructureNotifyMask, (XEvent *)&xev);
+ }
+ else
+ {
+ manager->terminate (manager->cb_data);
+ }
+
+ return manager;
+}
+
+void
+xsettings_manager_destroy (XSettingsManager *manager)
+{
+ XDestroyWindow (manager->display, manager->window);
+
+ g_hash_table_unref (manager->settings);
+
+ g_free (manager);
+}
+
+static void
+xsettings_manager_set_setting (XSettingsManager *manager,
+ const gchar *name,
+ gint tier,
+ GVariant *value)
+{
+ XSettingsSetting *setting;
+
+ setting = g_hash_table_lookup (manager->settings, name);
+
+ if (setting == NULL)
+ {
+ setting = xsettings_setting_new (name);
+ setting->last_change_serial = manager->serial;
+ g_hash_table_insert (manager->settings, setting->name, setting);
+ }
+
+ xsettings_setting_set (setting, tier, value, manager->serial);
+
+ if (xsettings_setting_get (setting) == NULL)
+ g_hash_table_remove (manager->settings, name);
+}
+
+void
+xsettings_manager_set_int (XSettingsManager *manager,
+ const char *name,
+ int value)
+{
+ xsettings_manager_set_setting (manager, name, 0, g_variant_new_int32 (value));
+}
+
+void
+xsettings_manager_set_string (XSettingsManager *manager,
+ const char *name,
+ const char *value)
+{
+ xsettings_manager_set_setting (manager, name, 0, g_variant_new_string (value));
+}
+
+void
+xsettings_manager_set_color (XSettingsManager *manager,
+ const char *name,
+ XSettingsColor *value)
+{
+ GVariant *tmp;
+
+ tmp = g_variant_new ("(qqqq)", value->red, value->green, value->blue, value->alpha);
+ g_assert (g_variant_is_of_type (tmp, XSETTINGS_VARIANT_TYPE_COLOR)); /* paranoia... */
+ xsettings_manager_set_setting (manager, name, 0, tmp);
+}
+
+void
+xsettings_manager_delete_setting (XSettingsManager *manager,
+ const char *name)
+{
+ xsettings_manager_set_setting (manager, name, 0, NULL);
+}
+
+static gchar
+xsettings_get_typecode (GVariant *value)
+{
+ switch (g_variant_classify (value))
+ {
+ case G_VARIANT_CLASS_INT32:
+ return XSETTINGS_TYPE_INT;
+ case G_VARIANT_CLASS_STRING:
+ return XSETTINGS_TYPE_STRING;
+ case G_VARIANT_CLASS_TUPLE:
+ return XSETTINGS_TYPE_COLOR;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+align_string (GString *string,
+ gint alignment)
+{
+ /* Adds nul-bytes to the string until its length is an even multiple
+ * of the specified alignment requirement.
+ */
+ while ((string->len % alignment) != 0)
+ g_string_append_c (string, '\0');
+}
+
+static void
+setting_store (XSettingsSetting *setting,
+ GString *buffer)
+{
+ XSettingsType type;
+ GVariant *value;
+ guint16 len16;
+
+ value = xsettings_setting_get (setting);
+
+ type = xsettings_get_typecode (value);
+
+ g_string_append_c (buffer, type);
+ g_string_append_c (buffer, 0);
+
+ len16 = strlen (setting->name);
+ g_string_append_len (buffer, (gchar *) &len16, 2);
+ g_string_append (buffer, setting->name);
+ align_string (buffer, 4);
+
+ g_string_append_len (buffer, (gchar *) &setting->last_change_serial, 4);
+
+ if (type == XSETTINGS_TYPE_STRING)
+ {
+ const gchar *string;
+ gsize stringlen;
+ guint32 len32;
+
+ string = g_variant_get_string (value, &stringlen);
+ len32 = stringlen;
+ g_string_append_len (buffer, (gchar *) &len32, 4);
+ g_string_append (buffer, string);
+ align_string (buffer, 4);
+ }
+ else
+ /* GVariant format is the same as XSETTINGS format for the non-string types */
+ g_string_append_len (buffer, g_variant_get_data (value), g_variant_get_size (value));
+}
+
+void
+xsettings_manager_notify (XSettingsManager *manager)
+{
+ GString *buffer;
+ GHashTableIter iter;
+ int n_settings;
+ gpointer value;
+
+ n_settings = g_hash_table_size (manager->settings);
+
+ buffer = g_string_new (NULL);
+ g_string_append_c (buffer, xsettings_byte_order ());
+ g_string_append_c (buffer, '\0');
+ g_string_append_c (buffer, '\0');
+ g_string_append_c (buffer, '\0');
+
+ g_string_append_len (buffer, (gchar *) &manager->serial, 4);
+ g_string_append_len (buffer, (gchar *) &n_settings, 4);
+
+ g_hash_table_iter_init (&iter, manager->settings);
+ while (g_hash_table_iter_next (&iter, NULL, &value))
+ setting_store (value, buffer);
+
+ XChangeProperty (manager->display, manager->window,
+ manager->xsettings_atom, manager->xsettings_atom,
+ 8, PropModeReplace, (guchar *) buffer->str, buffer->len);
+
+ g_string_free (buffer, TRUE);
+ manager->serial++;
+}
+
+void
+xsettings_manager_set_overrides (XSettingsManager *manager,
+ GVariant *overrides)
+{
+ GVariantIter iter;
+ const gchar *key;
+ GVariant *value;
+
+ g_return_if_fail (overrides != NULL && g_variant_is_of_type (overrides, G_VARIANT_TYPE_VARDICT));
+
+ if (manager->overrides)
+ {
+ /* unset the existing overrides */
+
+ g_variant_iter_init (&iter, manager->overrides);
+ while (g_variant_iter_next (&iter, "{&sv}", &key, NULL))
+ /* only unset it at this point if it's not in the new list */
+ if (!g_variant_lookup (overrides, key, "*", NULL))
+ xsettings_manager_set_setting (manager, key, 1, NULL);
+ g_variant_unref (manager->overrides);
+ }
+
+ /* save this so we can do the unsets next time */
+ manager->overrides = g_variant_ref_sink (overrides);
+
+ /* set the new values */
+ g_variant_iter_init (&iter, overrides);
+ while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
+ {
+ /* only accept recognised types... */
+ if (!g_variant_is_of_type (value, G_VARIANT_TYPE_STRING) &&
+ !g_variant_is_of_type (value, G_VARIANT_TYPE_INT32) &&
+ !g_variant_is_of_type (value, XSETTINGS_VARIANT_TYPE_COLOR))
+ continue;
+
+ xsettings_manager_set_setting (manager, key, 1, value);
+ }
+}