[gtk+/native-layout] Make DND work better with shaped windows



commit fe7af7a9e5aa8adb8e3f0c54eb6a15f28718d1da
Author: Matthias Clasen <mclasen redhat com>
Date:   Thu Feb 4 19:15:54 2010 -0500

    Make DND work better with shaped windows
    
    If mouse clicks go through, drag-and-drop should too...
    Fixes bug 608615.

 gdk/x11/gdkdisplay-x11.c |    4 +-
 gdk/x11/gdkdisplay-x11.h |    1 +
 gdk/x11/gdkdnd-x11.c     |  148 +++++++++++++++++++++++++++++++++++++++++-----
 gdk/x11/gdkprivate-x11.h |    4 +
 gdk/x11/gdkwindow-x11.c  |   14 ++--
 5 files changed, 146 insertions(+), 25 deletions(-)
---
diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c
index 2b10a93..c0cbc99 100644
--- a/gdk/x11/gdkdisplay-x11.c
+++ b/gdk/x11/gdkdisplay-x11.c
@@ -280,10 +280,10 @@ gdk_display_open (const gchar *display_name)
   display_x11->have_shapes = FALSE;
   display_x11->have_input_shapes = FALSE;
 
-  if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (display), &ignore, &ignore))
+  if (XShapeQueryExtension (GDK_DISPLAY_XDISPLAY (display), &display_x11->shape_event_base, &ignore))
     {
       display_x11->have_shapes = TRUE;
-#ifdef ShapeInput	      
+#ifdef ShapeInput
       if (XShapeQueryVersion (GDK_DISPLAY_XDISPLAY (display), &maj, &min))
 	display_x11->have_input_shapes = (maj == 1 && min >= 1);
 #endif
diff --git a/gdk/x11/gdkdisplay-x11.h b/gdk/x11/gdkdisplay-x11.h
index 2873c2d..f8a55d0 100644
--- a/gdk/x11/gdkdisplay-x11.h
+++ b/gdk/x11/gdkdisplay-x11.h
@@ -147,6 +147,7 @@ struct _GdkDisplayX11
 
   guint have_shapes : 1;
   guint have_input_shapes : 1;
+  gint shape_event_base;
 
   /* Alpha mask picture format */
   XRenderPictFormat *mask_format;
diff --git a/gdk/x11/gdkdnd-x11.c b/gdk/x11/gdkdnd-x11.c
index 938e7e1..2da9567 100644
--- a/gdk/x11/gdkdnd-x11.c
+++ b/gdk/x11/gdkdnd-x11.c
@@ -27,6 +27,8 @@
 #include "config.h"
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
+#include <X11/extensions/shape.h>
+
 #include <string.h>
 
 #include "gdk.h"          /* For gdk_flush() */
@@ -53,6 +55,9 @@ typedef struct {
   guint32 xid;
   gint x, y, width, height;
   gboolean mapped;
+  gboolean shape_selected;
+  gboolean shape_valid;
+  GdkRegion *shape;
 } GdkCacheChild;
 
 typedef struct {
@@ -309,6 +314,23 @@ precache_target_list (GdkDragContext *context)
 /* Utility functions */
 
 static void
+free_cache_child (GdkCacheChild *child,
+                  GdkDisplay    *display)
+{
+  if (child->shape)
+    gdk_region_destroy (child->shape);
+
+  if (child->shape_selected && display)
+    {
+      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+      XShapeSelectInput (display_x11->xdisplay, child->xid, 0);
+    }
+
+  g_free (child);
+}
+
+static void
 gdk_window_cache_add (GdkWindowCache *cache,
 		      guint32 xid,
 		      gint x, gint y, gint width, gint height, 
@@ -322,6 +344,9 @@ gdk_window_cache_add (GdkWindowCache *cache,
   child->width = width;
   child->height = height;
   child->mapped = mapped;
+  child->shape_selected = FALSE;
+  child->shape_valid = FALSE;
+  child->shape = NULL;
 
   cache->children = g_list_prepend (cache->children, child);
   g_hash_table_insert (cache->child_hash, GUINT_TO_POINTER (xid), 
@@ -329,6 +354,41 @@ gdk_window_cache_add (GdkWindowCache *cache,
 }
 
 static GdkFilterReturn
+gdk_window_cache_shape_filter (GdkXEvent *xev,
+                               GdkEvent  *event,
+                               gpointer   data)
+{
+  XEvent *xevent = (XEvent *)xev;
+  GdkWindowCache *cache = data;
+
+  GdkDisplayX11 *display = GDK_DISPLAY_X11 (gdk_screen_get_display (cache->screen));
+
+  if (display->have_shapes &&
+      xevent->type == display->shape_event_base + ShapeNotify)
+    {
+      XShapeEvent *xse = (XShapeEvent*)xevent;
+      GList *node;
+
+      node = g_hash_table_lookup (cache->child_hash,
+                                  GUINT_TO_POINTER (xse->window));
+      if (node)
+        {
+          GdkCacheChild *child = node->data;
+          child->shape_valid = FALSE;
+          if (child->shape)
+            {
+              gdk_region_destroy (child->shape);
+              child->shape = NULL;
+            }
+        }
+
+      return GDK_FILTER_REMOVE;
+    }
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static GdkFilterReturn
 gdk_window_cache_filter (GdkXEvent *xev,
 			 GdkEvent  *event,
 			 gpointer   data)
@@ -403,10 +463,13 @@ gdk_window_cache_filter (GdkXEvent *xev,
 				    GUINT_TO_POINTER (xdwe->window));
 	if (node) 
 	  {
+	    GdkCacheChild *child = node->data;
+
 	    g_hash_table_remove (cache->child_hash,
 				 GUINT_TO_POINTER (xdwe->window));
 	    cache->children = g_list_remove_link (cache->children, node);
-	    g_free (node->data);
+	    /* window is destroyed, no need to disable ShapeNotify */
+	    free_cache_child (child, NULL);
 	    g_list_free_1 (node);
 	  }
 	break;
@@ -434,7 +497,7 @@ gdk_window_cache_filter (GdkXEvent *xev,
 
 	node = g_hash_table_lookup (cache->child_hash, 
 				    GUINT_TO_POINTER (xume->window));
-	if (node) 
+	if (node)
 	  {
 	    GdkCacheChild *child = node->data;
 	    child->mapped = FALSE;
@@ -486,6 +549,7 @@ gdk_window_cache_new (GdkScreen *screen)
   XSelectInput (xdisplay, GDK_WINDOW_XWINDOW (root_window),
 		result->old_event_mask | SubstructureNotifyMask);
   gdk_window_add_filter (root_window, gdk_window_cache_filter, result);
+  gdk_window_add_filter (NULL, gdk_window_cache_shape_filter, result);
 
   if (!_gdk_x11_get_window_child_info (gdk_screen_get_display (screen),
 				       GDK_WINDOW_XWINDOW (root_window),
@@ -514,14 +578,57 @@ gdk_window_cache_destroy (GdkWindowCache *cache)
 		GDK_WINDOW_XWINDOW (root_window),
 		cache->old_event_mask);
   gdk_window_remove_filter (root_window, gdk_window_cache_filter, cache);
+  gdk_window_remove_filter (NULL, gdk_window_cache_shape_filter, cache);
 
-  g_list_foreach (cache->children, (GFunc)g_free, NULL);
+  g_list_foreach (cache->children, (GFunc)free_cache_child, 
+      gdk_screen_get_display (cache->screen));
   g_list_free (cache->children);
   g_hash_table_destroy (cache->child_hash);
 
   g_free (cache);
 }
 
+static gboolean
+is_pointer_within_shape (GdkDisplay    *display,
+                         GdkCacheChild *child,
+                         gint           x_pos,
+                         gint           y_pos)
+{
+  if (!child->shape_selected)
+    {
+      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+
+      XShapeSelectInput (display_x11->xdisplay, child->xid, ShapeNotifyMask);
+      child->shape_selected = TRUE;
+    }
+  if (!child->shape_valid)
+    {
+      GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
+      GdkRegion *input_shape;
+
+      child->shape = _xwindow_get_shape (display_x11->xdisplay,
+                                         child->xid, ShapeBounding);
+#ifdef ShapeInput
+      input_shape = _xwindow_get_shape (display_x11->xdisplay,
+                                        child->xid, ShapeInput);
+      if (child->shape && input_shape)
+        {
+          gdk_region_intersect (child->shape, input_shape);
+          gdk_region_destroy (input_shape);
+        }
+      else if (input_shape)
+        {
+          child->shape = input_shape;
+        }
+#endif
+
+      child->shape_valid = TRUE;
+    }
+
+  return child->shape == NULL ||
+         gdk_region_point_in (child->shape, x_pos, y_pos);
+}
+
 static Window
 get_client_window_at_coords_recurse (GdkDisplay *display,
 				     Window      win,
@@ -594,19 +701,28 @@ get_client_window_at_coords (GdkWindowCache *cache,
       GdkCacheChild *child = tmp_list->data;
 
       if ((child->xid != ignore) && (child->mapped))
-	{
-	  if ((x_root >= child->x) && (x_root < child->x + child->width) &&
-	      (y_root >= child->y) && (y_root < child->y + child->height))
-	    {
-	      retval = get_client_window_at_coords_recurse (gdk_screen_get_display (cache->screen),
-							    child->xid, TRUE,
-							    x_root - child->x,
-							    y_root - child->y);
-	      if (!retval)
-		retval = child->xid;
-	    }
-	  
-	}
+        {
+          if ((x_root >= child->x) && (x_root < child->x + child->width) &&
+              (y_root >= child->y) && (y_root < child->y + child->height))
+            {
+              GdkDisplay *display = gdk_screen_get_display (cache->screen);
+
+              if (!is_pointer_within_shape (display, child,
+                                            x_root - child->x,
+                                            y_root - child->y))
+                {
+                  tmp_list = tmp_list->next;
+                  continue;
+                }
+
+              retval = get_client_window_at_coords_recurse (display,
+                  child->xid, TRUE,
+                  x_root - child->x,
+                  y_root - child->y);
+              if (!retval)
+                retval = child->xid;
+            }
+        }
       tmp_list = tmp_list->next;
     }
 
diff --git a/gdk/x11/gdkprivate-x11.h b/gdk/x11/gdkprivate-x11.h
index bfbf3d9..5eaf188 100644
--- a/gdk/x11/gdkprivate-x11.h
+++ b/gdk/x11/gdkprivate-x11.h
@@ -138,6 +138,10 @@ void     _gdk_x11_window_queue_translation (GdkWindow *window,
 void     _gdk_selection_window_destroyed   (GdkWindow            *window);
 gboolean _gdk_selection_filter_clear_event (XSelectionClearEvent *event);
 
+GdkRegion* _xwindow_get_shape              (Display *xdisplay,
+                                            Window window,
+                                            gint shape_type);
+
 void     _gdk_region_get_xrectangles       (const GdkRegion      *region,
                                             gint                  x_offset,
                                             gint                  y_offset,
diff --git a/gdk/x11/gdkwindow-x11.c b/gdk/x11/gdkwindow-x11.c
index 9c46716..c6d608c 100644
--- a/gdk/x11/gdkwindow-x11.c
+++ b/gdk/x11/gdkwindow-x11.c
@@ -4593,10 +4593,10 @@ gdk_window_set_functions (GdkWindow    *window,
   gdk_window_set_mwm_hints (window, &hints);
 }
 
-static GdkRegion *
-xwindow_get_shape (Display *xdisplay,
-		   Window window,
-		   gint shape_type)
+GdkRegion *
+_xwindow_get_shape (Display *xdisplay,
+		    Window window,
+		    gint shape_type)
 {
   GdkRegion *shape;
   GdkRectangle *rl;
@@ -4658,7 +4658,7 @@ _gdk_windowing_get_shape_for_mask (GdkBitmap *mask)
 		     GDK_PIXMAP_XID (mask),
 		     ShapeSet);
   
-  region = xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display),
+  region = _xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display),
 			      window, ShapeBounding);
 
   XDestroyWindow (GDK_DISPLAY_XDISPLAY (display),
@@ -4672,7 +4672,7 @@ _gdk_windowing_window_get_shape (GdkWindow *window)
 {
   if (!GDK_WINDOW_DESTROYED (window) &&
       gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
-    return xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+    return _xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
 			      GDK_WINDOW_XID (window), ShapeBounding);
 
   return NULL;
@@ -4684,7 +4684,7 @@ _gdk_windowing_window_get_input_shape (GdkWindow *window)
 #if defined(ShapeInput)
   if (!GDK_WINDOW_DESTROYED (window) &&
       gdk_display_supports_shapes (GDK_WINDOW_DISPLAY (window)))
-    return xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
+    return _xwindow_get_shape (GDK_WINDOW_XDISPLAY (window),
 			      GDK_WINDOW_XID (window),
 			      ShapeInput);
 #endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]