diff --git a/gdk/x11/gdkdisplay-x11.c b/gdk/x11/gdkdisplay-x11.c index 2b10a93618..c0cbc99f52 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 2873c2d674..f8a55d0103 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 938e7e14da..2da9567b61 100644 --- a/gdk/x11/gdkdnd-x11.c +++ b/gdk/x11/gdkdnd-x11.c @@ -27,6 +27,8 @@ #include "config.h" #include #include +#include + #include #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 { @@ -308,6 +313,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, @@ -322,12 +344,50 @@ 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), cache->children); } +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, @@ -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 bfbf3d9bdd..5eaf188ae2 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 9c46716444..c6d608cd28 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