Blame plugins/xsettings/xsettings-manager.c

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