gdk: Add gdk_window_set_pass_through

An pass_through window is something you can draw in but does not
affect event handling. Normally if a window has with no event mask set
for a particular event then input events in it go to its parent window
(X11 semantics), whereas if pass_through is enabled the window below
the window will get the event. The later mode is useful when the
window is partially transparent. Note that an pass-through windows can
have child windows that are not pass-through so they can still get events
on some parts.

Semantically, this behaves the same as an regular window with
gdk_window_set_child_input_shapes() called on it (and re-called any
time a child is changed), but its far more efficient and easy to use.

This allows us to fix the testoverlay input stacking test.

https://bugzilla.gnome.org/show_bug.cgi?id=750568

https://bugs.freedesktop.org/show_bug.cgi?id=90917
This commit is contained in:
Alexander Larsson 2015-06-08 15:36:43 +02:00
parent ccc4b192ec
commit 4c3eece663
3 changed files with 128 additions and 4 deletions

View File

@ -314,6 +314,7 @@ struct _GdkWindow
guint8 fullscreen_mode;
guint input_only : 1;
guint pass_through : 1;
guint modal_hint : 1;
guint composited : 1;
guint has_alpha_background : 1;

View File

@ -6784,6 +6784,64 @@ gdk_window_set_child_input_shapes (GdkWindow *window)
do_child_input_shapes (window, FALSE);
}
/**
* gdk_window_set_pass_through:
* @window: a #GdkWindow
* @pass_through: a boolean
*
* Sets whether input to the window is passed through to the window
* below.
*
* The default value of this is %FALSE, which means that pointer
* events that happen inside the window are send first to the window,
* but if the event is not selected by the event mask then the event
* is sent to the parent window, and so on up the hierarchy.
*
* If @pass_through is %TRUE then such pointer events happen as if the
* window wasn't there at all, and thus will be sent first to any
* windows below @window. This is useful if the window is used in a
* transparent fashion. In the terminology of the web this would be called
* "pointer-events: none".
*
* Note that a window with @pass_through %TRUE can still have a subwindow
* without pass through, so you can get events on a subset of a window. And in
* that cases you would get the in-between related events such as the pointer
* enter/leave events on its way to the destination window.
*
* Since: 3.18
**/
void
gdk_window_set_pass_through (GdkWindow *window,
gboolean pass_through)
{
g_return_if_fail (GDK_IS_WINDOW (window));
window->pass_through = !!pass_through;
/* Pointer may have e.g. moved outside window due to the input region change */
_gdk_synthesize_crossing_events_for_geometry_change (window);
}
/**
* gdk_window_get_pass_through:
* @window: a #GdkWindow
* @pass_through: a boolean
*
* Returns whether input to the window is passed through to the window
* below.
*
* See gdk_window_set_pass_through() for details
*
* Since: 3.18
**/
gboolean
gdk_window_get_pass_through (GdkWindow *window)
{
g_return_if_fail (GDK_IS_WINDOW (window));
return window->pass_through;
}
/**
* gdk_window_merge_child_input_shapes:
* @window: a #GdkWindow
@ -7133,6 +7191,63 @@ point_in_window (GdkWindow *window,
x, y));
}
/* Same as point_in_window, except it also takes pass_through and its
interaction with child windows into account */
static gboolean
point_in_input_window (GdkWindow *window,
gdouble x,
gdouble y,
GdkWindow **input_window,
gdouble *input_window_x,
gdouble *input_window_y)
{
GdkWindow *sub;
double child_x, child_y;
GList *l;
if (!point_in_window (window, x, y))
return FALSE;
if (!window->pass_through)
{
if (input_window)
{
*input_window = window;
*input_window_x = x;
*input_window_y = y;
}
return TRUE;
}
/* For pass-through, must be over a child input window */
/* Children is ordered in reverse stack order, i.e. first is topmost */
for (l = window->children; l != NULL; l = l->next)
{
sub = l->data;
if (!GDK_WINDOW_IS_MAPPED (sub))
continue;
gdk_window_coords_from_parent ((GdkWindow *)sub,
x, y,
&child_x, &child_y);
if (point_in_input_window (sub, child_x, child_y,
input_window, input_window_x, input_window_y))
{
if (input_window)
gdk_window_coords_to_parent (sub,
*input_window_x,
*input_window_y,
input_window_x,
input_window_y);
return TRUE;
}
}
return FALSE;
}
static GdkWindow *
convert_native_coords_to_toplevel (GdkWindow *window,
gdouble child_x,
@ -7226,7 +7341,8 @@ _gdk_window_find_child_at (GdkWindow *window,
gdk_window_coords_from_parent ((GdkWindow *)sub,
x, y,
&child_x, &child_y);
if (point_in_window (sub, child_x, child_y))
if (point_in_input_window (sub, child_x, child_y,
NULL, NULL, NULL))
return (GdkWindow *)sub;
}
@ -7249,7 +7365,7 @@ _gdk_window_find_descendant_at (GdkWindow *window,
gdouble *found_x,
gdouble *found_y)
{
GdkWindow *sub;
GdkWindow *sub, *input_window;
gdouble child_x, child_y;
GList *l;
gboolean found;
@ -7270,11 +7386,12 @@ _gdk_window_find_descendant_at (GdkWindow *window,
gdk_window_coords_from_parent ((GdkWindow *)sub,
x, y,
&child_x, &child_y);
if (point_in_window (sub, child_x, child_y))
if (point_in_input_window (sub, child_x, child_y,
&input_window, &child_x, &child_y))
{
x = child_x;
y = child_y;
window = sub;
window = input_window;
found = TRUE;
break;
}

View File

@ -654,6 +654,12 @@ GDK_AVAILABLE_IN_ALL
void gdk_window_merge_child_input_shapes (GdkWindow *window);
GDK_AVAILABLE_IN_3_18
void gdk_window_set_pass_through (GdkWindow *window,
gboolean pass_through);
GDK_AVAILABLE_IN_3_18
gboolean gdk_window_get_pass_through (GdkWindow *window);
/*
* Check if a window has been shown, and whether all its
* parents up to a toplevel have been shown, respectively.