Make DND work better with shaped windows

If mouse clicks go through, drag-and-drop should too...
Fixes bug 608615.
This commit is contained in:
Matthias Clasen 2010-02-04 19:15:54 -05:00
parent 0d94617935
commit 69aa7a6392
5 changed files with 146 additions and 25 deletions

View File

@ -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

View File

@ -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;

View File

@ -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 {
@ -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;
}

View File

@ -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,

View File

@ -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