]> Pileus Git - ~andy/gtk/commitdiff
Decouple GdkWindowCache life-cycle from GdkX11DragContext
authorMatthias Clasen <mclasen@redhat.com>
Wed, 5 Jan 2011 00:25:17 +0000 (19:25 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Wed, 5 Jan 2011 00:30:04 +0000 (19:30 -0500)
By making window caches refcounted. This fixes problems with leaking
drag contexts, as experienced in
https://bugzilla.gnome.org/show_bug.cgi?id=637691
and
https://bugzilla.gnome.org/show_bug.cgi?id=144324

Based on a patch by drago01@gmail.com

gdk/x11/gdkdnd-x11.c

index 0cb66c518a05931a8b950268b598654548bf4981..4b8ee69aefeff6a24fd84192d22c8c5b38de4f87 100644 (file)
@@ -68,6 +68,7 @@ typedef struct {
   GHashTable *child_hash;
   guint old_event_mask;
   GdkScreen *screen;
+  gint ref_count;
 } GdkWindowCache;
 
 
@@ -105,7 +106,9 @@ struct _GdkX11DragContextClass
 
 /* Forward declarations */
 
-static void gdk_window_cache_destroy (GdkWindowCache *cache);
+static GdkWindowCache *gdk_window_cache_get   (GdkScreen      *screen);
+static GdkWindowCache *gdk_window_cache_ref   (GdkWindowCache *cache);
+static void            gdk_window_cache_unref (GdkWindowCache *cache);
 
 static void motif_read_target_table (GdkDisplay *display);
 
@@ -137,6 +140,7 @@ static void   xdnd_manage_source_filter (GdkDragContext *context,
                                          gboolean        add_filter);
 
 static GList *contexts;
+static GSList *window_caches;
 
 static const struct {
   const char *atom_name;
@@ -212,8 +216,8 @@ gdk_x11_drag_context_class_init (GdkX11DragContextClass *klass)
 static void
 gdk_x11_drag_context_finalize (GObject *object)
 {
-  GdkX11DragContext *context_x11 = GDK_X11_DRAG_CONTEXT (object);
   GdkDragContext *context = GDK_DRAG_CONTEXT (object);
+  GdkX11DragContext *x11_context = GDK_X11_DRAG_CONTEXT (object);
 
   if (context->source_window)
     {
@@ -221,7 +225,8 @@ gdk_x11_drag_context_finalize (GObject *object)
         xdnd_manage_source_filter (context, context->source_window, FALSE);
     }
 
-  g_slist_free_full (context_x11->window_caches, (GDestroyNotify)gdk_window_cache_destroy);
+  g_slist_free_full (x11_context->window_caches, (GDestroyNotify)gdk_window_cache_unref);
+  x11_context->window_caches = NULL;
 
   contexts = g_list_remove (contexts, context);
 
@@ -509,6 +514,7 @@ gdk_window_cache_new (GdkScreen *screen)
   result->children = NULL;
   result->child_hash = g_hash_table_new (g_direct_hash, NULL);
   result->screen = screen;
+  result->ref_count = 1;
 
   XGetWindowAttributes (xdisplay, GDK_WINDOW_XID (root_window), &xwa);
   result->old_event_mask = xwa.your_event_mask;
@@ -595,6 +601,48 @@ gdk_window_cache_destroy (GdkWindowCache *cache)
   g_free (cache);
 }
 
+static GdkWindowCache *
+gdk_window_cache_ref (GdkWindowCache *cache)
+{
+  cache->ref_count += 1;
+
+  return cache;
+}
+
+static void
+gdk_window_cache_unref (GdkWindowCache *cache)
+{
+  g_assert (cache->ref_count > 0);
+
+  cache->ref_count -= 1;
+
+  if (cache->ref_count == 0)
+    {
+      window_caches = g_slist_remove (window_caches, cache);
+      gdk_window_cache_destroy (cache);
+    }
+}
+
+GdkWindowCache *
+gdk_window_cache_get (GdkScreen *screen)
+{
+  GSList *list;
+  GdkWindowCache *cache;
+
+  for (list = window_caches; list; list = list->next)
+    {
+      cache = list->data;
+      if (cache->screen == screen)
+        return gdk_window_cache_ref (cache);
+    }
+
+  cache = gdk_window_cache_new (screen);
+
+  window_caches = g_slist_prepend (window_caches, cache);
+
+  return cache;
+}
+
 static gboolean
 is_pointer_within_shape (GdkDisplay    *display,
                          GdkCacheChild *child,
@@ -3229,7 +3277,7 @@ drag_context_find_window_cache (GdkX11DragContext *context_x11,
         return cache;
     }
 
-  cache = gdk_window_cache_new (screen);
+  cache = gdk_window_cache_get (screen);
   context_x11->window_caches = g_slist_prepend (context_x11->window_caches, cache);
 
   return cache;