]> Pileus Git - ~andy/gtk/blobdiff - gdk/x11/xsettings-client.c
x11: Remove XSettingsAction type
[~andy/gtk] / gdk / x11 / xsettings-client.c
index a54dbea8b5150efaa6ea7476990c56594080e25d..479ac9d865501e96854cc6efe33a89d98c724a61 100644 (file)
 #include <gdk/x11/gdkx11display.h>
 #include <gdk/x11/gdkx11screen.h>
 #include <gdk/x11/gdkx11window.h>
+#include <gdk/x11/gdkscreen-x11.h>
 
 #include <gdkinternals.h>
 
-#include <limits.h>
-#include <stdio.h>
 #include <string.h>
 
 #include <X11/Xlib.h>
 #include <X11/Xmd.h>           /* For CARD16 */
 
-typedef struct _XSettingsBuffer  XSettingsBuffer;
+#include "gdksettings.c"
 
-typedef enum
-{
-  XSETTINGS_SUCCESS,
-  XSETTINGS_ACCESS,
-  XSETTINGS_FAILED,
-  XSETTINGS_NO_ENTRY,
-  XSETTINGS_DUPLICATE_ENTRY
-} XSettingsResult;
+typedef struct _XSettingsBuffer  XSettingsBuffer;
 
 struct _XSettingsBuffer
 {
@@ -61,9 +53,6 @@ struct _XSettingsClient
 {
   GdkScreen *screen;
   Display *display;
-  XSettingsNotifyFunc notify;
-  XSettingsWatchFunc watch;
-  void *cb_data;
 
   Window manager_window;
   Atom manager_atom;
@@ -73,31 +62,53 @@ struct _XSettingsClient
   GHashTable *settings; /* string => XSettingsSetting */
 };
 
+static void
+gdk_xsettings_notify (const char       *name,
+                     GdkSettingAction  action,
+                     XSettingsSetting *setting,
+                     GdkScreen        *screen)
+{
+  GdkEvent new_event;
+  GdkX11Screen *x11_screen = GDK_X11_SCREEN (screen);
+
+  if (x11_screen->xsettings_in_init)
+    return;
+  
+  new_event.type = GDK_SETTING;
+  new_event.setting.window = gdk_screen_get_root_window (screen);
+  new_event.setting.send_event = FALSE;
+  new_event.setting.action = action;
+  new_event.setting.name = (char*) gdk_from_xsettings_name (name);
+
+  if (!new_event.setting.name)
+    return;
+
+  gdk_event_put (&new_event);
+}
+
 static void
 notify_changes (XSettingsClient *client,
                GHashTable      *old_list)
 {
   GHashTableIter iter;
   XSettingsSetting *setting, *old_setting;
-
-  if (!client->notify)
-    return;
+  const char *name;
 
   if (client->settings != NULL)
     {
       g_hash_table_iter_init (&iter, client->settings);
-      while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &setting))
+      while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &setting))
        {
-         old_setting = old_list ? g_hash_table_lookup (old_list, setting->name) : NULL;
+         old_setting = old_list ? g_hash_table_lookup (old_list, name) : NULL;
 
          if (old_setting == NULL)
-           client->notify (setting->name, XSETTINGS_ACTION_NEW, setting, client->cb_data);
+           gdk_xsettings_notify (name, GDK_SETTING_ACTION_NEW, setting, client->screen);
          else if (!xsettings_setting_equal (setting, old_setting))
-           client->notify (setting->name, XSETTINGS_ACTION_CHANGED, setting, client->cb_data);
+           gdk_xsettings_notify (name, GDK_SETTING_ACTION_CHANGED, setting, client->screen);
            
          /* remove setting from old_list */
          if (old_setting != NULL)
-           g_hash_table_remove (old_list, setting->name);
+           g_hash_table_remove (old_list, name);
        }
     }
 
