Commit d7e369c2 authored by Alexander Larsson's avatar Alexander Larsson Committed by Alexander Larsson

Implement input shapes

parent e14e05ad
......@@ -666,6 +666,10 @@ gdk_window_shape_combine_mask
gdk_window_shape_combine_region
gdk_window_set_child_shapes
gdk_window_merge_child_shapes
gdk_window_input_shape_combine_mask
gdk_window_input_shape_combine_region
gdk_window_set_child_input_shapes
gdk_window_merge_child_input_shapes
gdk_window_set_static_gravities
gdk_window_reparent
gdk_window_add_filter
......@@ -749,8 +753,6 @@ gdk_window_set_startup_id
gdk_window_set_transient_for
gdk_window_get_root_origin
gdk_window_get_frame_extents
gdk_window_input_shape_combine_mask
gdk_window_input_shape_combine_region
gdk_window_set_override_redirect
gdk_window_set_accept_focus
gdk_window_set_focus_on_map
......@@ -774,8 +776,6 @@ gdk_window_set_group
gdk_window_get_decorations
gdk_window_set_decorations
gdk_window_set_functions
gdk_window_set_child_input_shapes
gdk_window_merge_child_input_shapes
gdk_window_begin_move_drag
gdk_window_begin_resize_drag
gdk_window_enable_synchronized_configure
......
......@@ -336,6 +336,7 @@ void _gdk_windowing_window_get_offsets (GdkWindow *window,
gint *x_offset,
gint *y_offset);
GdkRegion *_gdk_windowing_window_get_shape (GdkWindow *window);
GdkRegion *_gdk_windowing_window_get_input_shape(GdkWindow *window);
GdkRegion *_gdk_windowing_get_shape_for_mask (GdkBitmap *mask);
......
......@@ -1010,12 +1010,10 @@ gdk_offscreen_window_shape_combine_region (GdkWindow *window,
}
static void
gdk_offscreen_window_set_child_shapes (GdkWindow *window)
{
}
static void
gdk_offscreen_window_merge_child_shapes (GdkWindow *window)
gdk_offscreen_window_input_shape_combine_region (GdkWindow *window,
const GdkRegion *shape_region,
gint offset_x,
gint offset_y)
{
}
......@@ -1170,8 +1168,7 @@ gdk_offscreen_window_impl_iface_init (GdkWindowImplIface *iface)
iface->set_cursor = gdk_offscreen_window_set_cursor;
iface->get_geometry = gdk_offscreen_window_get_geometry;
iface->shape_combine_region = gdk_offscreen_window_shape_combine_region;
iface->set_child_shapes = gdk_offscreen_window_set_child_shapes;
iface->merge_child_shapes = gdk_offscreen_window_merge_child_shapes;
iface->input_shape_combine_region = gdk_offscreen_window_input_shape_combine_region;
iface->set_static_gravities = gdk_offscreen_window_set_static_gravities;
iface->queue_antiexpose = gdk_offscreen_window_queue_antiexpose;
iface->queue_translation = gdk_offscreen_window_queue_translation;
......
......@@ -383,6 +383,12 @@ gdk_window_finalize (GObject *object)
obj->impl = NULL;
}
if (obj->shape)
gdk_region_destroy (obj->shape);
if (obj->input_shape)
gdk_region_destroy (obj->input_shape);
if (obj->cursor)
gdk_cursor_unref (obj->cursor);
......@@ -431,12 +437,14 @@ gdk_window_has_no_impl (GdkWindowObject *window)
static void
remove_child_area (GdkWindowObject *private,
GdkWindowObject *until,
gboolean for_input,
GdkRegion *region)
{
GdkWindowObject *child;
GdkRegion *child_region;
GdkRectangle r;
GList *l;
GdkRegion *shape;
for (l = private->children; l; l = l->next)
{
......@@ -464,7 +472,6 @@ remove_child_area (GdkWindowObject *private,
gdk_region_intersect (child_region, child->shape);
else if (private->window_type == GDK_WINDOW_FOREIGN)
{
GdkRegion *shape;
shape = _gdk_windowing_window_get_shape ((GdkWindow *)child);
if (shape)
{
......@@ -472,6 +479,21 @@ remove_child_area (GdkWindowObject *private,
gdk_region_destroy (shape);
}
}
if (for_input)
{
if (child->input_shape)
gdk_region_intersect (child_region, child->input_shape);
else if (private->window_type == GDK_WINDOW_FOREIGN)
{
shape = _gdk_windowing_window_get_input_shape ((GdkWindow *)child);
if (shape)
{
gdk_region_intersect (child_region, shape);
gdk_region_destroy (shape);
}
}
}
gdk_region_subtract (region, child_region);
gdk_region_destroy (child_region);
......@@ -533,7 +555,7 @@ recompute_visible_regions_internal (GdkWindowObject *private,
gdk_region_intersect (new_clip, private->parent->clip_region);
/* Remove all overlapping children from parent */
remove_child_area (private->parent, private, new_clip);
remove_child_area (private->parent, private, FALSE, new_clip);
}
/* Convert from parent coords to window coords */
......@@ -552,7 +574,7 @@ recompute_visible_regions_internal (GdkWindowObject *private,
old_clip_region_with_children = private->clip_region_with_children;
private->clip_region_with_children = gdk_region_copy (private->clip_region);
remove_child_area (private, NULL, private->clip_region_with_children);
remove_child_area (private, NULL, FALSE, private->clip_region_with_children);
if (clip_region_changed ||
!gdk_region_equal (private->clip_region_with_children, old_clip_region_with_children))
......@@ -590,8 +612,7 @@ recompute_visible_regions_internal (GdkWindowObject *private,
{
GDK_WINDOW_IMPL_GET_IFACE (private->impl)->shape_combine_region ((GdkWindow *)private, private->clip_region, 0, 0);
}
if (recalculate_siblings &&
private->parent != NULL &&
GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT)
......@@ -1119,6 +1140,8 @@ gdk_window_set_has_native (GdkWindow *window, gboolean has_native)
private->impl = old_impl;
change_impl (private, new_impl);
GDK_WINDOW_IMPL_GET_IFACE (private->impl)->input_shape_combine_region ((GdkWindow *)private, private->input_shape, 0, 0);
}
else
{
......@@ -6275,7 +6298,7 @@ do_child_shapes (GdkWindow *window,
r.height = private->height;
region = gdk_region_rectangle (&r);
remove_child_area (private, NULL, region);
remove_child_area (private, NULL, FALSE, region);
if (merge && private->shape)
gdk_region_subtract (region, private->shape);
......@@ -6321,6 +6344,182 @@ gdk_window_merge_child_shapes (GdkWindow *window)
do_child_shapes (window, TRUE);
}
/**
* gdk_window_input_shape_combine_mask:
* @window: a #GdkWindow
* @mask: shape mask
* @x: X position of shape mask with respect to @window
* @y: Y position of shape mask with respect to @window
*
* Like gdk_window_shape_combine_mask(), but the shape applies
* only to event handling. Mouse events which happen while
* the pointer position corresponds to an unset bit in the
* mask will be passed on the window below @window.
*
* An input shape is typically used with RGBA windows.
* The alpha channel of the window defines which pixels are
* invisible and allows for nicely antialiased borders,
* and the input shape controls where the window is
* "clickable".
*
* On the X11 platform, this requires version 1.1 of the
* shape extension.
*
* On the Win32 platform, this functionality is not present and the
* function does nothing.
*
* Since: 2.10
*/
void
gdk_window_input_shape_combine_mask (GdkWindow *window,
GdkBitmap *mask,
gint x,
gint y)
{
GdkWindowObject *private;
GdkRegion *region;
g_return_if_fail (GDK_IS_WINDOW (window));
private = (GdkWindowObject *) window;
region = _gdk_windowing_get_shape_for_mask (mask);
gdk_window_input_shape_combine_region (window,
region,
x, y);
gdk_region_destroy (region);
}
/**
* gdk_window_input_shape_combine_region:
* @window: a #GdkWindow
* @shape_region: region of window to be non-transparent
* @offset_x: X position of @shape_region in @window coordinates
* @offset_y: Y position of @shape_region in @window coordinates
*
* Like gdk_window_shape_combine_region(), but the shape applies
* only to event handling. Mouse events which happen while
* the pointer position corresponds to an unset bit in the
* mask will be passed on the window below @window.
*
* An input shape is typically used with RGBA windows.
* The alpha channel of the window defines which pixels are
* invisible and allows for nicely antialiased borders,
* and the input shape controls where the window is
* "clickable".
*
* On the X11 platform, this requires version 1.1 of the
* shape extension.
*
* On the Win32 platform, this functionality is not present and the
* function does nothing.
*
* Since: 2.10
*/
void
gdk_window_input_shape_combine_region (GdkWindow *window,
const GdkRegion *shape_region,
gint offset_x,
gint offset_y)
{
GdkWindowObject *private;
g_return_if_fail (GDK_IS_WINDOW (window));
private = (GdkWindowObject *) window;
if (GDK_WINDOW_DESTROYED (window))
return;
if (private->input_shape)
gdk_region_destroy (private->input_shape);
if (shape_region)
{
private->input_shape = gdk_region_copy (shape_region);
gdk_region_offset (private->shape, offset_x, offset_y);
}
else
private->input_shape = NULL;
if (gdk_window_has_impl (private))
GDK_WINDOW_IMPL_GET_IFACE (private->impl)->input_shape_combine_region ((GdkWindow *)private, private->input_shape, 0, 0);
/* Pointer may have e.g. moved outside window due to the input mask change */
_gdk_syntesize_crossing_events_for_geometry_change (window);
}
static void
do_child_input_shapes (GdkWindow *window,
gboolean merge)
{
GdkWindowObject *private;
GdkRectangle r;
GdkRegion *region;
private = (GdkWindowObject *) window;
r.x = 0;
r.y = 0;
r.width = private->width;
r.height = private->height;
region = gdk_region_rectangle (&r);
remove_child_area (private, NULL, TRUE, region);
if (merge && private->shape)
gdk_region_subtract (region, private->shape);
if (merge && private->input_shape)
gdk_region_subtract (region, private->input_shape);
gdk_window_input_shape_combine_region (window, region, 0, 0);
}
/**
* gdk_window_set_child_input_shapes:
* @window: a #GdkWindow
*
* Sets the input shape mask of @window to the union of input shape masks
* for all children of @window, ignoring the input shape mask of @window
* itself. Contrast with gdk_window_merge_child_input_shapes() which includes
* the input shape mask of @window in the masks to be merged.
*
* Since: 2.10
**/
void
gdk_window_set_child_input_shapes (GdkWindow *window)
{
g_return_if_fail (GDK_IS_WINDOW (window));
do_child_input_shapes (window, FALSE);
}
/**
* gdk_window_merge_child_input_shapes:
* @window: a #GdkWindow
*
* Merges the input shape masks for any child windows into the
* input shape mask for @window. i.e. the union of all input masks
* for @window and its children will become the new input mask
* for @window. See gdk_window_input_shape_combine_mask().
*
* This function is distinct from gdk_window_set_child_input_shapes()
* because it includes @window's input shape mask in the set of
* shapes to be merged.
*
* Since: 2.10
**/
void
gdk_window_merge_child_input_shapes (GdkWindow *window)
{
g_return_if_fail (GDK_IS_WINDOW (window));
do_child_input_shapes (window, TRUE);
}
/**
* gdk_window_set_static_gravities:
......@@ -6713,6 +6912,9 @@ point_in_window (GdkWindowObject *window,
y >= 0 && y < window->height &&
(window->shape == NULL ||
gdk_region_point_in (window->shape,
x, y)) &&
(window->input_shape == NULL ||
gdk_region_point_in (window->input_shape,
x, y));
}
......
......@@ -339,6 +339,7 @@ struct _GdkWindowObject
GList *outstanding_moves;
GdkRegion *shape;
GdkRegion *input_shape;
cairo_surface_t *cairo_surface;
};
......
......@@ -90,8 +90,10 @@ struct _GdkWindowImplIface
const GdkRegion *shape_region,
gint offset_x,
gint offset_y);
void (* set_child_shapes) (GdkWindow *window);
void (* merge_child_shapes) (GdkWindow *window);
void (* input_shape_combine_region) (GdkWindow *window,
const GdkRegion *shape_region,
gint offset_x,
gint offset_y);
gboolean (* set_static_gravities) (GdkWindow *window,
gboolean use_static);
......
......@@ -3375,44 +3375,6 @@ gdk_window_add_colormap_windows (GdkWindow *window)
XFree (old_windows);
}
/**
* gdk_window_input_shape_combine_mask:
* @window: a #GdkWindow
* @mask: shape mask
* @x: X position of shape mask with respect to @window
* @y: Y position of shape mask with respect to @window
*
* Like gdk_window_shape_combine_mask(), but the shape applies
* only to event handling. Mouse events which happen while
* the pointer position corresponds to an unset bit in the
* mask will be passed on the window below @window.
*
* An input shape is typically used with RGBA windows.
* The alpha channel of the window defines which pixels are
* invisible and allows for nicely antialiased borders,
* and the input shape controls where the window is
* "clickable".
*
* On the X11 platform, this requires version 1.1 of the
* shape extension.
*
* On the Win32 platform, this functionality is not present and the
* function does nothing.
*
* Since: 2.10
*/
void
gdk_window_input_shape_combine_mask (GdkWindow *window,
GdkBitmap *mask,
gint x,
gint y)
{
#ifdef ShapeInput
do_shape_combine_mask (window, mask, x, y, ShapeInput);
#endif
}
static inline void
do_shape_combine_region (GdkWindow *window,
const GdkRegion *shape_region,
......@@ -3479,37 +3441,11 @@ gdk_window_x11_shape_combine_region (GdkWindow *window,
do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeBounding);
}
/**
* gdk_window_input_shape_combine_region:
* @window: a #GdkWindow
* @shape_region: region of window to be non-transparent
* @offset_x: X position of @shape_region in @window coordinates
* @offset_y: Y position of @shape_region in @window coordinates
*
* Like gdk_window_shape_combine_region(), but the shape applies
* only to event handling. Mouse events which happen while
* the pointer position corresponds to an unset bit in the
* mask will be passed on the window below @window.
*
* An input shape is typically used with RGBA windows.
* The alpha channel of the window defines which pixels are
* invisible and allows for nicely antialiased borders,
* and the input shape controls where the window is
* "clickable".
*
* On the X11 platform, this requires version 1.1 of the
* shape extension.
*
* On the Win32 platform, this functionality is not present and the
* function does nothing.
*
* Since: 2.10
*/
void
gdk_window_input_shape_combine_region (GdkWindow *window,
const GdkRegion *shape_region,
gint offset_x,
gint offset_y)
static void
gdk_window_x11_input_shape_combine_region (GdkWindow *window,
const GdkRegion *shape_region,
gint offset_x,
gint offset_y)
{
#ifdef ShapeInput
do_shape_combine_region (window, shape_region, offset_x, offset_y, ShapeInput);
......@@ -4568,152 +4504,10 @@ gdk_window_set_functions (GdkWindow *window,
gdk_window_set_mwm_hints (window, &hints);
}
#ifdef HAVE_SHAPE_EXT
/*
* propagate the shapes from all child windows of a GDK window to the parent
* window. Shamelessly ripped from Enlightenment's code
*
* - Raster
*/
struct _gdk_span
{
gint start;
gint end;
struct _gdk_span *next;
};
static void
gdk_add_to_span (struct _gdk_span **s,
gint x,
gint xx)
{
struct _gdk_span *ptr1, *ptr2, *noo, *ss;
gchar spanning;
ptr2 = NULL;
ptr1 = *s;
spanning = 0;
ss = NULL;
/* scan the spans for this line */
while (ptr1)
{
/* -- -> new span */
/* == -> existing span */
/* ## -> spans intersect */
/* if we are in the middle of spanning the span into the line */
if (spanning)
{
/* case: ---- ==== */
if (xx < ptr1->start - 1)
{
/* ends before next span - extend to here */
ss->end = xx;
return;
}
/* case: ----##=== */
else if (xx <= ptr1->end)
{
/* crosses into next span - delete next span and append */
ss->end = ptr1->end;
ss->next = ptr1->next;
g_free (ptr1);
return;
}
/* case: ---###--- */
else
{
/* overlaps next span - delete and keep checking */
ss->next = ptr1->next;
g_free (ptr1);
ptr1 = ss;
}
}
/* otherwise havent started spanning it in yet */
else
{
/* case: ---- ==== */
if (xx < ptr1->start - 1)
{
/* insert span here in list */
noo = g_malloc (sizeof (struct _gdk_span));
if (noo)
{
noo->start = x;
noo->end = xx;
noo->next = ptr1;
if (ptr2)
ptr2->next = noo;
else
*s = noo;
}
return;
}
/* case: ----##=== */
else if ((x < ptr1->start) && (xx <= ptr1->end))
{
/* expand this span to the left point of the new one */
ptr1->start = x;
return;
}
/* case: ===###=== */
else if ((x >= ptr1->start) && (xx <= ptr1->end))
{
/* throw the span away */
return;
}
/* case: ---###--- */
else if ((x < ptr1->start) && (xx > ptr1->end))
{
ss = ptr1;
spanning = 1;
ptr1->start = x;
ptr1->end = xx;
}
/* case: ===##---- */
else if ((x >= ptr1->start) && (x <= ptr1->end + 1) && (xx > ptr1->end))
{
ss = ptr1;
spanning = 1;
ptr1->end = xx;
}
/* case: ==== ---- */
/* case handled by next loop iteration - first case */
}
ptr2 = ptr1;
ptr1 = ptr1->next;
}
/* it started in the middle but spans beyond your current list */
if (spanning)
{
ptr2->end = xx;
return;
}
/* it does not start inside a span or in the middle, so add it to the end */
noo = g_malloc (sizeof (struct _gdk_span));
if (noo)
{
noo->start = x;
noo->end = xx;
if (ptr2)
{
noo->next = ptr2->next;
ptr2->next = noo;
}
else
{
noo->next = NULL;
*s = noo;
}
}
return;
}
static GdkRegion *
xwindow_get_shape (Display *xdisplay,
Window window)
Window window,
gint shape_type)
{
GdkRegion *shape;
GdkRectangle *rl;
......@@ -4725,8 +4519,8 @@ xwindow_get_shape (Display *xdisplay,
#if defined(HAVE_SHAPE_EXT)
xrl = XShapeGetRectangles (xdisplay,
window,
ShapeBounding, &rn, &ord);
shape_type, &rn, &ord);
if (rn == 0)
return gdk_region_new (); /* Empty */
......@@ -4778,7 +4572,7 @@ _gdk_windowing_get_shape_for_mask (GdkBitmap *mask)
ShapeSet);
region = xwindow_get_shape (GDK_DISPLAY_XDISPLAY (display),
window);
window, ShapeBounding);
XDestroyWindow (GDK_DISPLAY_XDISPLAY (display),
window);
......@@ -4793,273 +4587,26 @@ _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),
GDK_WINDOW_XID (window));
GDK_WINDOW_XID (window), ShapeBounding);
#endif
return NULL;
}
static void
gdk_add_rectangles (Display *disp,
Window win,
struct _gdk_span **spans,
gint basew,
gint baseh,
gint x,
gint y)
{
gint a, k;
gint x1, y1, x2, y2;
gint rn, ord;
XRectangle *rl;
rl = XShapeGetRectangles (disp, win, ShapeBounding, &rn, &ord);
if (rl)
{
/* go through all clip rects in this window's shape */
for (k = 0; k < rn; k++)
{
/* for each clip rect, add it to each line's spans */
x1 = x + rl[k].x;
x2 = x + rl[k].x + (rl[k].width - 1);
y1 = y + rl[k].y;
y2 = y + rl[k].y + (rl[k].height - 1);
if (x1 < 0)
x1 = 0;
if (y1 < 0)
y1 = 0;
if (x2 >= basew)
x2 = basew - 1;
if (y2 >= baseh)
y2 = baseh - 1;
for (a = y1; a <= y2; a++)
{
if ((x2 - x1) >= 0)
gdk_add_to_span (&spans[a], x1, x2);
}
}
XFree (rl);
}
}
static void
gdk_propagate_shapes (Display *disp,
Window win,
gboolean merge,
int shape)
{
Window rt, par, *list = NULL;
gint i, j, num = 0, num_rects = 0;
gint x, y, contig;
guint w, h, d;
gint baseh, basew;
XRectangle *rects = NULL;
struct _gdk_span **spans = NULL, *ptr1, *ptr2, *ptr3;
XWindowAttributes xatt;
XGetGeometry (disp, win, &rt, &x, &y, &w, &h, &d, &d);
if (h <= 0)
return;
basew = w;
baseh = h;
spans = g_malloc (sizeof (struct _gdk_span *) * h);
for (i = 0; i < h; i++)
spans[i] = NULL;
XQueryTree (disp, win, &rt, &par, &list, (unsigned int *)&num);
if (list)
{
/* go through all child windows and create/insert spans */
for (i = 0; i < num; i++)
{
if (XGetWindowAttributes (disp, list[i], &xatt) && (xatt.map_state != IsUnmapped))
if (XGetGeometry (disp, list[i], &rt, &x, &y, &w, &h, &d, &d))
gdk_add_rectangles (disp, list[i], spans, basew, baseh, x, y);
}
if (merge)
gdk_add_rectangles (disp, win, spans, basew, baseh, x, y);
/* go through the spans list and build a list of rects */
rects = g_malloc (sizeof (XRectangle) * 256);
num_rects = 0;