clutter/pick-stack: Use exclusive bottom/right box borders when picking

The graphene functions used by clutter for picking assume that boxes are
inclusive in both there start and end coordinates, so picking at y
coordinate 32 for an actor with the height 32 placed at y coordinate 0
would still be considered a hit. This however is wrong as 32 is the
first position that is not in the actor anymore.

Usually this would not be much of a problem, because motion events are
rarely ever at exactly these borders and even if they are there will be
another motion event soon after. But since actors in gnome-shell usually
are aligned with the pixel grid and on X11 enter/leave events are
generated by the X server at integer coordinates, this case is much
more likely for those.

This can cause issues with Firefox which when using client side
decorations, still requests MWM_DECOR_BORDER via _MOTIF_WM_HINTS to have
mutter draw a border + shadow. This means that the Firefox window even
when using CSD is still reparented. For such windows we receive among
others XI_RawMotion and XI_Enter events, but no XI_Motion events. And
the raw motion events are discarded after an enter event, because that
sets has_pointer_focus to TRUE in MetaSeatX11. So when moving the cursor
from the panel to a maximized Firefox window the last event clutter
receives is the enter event at exactly integer coordinates. Since the
panel is 32px tall and the generated enter event is at y position 32,
the picking code will pick a panel actor and the focus will remain on it
as long as the cursor does not leave the Firefox window.

Fix this by excluding the bottom and right border of a box when picking.

Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/4041

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1842>
This commit is contained in:
Sebastian Keller 2021-04-27 18:10:51 +02:00 committed by Marge Bot
parent cf76e694d7
commit a7c4e8cefa

View File

@ -126,10 +126,28 @@ ray_intersects_input_region (Record *rec,
if (G_LIKELY (is_axis_aligned_2d_rectangle (rec->vertices)))
{
graphene_box_t box;
graphene_box_t right_border;
graphene_box_t bottom_border;
/* Graphene considers both the start and end coordinates of boxes to be
* inclusive, while the vertices of a clutter actor are exclusive. So we
* need to manually exclude hits on these borders
*/
graphene_box_init_from_points (&box, 4, rec->vertices);
return graphene_box_contains_point (&box, point) ||
graphene_ray_intersects_box (ray, &box);
graphene_box_init_from_points (&right_border, 2, rec->vertices + 1);
graphene_box_init_from_points (&bottom_border, 2, rec->vertices + 2);
/* Fast path for actors without 3D transforms */
if (graphene_box_contains_point (&box, point))
{
return !graphene_box_contains_point (&right_border, point) &&
!graphene_box_contains_point (&bottom_border, point);
}
return graphene_ray_intersects_box (ray, &box) &&
!graphene_ray_intersects_box (ray, &right_border) &&
!graphene_ray_intersects_box (ray, &bottom_border);
}
else
{