@@ -105,21 +116,29 @@ notify_changes (XSettingsClient *client,
     {
       /* old_list now contains only deleted settings */
       g_hash_table_iter_init (&iter, old_list);
-      while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &old_setting))
-       client->notify (old_setting->name, XSETTINGS_ACTION_DELETED, NULL, client->cb_data);
+      while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer*) &old_setting))
+       gdk_xsettings_notify (name, GDK_SETTING_ACTION_DELETED, NULL, client->screen);
     }
 }
 
 #define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos)
 
-static XSettingsResult
+#define return_if_fail_bytes(buffer, n_bytes) G_STMT_START{ \
+  if (BYTES_LEFT (buffer) < (n_bytes)) \
+    { \
+      g_warning ("Invalid XSETTINGS property (read off end: Expected %u bytes, only %ld left", \
+                 (n_bytes), BYTES_LEFT (buffer)); \
+      return FALSE; \
+    } \
+}G_STMT_END
+
+static gboolean
 fetch_card16 (XSettingsBuffer *buffer,
              CARD16          *result)
 {
   CARD16 x;
 
-  if (BYTES_LEFT (buffer) < 2)
-    return XSETTINGS_ACCESS;
+  return_if_fail_bytes (buffer, 2);
 
   x = *(CARD16 *)buffer->pos;
   buffer->pos += 2;
@@ -129,31 +148,30 @@ fetch_card16 (XSettingsBuffer *buffer,
   else
     *result = GUINT16_FROM_LE (x);
 
-  return XSETTINGS_SUCCESS;
+  return TRUE;
 }
 
-static XSettingsResult
+static gboolean
 fetch_ushort (XSettingsBuffer *buffer,
              unsigned short  *result) 
 {
   CARD16 x;
-  XSettingsResult r;  
+  gboolean r;  
 
   r = fetch_card16 (buffer, &x);
-  if (r == XSETTINGS_SUCCESS)
+  if (r)
     *result = x;
 
   return r;
 }
 
-static XSettingsResult
+static gboolean
 fetch_card32 (XSettingsBuffer *buffer,
              CARD32          *result)
 {
   CARD32 x;
 
-  if (BYTES_LEFT (buffer) < 4)
-    return XSETTINGS_ACCESS;
+  return_if_fail_bytes (buffer, 4);
 
   x = *(CARD32 *)buffer->pos;
   buffer->pos += 4;
@@ -163,25 +181,24 @@ fetch_card32 (XSettingsBuffer *buffer,
   else
     *result = GUINT32_FROM_LE (x);
   
-  return XSETTINGS_SUCCESS;
+  return TRUE;
 }
 
-static XSettingsResult
+static gboolean
 fetch_card8 (XSettingsBuffer *buffer,
             CARD8           *result)
 {
-  if (BYTES_LEFT (buffer) < 1)
-    return XSETTINGS_ACCESS;
+  return_if_fail_bytes (buffer, 1);
 
   *result = *(CARD8 *)buffer->pos;
   buffer->pos += 1;
 
-  return XSETTINGS_SUCCESS;
+  return TRUE;
 }
 
 #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1)))
 
-static XSettingsResult
+static gboolean
 fetch_string (XSettingsBuffer  *buffer,
               guint             length,
               char            **result)
@@ -189,14 +206,18 @@ fetch_string (XSettingsBuffer  *buffer,
   guint pad_len;
 
   pad_len = XSETTINGS_PAD (length, 4);
-  if (pad_len < length /* guard against overflow */
-      || BYTES_LEFT (buffer) < pad_len)
-    return XSETTINGS_ACCESS;
+  if (pad_len < length) /* guard against overflow */
+    {
+      g_warning ("Invalid XSETTINGS property (overflow in string length)");
+      return FALSE;
+    }
+
+  return_if_fail_bytes (buffer, pad_len);
 
   *result = g_strndup ((char *) buffer->pos, length);
   buffer->pos += pad_len;
 
-  return XSETTINGS_SUCCESS;
+  return TRUE;
 }
 
 static GHashTable *
@@ -204,33 +225,30 @@ parse_settings (unsigned char *data,
                size_t         len)
 {
   XSettingsBuffer buffer;
-  XSettingsResult result = XSETTINGS_SUCCESS;
   GHashTable *settings = NULL;
   CARD32 serial;
   CARD32 n_entries;
   CARD32 i;
   XSettingsSetting *setting = NULL;
+  char *name;
   
   buffer.pos = buffer.data = data;
   buffer.len = len;
   
-  result = fetch_card8 (&buffer, (unsigned char *)&buffer.byte_order);
+  if (!fetch_card8 (&buffer, (unsigned char *)&buffer.byte_order))
+    goto out;
+
   if (buffer.byte_order != MSBFirst &&
       buffer.byte_order != LSBFirst)
     {
-      fprintf (stderr, "Invalid byte order in XSETTINGS property\n");
-      result = XSETTINGS_FAILED;
+      g_warning ("Invalid XSETTINGS property (unknown byte order %u)", buffer.byte_order);
       goto out;
     }
 
   buffer.pos += 3;
 
-  result = fetch_card32 (&buffer, &serial);
-  if (result != XSETTINGS_SUCCESS)
-    goto out;
-
-  result = fetch_card32 (&buffer, &n_entries);
-  if (result != XSETTINGS_SUCCESS)
+  if (!fetch_card32 (&buffer, &serial) ||
+      !fetch_card32 (&buffer, &n_entries))
     goto out;
 
   GDK_NOTE(SETTINGS, g_print("reading %u settings (serial %u byte order %u)\n", n_entries, serial, buffer.byte_order));
@@ -241,71 +259,52 @@ parse_settings (unsigned char *data,
       CARD16 name_len;
       CARD32 v_int;
       
-      result = fetch_card8 (&buffer, &type);
-      if (result != XSETTINGS_SUCCESS)
+      if (!fetch_card8 (&buffer, &type))
        goto out;
 
       buffer.pos += 1;
 
-      result = fetch_card16 (&buffer, &name_len);
-      if (result != XSETTINGS_SUCCESS)
+      if (!fetch_card16 (&buffer, &name_len))
        goto out;
 
       setting = g_new (XSettingsSetting, 1);
       setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */
 
-      setting->name = NULL;
-      result = fetch_string (&buffer, name_len, &setting->name);
-      if (result != XSETTINGS_SUCCESS)
-       goto out;
-
-      /* last change serial (we ignore it) */
-      result = fetch_card32 (&buffer, &v_int);
-      if (result != XSETTINGS_SUCCESS)
+      if (!fetch_string (&buffer, name_len, &name) ||
+          /* last change serial (we ignore it) */
+          !fetch_card32 (&buffer, &v_int))
        goto out;
 
       switch (type)
        {
        case XSETTINGS_TYPE_INT:
-         result = fetch_card32 (&buffer, &v_int);
-         if (result != XSETTINGS_SUCCESS)
+         if (!fetch_card32 (&buffer, &v_int))
            goto out;
 
          setting->data.v_int = (INT32)v_int;
-          GDK_NOTE(SETTINGS, g_print("  %s = %d\n", setting->name, (gint) setting->data.v_int));
+          GDK_NOTE(SETTINGS, g_print("  %s = %d\n", name, (gint) setting->data.v_int));
          break;
        case XSETTINGS_TYPE_STRING:
-         result = fetch_card32 (&buffer, &v_int);
-         if (result != XSETTINGS_SUCCESS)
+         if (!fetch_card32 (&buffer, &v_int) ||
+              !fetch_string (&buffer, v_int, &setting->data.v_string))
            goto out;
-
-          result = fetch_string (&buffer, v_int, &setting->data.v_string);
-          if (result != XSETTINGS_SUCCESS)
-            goto out;
          
-          GDK_NOTE(SETTINGS, g_print("  %s = \"%s\"\n", setting->name, setting->data.v_string));
+          GDK_NOTE(SETTINGS, g_print("  %s = \"%s\"\n", name, setting->data.v_string));
          break;
        case XSETTINGS_TYPE_COLOR:
-         result = fetch_ushort (&buffer, &setting->data.v_color.red);
-         if (result != XSETTINGS_SUCCESS)
-           goto out;
-         result = fetch_ushort (&buffer, &setting->data.v_color.green);
-         if (result != XSETTINGS_SUCCESS)
-           goto out;
-         result = fetch_ushort (&buffer, &setting->data.v_color.blue);
-         if (result != XSETTINGS_SUCCESS)
-           goto out;
-         result = fetch_ushort (&buffer, &setting->data.v_color.alpha);
-         if (result != XSETTINGS_SUCCESS)
+         if (!fetch_ushort (&buffer, &setting->data.v_color.red) ||
+             !fetch_ushort (&buffer, &setting->data.v_color.green) ||
+             !fetch_ushort (&buffer, &setting->data.v_color.blue) ||
+             !fetch_ushort (&buffer, &setting->data.v_color.alpha))
            goto out;
 
-          GDK_NOTE(SETTINGS, g_print("  %s = #%02X%02X%02X%02X\n", setting->name, 
+          GDK_NOTE(SETTINGS, g_print("  %s = #%02X%02X%02X%02X\n", name, 
                                  setting->data.v_color.alpha, setting->data.v_color.red,
                                  setting->data.v_color.green, setting->data.v_color.blue));
          break;
        default:
          /* Quietly ignore unknown types */
-          GDK_NOTE(SETTINGS, g_print("  %s = ignored (unknown type %u)\n", setting->name, type));
+          GDK_NOTE(SETTINGS, g_print("  %s = ignored (unknown type %u)\n", name, type));
          break;
        }
 
@@ -313,45 +312,31 @@ parse_settings (unsigned char *data,
 
       if (settings == NULL)
         settings = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                          NULL,
+                                          g_free,
                                           (GDestroyNotify) xsettings_setting_free);
 
-      if (g_hash_table_lookup (settings, setting->name) != NULL)
+      if (g_hash_table_lookup (settings, name) != NULL)
         {
-          result = XSETTINGS_DUPLICATE_ENTRY;
+         g_warning ("Invalid XSETTINGS property (Duplicate entry for '%s')", name);
           goto out;
         }
 
-      g_hash_table_insert (settings, setting->name, setting);
+      g_hash_table_insert (settings, name, setting);
       setting = NULL;
+      name = NULL;
     }
 
- out:
+  return settings;
 
-  if (result != XSETTINGS_SUCCESS)
-    {
-      switch (result)
-       {
-       case XSETTINGS_ACCESS:
-         fprintf(stderr, "Invalid XSETTINGS property (read off end)\n");
-         break;
-       case XSETTINGS_DUPLICATE_ENTRY:
-         fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name);
-       case XSETTINGS_FAILED:
-       case XSETTINGS_SUCCESS:
-       case XSETTINGS_NO_ENTRY:
-         break;
-       }
+ out:
 
-      if (setting)
-       xsettings_setting_free (setting);
+  if (setting)
+    xsettings_setting_free (setting);
 
-      if (settings)
-        g_hash_table_unref (settings);
-      settings = NULL;
-    }
+  if (settings)
+    g_hash_table_unref (settings);
 
-  return settings;
+  return NULL;
 }
 
 static void
@@ -410,11 +395,17 @@ add_events (Display *display,
   XSelectInput (display, window, attr.your_event_mask | mask);
 }
 
+static Bool
+gdk_xsettings_watch (Window     window,
+                    Bool       is_start,
+                    long       mask,
+                    GdkScreen *screen);
+
 static void
 check_manager_window (XSettingsClient *client)
 {
-  if (client->manager_window && client->watch)
-    client->watch (client->manager_window, False, 0, client->cb_data);
+  if (client->manager_window)
+    gdk_xsettings_watch (client->manager_window, False, 0, client->screen);
 
   gdk_x11_display_grab (gdk_screen_get_display (client->screen));
 
@@ -428,11 +419,11 @@ check_manager_window (XSettingsClient *client)
   
   XFlush (client->display);
 
-  if (client->manager_window && client->watch)
+  if (client->manager_window)
     {
-      if (!client->watch (client->manager_window, True, 
-                         PropertyChangeMask | StructureNotifyMask,
-                         client->cb_data))
+      if (!gdk_xsettings_watch (client->manager_window, True, 
+                                PropertyChangeMask | StructureNotifyMask,
+                                client->screen))
        {
          /* Inability to watch the window probably means that it was destroyed
           * after we ungrabbed
@@ -442,15 +433,100 @@ check_manager_window (XSettingsClient *client)
        }
     }
       
-  
   read_settings (client);
 }
 
+static GdkFilterReturn
+gdk_xsettings_client_event_filter (GdkXEvent *xevent,
+                                  GdkEvent  *event,
+                                  gpointer   data)
+{
+  GdkScreen *screen = data;
+  XSettingsClient *client = GDK_X11_SCREEN (screen)->xsettings_client;
+  XEvent *xev = xevent;
+
+  /* The checks here will not unlikely cause us to reread
+   * the properties from the manager window a number of
+   * times when the manager changes from A->B. But manager changes
+   * are going to be pretty rare.
+   */
+  if (xev->xany.window == gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)))
+    {
+      if (xev->xany.type == ClientMessage &&
+         xev->xclient.message_type == client->manager_atom &&
+         xev->xclient.data.l[1] == client->selection_atom)
+       {
+         check_manager_window (client);
+         return GDK_FILTER_REMOVE;
+       }
+    }
+  else if (xev->xany.window == client->manager_window)
+    {
+      if (xev->xany.type == DestroyNotify)
+       {
+         check_manager_window (client);
+          /* let GDK do its cleanup */
+         return GDK_FILTER_CONTINUE; 
+       }
+      else if (xev->xany.type == PropertyNotify)
+       {
+         read_settings (client);
+         return GDK_FILTER_REMOVE;
+       }
+    }
+  
+  return GDK_FILTER_CONTINUE;;
+}
+
+static Bool
+gdk_xsettings_watch (Window     window,
+                    Bool       is_start,
+                    long       mask,
+                    GdkScreen *screen)
+{
+  GdkWindow *gdkwin;
+
+  gdkwin = gdk_x11_window_lookup_for_display (gdk_screen_get_display (screen), window);
+
+  if (is_start)
+    {
+      if (gdkwin)
+       g_object_ref (gdkwin);
+      else
+       {
+         gdkwin = gdk_x11_window_foreign_new_for_display (gdk_screen_get_display (screen), window);
+         
+         /* gdk_window_foreign_new_for_display() can fail and return NULL if the
+          * window has already been destroyed.
+          */
+         if (!gdkwin)
+           return False;
+       }
+
+      gdk_window_add_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
+    }
+  else
+    {
+      if (!gdkwin)
+       {
+         /* gdkwin should not be NULL here, since if starting the watch succeeded
+          * we have a reference on the window. It might mean that the caller didn't
+          * remove the watch when it got a DestroyNotify event. Or maybe the
+          * caller ignored the return value when starting the watch failed.
+          */
+         g_warning ("gdk_xsettings_watch_cb(): Couldn't find window to unwatch");
+         return False;
+       }
+      
+      gdk_window_remove_filter (gdkwin, gdk_xsettings_client_event_filter, screen);
+      g_object_unref (gdkwin);
+    }
+
+  return True;
+}
+
 XSettingsClient *
-xsettings_client_new (GdkScreen           *screen,
-                     XSettingsNotifyFunc  notify,
-                     XSettingsWatchFunc   watch,
-                     void                *cb_data)
+xsettings_client_new (GdkScreen *screen)
 {
   XSettingsClient *client;
   char buffer[256];
@@ -463,9 +539,7 @@ xsettings_client_new (GdkScreen           *screen,
 
   client->screen = screen;
   client->display = gdk_x11_display_get_xdisplay (gdk_screen_get_display (screen));
-  client->notify = notify;
-  client->watch = watch;
-  client->cb_data = cb_data;
+  client->screen = screen;
   client->manager_window = None;
   client->settings = NULL;
 
@@ -484,9 +558,8 @@ xsettings_client_new (GdkScreen           *screen,
    */
   add_events (client->display, gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)), StructureNotifyMask);
 
-  if (client->watch)
-    client->watch (gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)), True, StructureNotifyMask,
-                  client->cb_data);
+  gdk_xsettings_watch (gdk_x11_window_get_xid (gdk_screen_get_root_window (screen)), True, StructureNotifyMask,
+                       client->screen);
 
   check_manager_window (client);
 
@@ -496,11 +569,10 @@ xsettings_client_new (GdkScreen           *screen,
 void
 xsettings_client_destroy (XSettingsClient *client)
 {
-  if (client->watch)
-    client->watch (gdk_x11_window_get_xid (gdk_screen_get_root_window (client->screen)),
-                  False, 0, client->cb_data);
-  if (client->manager_window && client->watch)
-    client->watch (client->manager_window, False, 0, client->cb_data);
+  gdk_xsettings_watch (gdk_x11_window_get_xid (gdk_screen_get_root_window (client->screen)),
+                      False, 0, client->screen);
+  if (client->manager_window)
+    gdk_xsettings_watch (client->manager_window, False, 0, client->screen);
   
   if (client->settings)
     g_hash_table_unref (client->settings);
@@ -514,43 +586,6 @@ xsettings_client_get_setting (XSettingsClient   *client,
   return g_hash_table_lookup (client->settings, name);
 }
 
-Bool
-xsettings_client_process_event (XSettingsClient *client,
-                               XEvent          *xev)
-{
-  /* The checks here will not unlikely cause us to reread
-   * the properties from the manager window a number of
-   * times when the manager changes from A->B. But manager changes
-   * are going to be pretty rare.
-   */
-  if (xev->xany.window == gdk_x11_window_get_xid (gdk_screen_get_root_window (client->screen)))
-    {
-      if (xev->xany.type == ClientMessage &&
-         xev->xclient.message_type == client->manager_atom &&
-         xev->xclient.data.l[1] == client->selection_atom)
-       {
-         check_manager_window (client);
-         return True;
-       }
-    }
-  else if (xev->xany.window == client->manager_window)
-    {
-      if (xev->xany.type == DestroyNotify)
-       {
-         check_manager_window (client);
-          /* let GDK do its cleanup */
-         return False; 
-       }
-      else if (xev->xany.type == PropertyNotify)
-       {
-         read_settings (client);
-         return True;
-       }
-    }
-  
-  return False;
-}
-
 int
 xsettings_setting_equal (XSettingsSetting *setting_a,
                         XSettingsSetting *setting_b)
@@ -558,9 +593,6 @@ xsettings_setting_equal (XSettingsSetting *setting_a,
   if (setting_a->type != setting_b->type)
     return 0;
 
-  if (strcmp (setting_a->name, setting_b->name) != 0)
-    return 0;
-
   switch (setting_a->type)
     {
     case XSETTINGS_TYPE_INT:
@@ -583,9 +615,6 @@ xsettings_setting_free (XSettingsSetting *setting)
   if (setting->type == XSETTINGS_TYPE_STRING)
     g_free (setting->data.v_string);
 
-  if (setting->name)
-    g_free (setting->name);
-  
   g_free (setting);
 }