mirror of
https://github.com/brl/mutter.git
synced 2024-12-18 00:52:05 +00:00
98ef6d0d05
Changes in games between fullscreen and windowed modes may trigger chaotic situations where the buffer and the frame size temporarily disagree, producing rectangles with negative width/height. This is usually followed by other updates that bring the pointer constraint up to date. This makes cairo panic and return an "error" empty region, which breaks deeper down when using the region rectangles to apply the pointer constraint. If we hit this situation, ignore the frame rectangle, and just go with the buffer rectangle. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1655>
1202 lines
38 KiB
C
1202 lines
38 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2015 Red Hat
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* Written by:
|
|
* Jonas Ådahl <jadahl@gmail.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "wayland/meta-wayland-pointer-constraints.h"
|
|
|
|
#include <glib.h>
|
|
|
|
#include "backends/meta-backend-private.h"
|
|
#include "backends/meta-pointer-constraint.h"
|
|
#include "core/frame.h"
|
|
#include "core/window-private.h"
|
|
#include "meta/meta-backend.h"
|
|
#include "wayland/meta-pointer-confinement-wayland.h"
|
|
#include "wayland/meta-pointer-lock-wayland.h"
|
|
#include "wayland/meta-wayland-pointer.h"
|
|
#include "wayland/meta-wayland-private.h"
|
|
#include "wayland/meta-wayland-region.h"
|
|
#include "wayland/meta-wayland-seat.h"
|
|
#include "wayland/meta-wayland-surface.h"
|
|
#include "wayland/meta-xwayland.h"
|
|
|
|
#include "pointer-constraints-unstable-v1-server-protocol.h"
|
|
|
|
static GQuark quark_pending_constraint_state = 0;
|
|
static GQuark quark_surface_pointer_constraints_data = 0;
|
|
|
|
struct _MetaWaylandPointerConstraint
|
|
{
|
|
GObject parent;
|
|
|
|
MetaWaylandSurface *surface;
|
|
gboolean is_enabled;
|
|
cairo_region_t *region;
|
|
struct wl_resource *resource;
|
|
MetaWaylandPointerGrab grab;
|
|
MetaWaylandSeat *seat;
|
|
enum zwp_pointer_constraints_v1_lifetime lifetime;
|
|
gulong pointer_focus_surface_handler_id;
|
|
|
|
gboolean hint_set;
|
|
wl_fixed_t x_hint;
|
|
wl_fixed_t y_hint;
|
|
|
|
MetaPointerConfinementWayland *confinement;
|
|
};
|
|
|
|
typedef struct _MetaWaylandSurfacePointerConstraintsData
|
|
{
|
|
MetaWaylandSurface *surface;
|
|
|
|
GList *pointer_constraints;
|
|
|
|
MetaWindow *window;
|
|
gulong window_associated_handler_id;
|
|
|
|
gulong appears_changed_handler_id;
|
|
gulong raised_handler_id;
|
|
} MetaWaylandSurfacePointerConstraintsData;
|
|
|
|
typedef struct
|
|
{
|
|
MetaWaylandPointerConstraint *constraint;
|
|
cairo_region_t *region;
|
|
gulong applied_handler_id;
|
|
} MetaWaylandPendingConstraintState;
|
|
|
|
typedef struct
|
|
{
|
|
GList *pending_constraint_states;
|
|
} MetaWaylandPendingConstraintStateContainer;
|
|
|
|
G_DEFINE_TYPE (MetaWaylandPointerConstraint, meta_wayland_pointer_constraint,
|
|
G_TYPE_OBJECT);
|
|
|
|
static const struct zwp_locked_pointer_v1_interface locked_pointer_interface;
|
|
static const struct zwp_confined_pointer_v1_interface confined_pointer_interface;
|
|
static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface;
|
|
static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface;
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint);
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint);
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window);
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat,
|
|
MetaWindow *window);
|
|
|
|
static MetaWaylandSurfacePointerConstraintsData *
|
|
get_surface_constraints_data (MetaWaylandSurface *surface)
|
|
{
|
|
return g_object_get_qdata (G_OBJECT (surface),
|
|
quark_surface_pointer_constraints_data);
|
|
}
|
|
|
|
static void
|
|
appears_focused_changed (MetaWindow *window,
|
|
GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
MetaWaylandCompositor *wayland_compositor;
|
|
|
|
wayland_compositor = meta_wayland_compositor_get_default ();
|
|
meta_wayland_pointer_constraint_maybe_remove_for_seat (wayland_compositor->seat,
|
|
window);
|
|
|
|
meta_wayland_pointer_constraint_maybe_enable_for_window (window);
|
|
}
|
|
|
|
static void
|
|
window_raised (MetaWindow *window)
|
|
{
|
|
meta_wayland_pointer_constraint_maybe_enable_for_window (window);
|
|
}
|
|
|
|
static void
|
|
connect_window (MetaWaylandSurfacePointerConstraintsData *data,
|
|
MetaWindow *window)
|
|
{
|
|
data->window = window;
|
|
g_object_add_weak_pointer (G_OBJECT (data->window),
|
|
(gpointer *) &data->window);
|
|
data->appears_changed_handler_id =
|
|
g_signal_connect (data->window, "notify::appears-focused",
|
|
G_CALLBACK (appears_focused_changed), NULL);
|
|
data->raised_handler_id =
|
|
g_signal_connect (data->window, "raised",
|
|
G_CALLBACK (window_raised), NULL);
|
|
}
|
|
|
|
static void
|
|
window_associated (MetaWaylandSurfaceRole *surface_role,
|
|
MetaWaylandSurfacePointerConstraintsData *data)
|
|
{
|
|
MetaWaylandSurface *surface = data->surface;
|
|
MetaWindow *window;
|
|
|
|
window = meta_wayland_surface_get_window (surface);
|
|
connect_window (data, window);
|
|
g_clear_signal_handler (&data->window_associated_handler_id, surface);
|
|
|
|
meta_wayland_pointer_constraint_maybe_enable_for_window (window);
|
|
}
|
|
|
|
static MetaWaylandSurfacePointerConstraintsData *
|
|
surface_constraint_data_new (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandSurfacePointerConstraintsData *data;
|
|
MetaWindow *window;
|
|
|
|
data = g_new0 (MetaWaylandSurfacePointerConstraintsData, 1);
|
|
|
|
data->surface = surface;
|
|
|
|
window = meta_wayland_surface_get_window (surface);
|
|
if (window)
|
|
{
|
|
connect_window (data, window);
|
|
}
|
|
else if (meta_xwayland_is_xwayland_surface (surface))
|
|
{
|
|
data->window_associated_handler_id =
|
|
g_signal_connect (surface->role, "window-associated",
|
|
G_CALLBACK (window_associated),
|
|
data);
|
|
}
|
|
else
|
|
{
|
|
/* TODO: Support constraints on non-toplevel windows, such as subsurfaces.
|
|
*/
|
|
g_warn_if_reached ();
|
|
}
|
|
|
|
return data;
|
|
}
|
|
static void
|
|
surface_constraint_data_free (MetaWaylandSurfacePointerConstraintsData *data)
|
|
{
|
|
if (data->window)
|
|
{
|
|
g_clear_signal_handler (&data->appears_changed_handler_id, data->window);
|
|
g_clear_signal_handler (&data->raised_handler_id, data->window);
|
|
g_object_remove_weak_pointer (G_OBJECT (data->window),
|
|
(gpointer *) &data->window);
|
|
}
|
|
else
|
|
{
|
|
g_clear_signal_handler (&data->window_associated_handler_id,
|
|
data->surface->role);
|
|
}
|
|
|
|
g_list_free_full (data->pointer_constraints,
|
|
(GDestroyNotify) meta_wayland_pointer_constraint_destroy);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
constrained_surface_destroyed (MetaWaylandSurface *surface,
|
|
MetaWaylandSurfacePointerConstraintsData *data)
|
|
{
|
|
surface_constraint_data_free (data);
|
|
}
|
|
|
|
static MetaWaylandSurfacePointerConstraintsData *
|
|
ensure_surface_constraints_data (MetaWaylandSurface *surface)
|
|
{
|
|
MetaWaylandSurfacePointerConstraintsData *data;
|
|
|
|
data = get_surface_constraints_data (surface);
|
|
if (!data)
|
|
{
|
|
data = surface_constraint_data_new (surface);
|
|
g_object_set_qdata (G_OBJECT (surface),
|
|
quark_surface_pointer_constraints_data,
|
|
data);
|
|
g_signal_connect (surface, "destroy",
|
|
G_CALLBACK (constrained_surface_destroyed), data);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
surface_add_pointer_constraint (MetaWaylandSurface *surface,
|
|
MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWaylandSurfacePointerConstraintsData *data;
|
|
|
|
data = ensure_surface_constraints_data (surface);
|
|
data->pointer_constraints = g_list_append (data->pointer_constraints,
|
|
constraint);
|
|
}
|
|
|
|
static void
|
|
surface_remove_pointer_constraints (MetaWaylandSurface *surface,
|
|
MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWaylandSurfacePointerConstraintsData *data;
|
|
|
|
data = get_surface_constraints_data (surface);
|
|
data->pointer_constraints =
|
|
g_list_remove (data->pointer_constraints, constraint);
|
|
|
|
if (!data->pointer_constraints)
|
|
{
|
|
g_object_set_qdata (G_OBJECT (surface),
|
|
quark_surface_pointer_constraints_data,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pointer_focus_surface_changed (MetaWaylandPointer *pointer,
|
|
MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
window = meta_wayland_surface_get_window (constraint->surface);
|
|
if (window)
|
|
{
|
|
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
|
|
|
|
meta_wayland_pointer_constraint_maybe_remove_for_seat (seat, window);
|
|
}
|
|
|
|
meta_wayland_pointer_constraint_maybe_enable (constraint);
|
|
}
|
|
|
|
static MetaWaylandPointerConstraint *
|
|
meta_wayland_pointer_constraint_new (MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat,
|
|
MetaWaylandRegion *region,
|
|
enum zwp_pointer_constraints_v1_lifetime lifetime,
|
|
struct wl_resource *resource,
|
|
const MetaWaylandPointerGrabInterface *grab_interface)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint;
|
|
|
|
constraint = g_object_new (META_TYPE_WAYLAND_POINTER_CONSTRAINT, NULL);
|
|
if (!constraint)
|
|
return NULL;
|
|
|
|
constraint->surface = surface;
|
|
constraint->seat = seat;
|
|
constraint->lifetime = lifetime;
|
|
constraint->resource = resource;
|
|
constraint->grab.interface = grab_interface;
|
|
|
|
if (region)
|
|
{
|
|
constraint->region =
|
|
cairo_region_copy (meta_wayland_region_peek_cairo_region (region));
|
|
}
|
|
else
|
|
{
|
|
constraint->region = NULL;
|
|
}
|
|
|
|
constraint->pointer_focus_surface_handler_id =
|
|
g_signal_connect (seat->pointer, "focus-surface-changed",
|
|
G_CALLBACK (pointer_focus_surface_changed),
|
|
constraint);
|
|
|
|
return constraint;
|
|
}
|
|
|
|
static gboolean
|
|
meta_wayland_pointer_constraint_is_enabled (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
return constraint->is_enabled;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_notify_activated (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
struct wl_resource *resource = constraint->resource;
|
|
|
|
if (wl_resource_instance_of (resource,
|
|
&zwp_locked_pointer_v1_interface,
|
|
&locked_pointer_interface))
|
|
{
|
|
zwp_locked_pointer_v1_send_locked (resource);
|
|
}
|
|
else if (wl_resource_instance_of (resource,
|
|
&zwp_confined_pointer_v1_interface,
|
|
&confined_pointer_interface))
|
|
{
|
|
zwp_confined_pointer_v1_send_confined (resource);
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_notify_deactivated (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
struct wl_resource *resource = constraint->resource;
|
|
|
|
if (wl_resource_instance_of (resource,
|
|
&zwp_locked_pointer_v1_interface,
|
|
&locked_pointer_interface))
|
|
zwp_locked_pointer_v1_send_unlocked (resource);
|
|
else if (wl_resource_instance_of (resource,
|
|
&zwp_confined_pointer_v1_interface,
|
|
&confined_pointer_interface))
|
|
zwp_confined_pointer_v1_send_unconfined (resource);
|
|
}
|
|
|
|
static MetaPointerConfinementWayland *
|
|
meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
struct wl_resource *resource = constraint->resource;
|
|
|
|
if (wl_resource_instance_of (resource,
|
|
&zwp_locked_pointer_v1_interface,
|
|
&locked_pointer_interface))
|
|
{
|
|
return meta_pointer_lock_wayland_new (constraint);
|
|
}
|
|
else if (wl_resource_instance_of (resource,
|
|
&zwp_confined_pointer_v1_interface,
|
|
&confined_pointer_interface))
|
|
{
|
|
return meta_pointer_confinement_wayland_new (constraint);
|
|
}
|
|
g_assert_not_reached ();
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
g_assert (!constraint->is_enabled);
|
|
|
|
constraint->is_enabled = TRUE;
|
|
meta_wayland_pointer_constraint_notify_activated (constraint);
|
|
meta_wayland_pointer_start_grab (constraint->seat->pointer,
|
|
&constraint->grab);
|
|
|
|
constraint->confinement =
|
|
meta_wayland_pointer_constraint_create_pointer_constraint (constraint);
|
|
meta_pointer_confinement_wayland_enable (constraint->confinement);
|
|
g_object_add_weak_pointer (G_OBJECT (constraint->confinement),
|
|
(gpointer *) &constraint->confinement);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_disable (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
constraint->is_enabled = FALSE;
|
|
|
|
if (constraint->confinement)
|
|
{
|
|
meta_pointer_confinement_wayland_disable (constraint->confinement);
|
|
g_object_unref (constraint->confinement);
|
|
}
|
|
|
|
meta_wayland_pointer_constraint_notify_deactivated (constraint);
|
|
meta_wayland_pointer_end_grab (constraint->grab.pointer);
|
|
}
|
|
|
|
void
|
|
meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
if (meta_wayland_pointer_constraint_is_enabled (constraint))
|
|
meta_wayland_pointer_constraint_disable (constraint);
|
|
|
|
wl_resource_set_user_data (constraint->resource, NULL);
|
|
g_clear_pointer (&constraint->region, cairo_region_destroy);
|
|
g_object_unref (constraint);
|
|
}
|
|
|
|
static gboolean
|
|
is_within_constraint_region (MetaWaylandPointerConstraint *constraint,
|
|
wl_fixed_t sx,
|
|
wl_fixed_t sy)
|
|
{
|
|
cairo_region_t *region;
|
|
gboolean is_within;
|
|
|
|
region = meta_wayland_pointer_constraint_calculate_effective_region (constraint);
|
|
is_within = cairo_region_contains_point (region,
|
|
wl_fixed_to_int (sx),
|
|
wl_fixed_to_int (sy));
|
|
cairo_region_destroy (region);
|
|
|
|
return is_within;
|
|
}
|
|
|
|
static gboolean
|
|
should_constraint_be_enabled (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWindow *window;
|
|
|
|
window = meta_wayland_surface_get_window (constraint->surface);
|
|
if (!window)
|
|
{
|
|
/*
|
|
* Locks from Xwayland may come before we have had the opportunity to
|
|
* associate the X11 Window with the wl_surface.
|
|
*/
|
|
g_warn_if_fail (meta_xwayland_is_xwayland_surface (constraint->surface));
|
|
return FALSE;
|
|
}
|
|
|
|
if (window->unmanaging)
|
|
return FALSE;
|
|
|
|
if (constraint->seat->pointer->focus_surface != constraint->surface)
|
|
return FALSE;
|
|
|
|
if (meta_xwayland_is_xwayland_surface (constraint->surface))
|
|
{
|
|
MetaDisplay *display = meta_get_display ();
|
|
|
|
/*
|
|
* We need to handle Xwayland surfaces differently in order to allow
|
|
* Xwayland to be able to lock the pointer. For example, we cannot require
|
|
* the locked window to "appear focused" because the surface Xwayland
|
|
* locks might not be able to appear focused (for example it may be a
|
|
* override redirect window).
|
|
*
|
|
* Since we don't have any way to know what focused window an override
|
|
* redirect is associated with, nor have a way to know if the override
|
|
* redirect window even shares the same connection as a focused window,
|
|
* we simply can only really restrict it to enable the lock if any
|
|
* Xwayland window appears focused.
|
|
*/
|
|
|
|
if (display->focus_window &&
|
|
display->focus_window->client_type != META_WINDOW_CLIENT_TYPE_X11)
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (!meta_window_appears_focused (window))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
wl_fixed_t sx, sy;
|
|
|
|
if (constraint->is_enabled)
|
|
return;
|
|
|
|
if (!should_constraint_be_enabled (constraint))
|
|
return;
|
|
|
|
meta_wayland_pointer_get_relative_coordinates (constraint->seat->pointer,
|
|
constraint->surface,
|
|
&sx, &sy);
|
|
if (!is_within_constraint_region (constraint, sx, sy))
|
|
return;
|
|
|
|
meta_wayland_pointer_constraint_enable (constraint);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_remove (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWaylandSurface *surface = constraint->surface;
|
|
|
|
surface_remove_pointer_constraints (surface, constraint);
|
|
meta_wayland_pointer_constraint_destroy (constraint);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_deactivate (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
switch (constraint->lifetime)
|
|
{
|
|
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT:
|
|
meta_wayland_pointer_constraint_remove (constraint);
|
|
break;
|
|
|
|
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
|
|
meta_wayland_pointer_constraint_disable (constraint);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat,
|
|
MetaWindow *window)
|
|
{
|
|
MetaWaylandPointer *pointer = seat->pointer;
|
|
MetaWaylandPointerConstraint *constraint;
|
|
|
|
if ((pointer->grab->interface != &confined_pointer_grab_interface &&
|
|
pointer->grab->interface != &locked_pointer_grab_interface))
|
|
return;
|
|
|
|
constraint = wl_container_of (pointer->grab, constraint, grab);
|
|
|
|
if (should_constraint_be_enabled (constraint))
|
|
return;
|
|
|
|
meta_wayland_pointer_constraint_deactivate (constraint);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window)
|
|
{
|
|
MetaWaylandSurface *surface = window->surface;
|
|
MetaWaylandSurfacePointerConstraintsData *surface_data;
|
|
GList *l;
|
|
|
|
if (!surface)
|
|
{
|
|
g_warn_if_fail (window->client_type == META_WINDOW_CLIENT_TYPE_X11);
|
|
return;
|
|
}
|
|
|
|
surface_data = get_surface_constraints_data (surface);
|
|
if (!surface_data)
|
|
return;
|
|
|
|
for (l = surface_data->pointer_constraints; l; l = l->next)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint = l->data;
|
|
|
|
meta_wayland_pointer_constraint_maybe_enable (constraint);
|
|
}
|
|
}
|
|
|
|
MetaWaylandSeat *
|
|
meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
return constraint->seat;
|
|
}
|
|
|
|
cairo_region_t *
|
|
meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
cairo_region_t *region;
|
|
MetaWindow *window;
|
|
|
|
region = meta_wayland_surface_calculate_input_region (constraint->surface);
|
|
if (constraint->region)
|
|
cairo_region_intersect (region, constraint->region);
|
|
|
|
window = meta_wayland_surface_get_window (constraint->surface);
|
|
if (window && window->frame)
|
|
{
|
|
MetaFrame *frame = window->frame;
|
|
int actual_width, actual_height;
|
|
|
|
g_assert (meta_xwayland_is_xwayland_surface (constraint->surface));
|
|
|
|
actual_width = window->buffer_rect.width - (frame->child_x +
|
|
frame->right_width);
|
|
actual_height = window->buffer_rect.height - (frame->child_y +
|
|
frame->bottom_height);
|
|
if (actual_width > 0 && actual_height > 0)
|
|
{
|
|
cairo_region_intersect_rectangle (region, &(cairo_rectangle_int_t) {
|
|
.x = frame->child_x,
|
|
.y = frame->child_y,
|
|
.width = actual_width,
|
|
.height = actual_height
|
|
});
|
|
}
|
|
}
|
|
|
|
return region;
|
|
}
|
|
|
|
MetaWaylandSurface *
|
|
meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
return constraint->surface;
|
|
}
|
|
|
|
static void
|
|
pointer_constraint_resource_destroyed (struct wl_resource *resource)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_resource_get_user_data (resource);
|
|
|
|
if (!constraint)
|
|
return;
|
|
|
|
meta_wayland_pointer_constraint_remove (constraint);
|
|
}
|
|
|
|
static void
|
|
pending_constraint_state_free (MetaWaylandPendingConstraintState *constraint_pending)
|
|
{
|
|
g_clear_pointer (&constraint_pending->region, cairo_region_destroy);
|
|
if (constraint_pending->constraint)
|
|
g_object_remove_weak_pointer (G_OBJECT (constraint_pending->constraint),
|
|
(gpointer *) &constraint_pending->constraint);
|
|
}
|
|
|
|
static MetaWaylandPendingConstraintStateContainer *
|
|
get_pending_constraint_state_container (MetaWaylandSurfaceState *pending)
|
|
{
|
|
return g_object_get_qdata (G_OBJECT (pending),
|
|
quark_pending_constraint_state);
|
|
}
|
|
|
|
static MetaWaylandPendingConstraintState *
|
|
get_pending_constraint_state (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWaylandSurfaceState *pending;
|
|
MetaWaylandPendingConstraintStateContainer *container;
|
|
GList *l;
|
|
|
|
pending = meta_wayland_surface_get_pending_state (constraint->surface);
|
|
container = get_pending_constraint_state_container (pending);
|
|
for (l = container->pending_constraint_states; l; l = l->next)
|
|
{
|
|
MetaWaylandPendingConstraintState *constraint_pending = l->data;
|
|
|
|
if (constraint_pending->constraint == constraint)
|
|
return constraint_pending;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
pending_constraint_state_container_free (MetaWaylandPendingConstraintStateContainer *container)
|
|
{
|
|
g_list_free_full (container->pending_constraint_states,
|
|
(GDestroyNotify) pending_constraint_state_free);
|
|
g_free (container);
|
|
}
|
|
|
|
static MetaWaylandPendingConstraintStateContainer *
|
|
ensure_pending_constraint_state_container (MetaWaylandSurfaceState *pending)
|
|
{
|
|
MetaWaylandPendingConstraintStateContainer *container;
|
|
|
|
container = get_pending_constraint_state_container (pending);
|
|
if (!container)
|
|
{
|
|
container = g_new0 (MetaWaylandPendingConstraintStateContainer, 1);
|
|
g_object_set_qdata_full (G_OBJECT (pending),
|
|
quark_pending_constraint_state,
|
|
container,
|
|
(GDestroyNotify) pending_constraint_state_container_free);
|
|
|
|
}
|
|
|
|
return container;
|
|
}
|
|
|
|
static void
|
|
remove_pending_constraint_state (MetaWaylandPointerConstraint *constraint,
|
|
MetaWaylandSurfaceState *pending)
|
|
{
|
|
MetaWaylandPendingConstraintStateContainer *container;
|
|
GList *l;
|
|
|
|
container = get_pending_constraint_state_container (pending);
|
|
for (l = container->pending_constraint_states; l; l = l->next)
|
|
{
|
|
MetaWaylandPendingConstraintState *constraint_pending = l->data;
|
|
if (constraint_pending->constraint != constraint)
|
|
continue;
|
|
|
|
pending_constraint_state_free (l->data);
|
|
container->pending_constraint_states =
|
|
g_list_remove_link (container->pending_constraint_states, l);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pending_constraint_state_applied (MetaWaylandSurfaceState *pending,
|
|
MetaWaylandPendingConstraintState *constraint_pending)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint = constraint_pending->constraint;
|
|
|
|
if (!constraint)
|
|
return;
|
|
|
|
g_clear_pointer (&constraint->region, cairo_region_destroy);
|
|
if (constraint_pending->region)
|
|
{
|
|
constraint->region = constraint_pending->region;
|
|
constraint_pending->region = NULL;
|
|
}
|
|
else
|
|
{
|
|
constraint->region = NULL;
|
|
}
|
|
|
|
g_clear_signal_handler (&constraint_pending->applied_handler_id, pending);
|
|
remove_pending_constraint_state (constraint, pending);
|
|
|
|
/* The pointer is potentially warped by the actor paint signal callback if
|
|
* the new region proved it necessary.
|
|
*/
|
|
}
|
|
|
|
static MetaWaylandPendingConstraintState *
|
|
ensure_pending_constraint_state (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
MetaWaylandSurfaceState *pending;
|
|
MetaWaylandPendingConstraintStateContainer *container;
|
|
MetaWaylandPendingConstraintState *constraint_pending;
|
|
|
|
pending = meta_wayland_surface_get_pending_state (constraint->surface);
|
|
container = ensure_pending_constraint_state_container (pending);
|
|
constraint_pending = get_pending_constraint_state (constraint);
|
|
if (!constraint_pending)
|
|
{
|
|
constraint_pending = g_new0 (MetaWaylandPendingConstraintState, 1);
|
|
constraint_pending->constraint = constraint;
|
|
constraint_pending->applied_handler_id =
|
|
g_signal_connect (pending, "applied",
|
|
G_CALLBACK (pending_constraint_state_applied),
|
|
constraint_pending);
|
|
g_object_add_weak_pointer (G_OBJECT (constraint),
|
|
(gpointer *) &constraint_pending->constraint);
|
|
|
|
container->pending_constraint_states =
|
|
g_list_append (container->pending_constraint_states,
|
|
constraint_pending);
|
|
}
|
|
|
|
return constraint_pending;
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_set_pending_region (MetaWaylandPointerConstraint *constraint,
|
|
MetaWaylandRegion *region)
|
|
{
|
|
MetaWaylandPendingConstraintState *constraint_pending;
|
|
|
|
constraint_pending = ensure_pending_constraint_state (constraint);
|
|
|
|
g_clear_pointer (&constraint_pending->region, cairo_region_destroy);
|
|
if (region)
|
|
{
|
|
constraint_pending->region =
|
|
cairo_region_copy (meta_wayland_region_peek_cairo_region (region));
|
|
}
|
|
}
|
|
|
|
static MetaWaylandPointerConstraint *
|
|
get_pointer_constraint_for_seat (MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat)
|
|
{
|
|
MetaWaylandSurfacePointerConstraintsData *surface_data;
|
|
GList *l;
|
|
|
|
surface_data = get_surface_constraints_data (surface);
|
|
if (!surface_data)
|
|
return NULL;
|
|
|
|
for (l = surface_data->pointer_constraints; l; l = l->next)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint = l->data;
|
|
|
|
if (seat == constraint->seat)
|
|
return constraint;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
init_pointer_constraint (struct wl_resource *resource,
|
|
uint32_t id,
|
|
MetaWaylandSurface *surface,
|
|
MetaWaylandSeat *seat,
|
|
MetaWaylandRegion *region,
|
|
enum zwp_pointer_constraints_v1_lifetime lifetime,
|
|
const struct wl_interface *interface,
|
|
const void *implementation,
|
|
const MetaWaylandPointerGrabInterface *grab_interface)
|
|
{
|
|
struct wl_client *client = wl_resource_get_client (resource);
|
|
struct wl_resource *cr;
|
|
MetaWaylandPointerConstraint *constraint;
|
|
|
|
if (get_pointer_constraint_for_seat (surface, seat))
|
|
{
|
|
wl_resource_post_error (resource,
|
|
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
|
"the pointer was already requested to be "
|
|
"locked or confined on that surface");
|
|
return;
|
|
}
|
|
|
|
cr = wl_resource_create (client, interface,
|
|
wl_resource_get_version (resource),
|
|
id);
|
|
if (cr == NULL)
|
|
{
|
|
wl_client_post_no_memory (client);
|
|
return;
|
|
}
|
|
|
|
switch (lifetime)
|
|
{
|
|
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT:
|
|
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
|
|
break;
|
|
|
|
default:
|
|
wl_resource_post_error (resource,
|
|
WL_DISPLAY_ERROR_INVALID_OBJECT,
|
|
"Invalid constraint lifetime");
|
|
return;
|
|
}
|
|
|
|
constraint = meta_wayland_pointer_constraint_new (surface, seat,
|
|
region,
|
|
lifetime,
|
|
cr, grab_interface);
|
|
if (constraint == NULL)
|
|
{
|
|
wl_client_post_no_memory (client);
|
|
return;
|
|
}
|
|
|
|
surface_add_pointer_constraint (surface, constraint);
|
|
|
|
wl_resource_set_implementation (cr, implementation, constraint,
|
|
pointer_constraint_resource_destroyed);
|
|
|
|
meta_wayland_pointer_constraint_maybe_enable (constraint);
|
|
}
|
|
|
|
static void
|
|
locked_pointer_destroy (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_resource_get_user_data (resource);
|
|
gboolean warp_pointer = FALSE;
|
|
int warp_x, warp_y;
|
|
|
|
if (constraint && constraint->is_enabled && constraint->hint_set &&
|
|
is_within_constraint_region (constraint,
|
|
constraint->x_hint,
|
|
constraint->y_hint))
|
|
{
|
|
float sx, sy;
|
|
float x, y;
|
|
|
|
sx = (float)wl_fixed_to_double (constraint->x_hint);
|
|
sy = (float)wl_fixed_to_double (constraint->y_hint);
|
|
meta_wayland_surface_get_absolute_coordinates (constraint->surface,
|
|
sx, sy,
|
|
&x, &y);
|
|
warp_pointer = TRUE;
|
|
warp_x = (int) x;
|
|
warp_y = (int) y;
|
|
}
|
|
wl_resource_destroy (resource);
|
|
|
|
if (warp_pointer)
|
|
{
|
|
ClutterSeat *seat;
|
|
|
|
seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
|
|
clutter_seat_warp_pointer (seat, warp_x, warp_y);
|
|
}
|
|
}
|
|
|
|
static void
|
|
locked_pointer_set_cursor_position_hint (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
wl_fixed_t surface_x,
|
|
wl_fixed_t surface_y)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_resource_get_user_data (resource);
|
|
|
|
/* Ignore a set cursor hint that was already sent after the constraint
|
|
* was cancelled. */
|
|
if (!constraint || !constraint->resource || constraint->resource != resource)
|
|
return;
|
|
|
|
constraint->hint_set = TRUE;
|
|
constraint->x_hint = surface_x;
|
|
constraint->y_hint = surface_y;
|
|
}
|
|
|
|
static void
|
|
locked_pointer_set_region (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_resource *region_resource)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_resource_get_user_data (resource);
|
|
MetaWaylandRegion *region =
|
|
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
|
|
|
|
if (!constraint)
|
|
return;
|
|
|
|
meta_wayland_pointer_constraint_set_pending_region (constraint, region);
|
|
}
|
|
|
|
static const struct zwp_locked_pointer_v1_interface locked_pointer_interface = {
|
|
locked_pointer_destroy,
|
|
locked_pointer_set_cursor_position_hint,
|
|
locked_pointer_set_region,
|
|
};
|
|
|
|
static void
|
|
locked_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab,
|
|
MetaWaylandSurface *surface)
|
|
{
|
|
}
|
|
|
|
static void
|
|
locked_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab,
|
|
const ClutterEvent *event)
|
|
{
|
|
meta_wayland_pointer_send_relative_motion (grab->pointer, event);
|
|
meta_wayland_pointer_broadcast_frame (grab->pointer);
|
|
}
|
|
|
|
static void
|
|
locked_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab,
|
|
const ClutterEvent *event)
|
|
{
|
|
meta_wayland_pointer_send_button (grab->pointer, event);
|
|
}
|
|
|
|
static void
|
|
locked_pointer_grab_pointer_cancel (MetaWaylandPointerGrab *grab)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_container_of (grab, constraint, grab);
|
|
|
|
meta_wayland_pointer_constraint_deactivate (constraint);
|
|
}
|
|
|
|
static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface = {
|
|
locked_pointer_grab_pointer_focus,
|
|
locked_pointer_grab_pointer_motion,
|
|
locked_pointer_grab_pointer_button,
|
|
locked_pointer_grab_pointer_cancel,
|
|
};
|
|
|
|
static void
|
|
pointer_constraints_destroy (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
static void
|
|
pointer_constraints_lock_pointer (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t id,
|
|
struct wl_resource *surface_resource,
|
|
struct wl_resource *pointer_resource,
|
|
struct wl_resource *region_resource,
|
|
uint32_t lifetime)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
|
|
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
|
|
MetaWaylandRegion *region =
|
|
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
|
|
|
|
init_pointer_constraint (resource, id, surface, seat, region, lifetime,
|
|
&zwp_locked_pointer_v1_interface,
|
|
&locked_pointer_interface,
|
|
&locked_pointer_grab_interface);
|
|
}
|
|
|
|
static void
|
|
confined_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab,
|
|
MetaWaylandSurface *surface)
|
|
{
|
|
}
|
|
|
|
static void
|
|
confined_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab,
|
|
const ClutterEvent *event)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_container_of (grab, constraint, grab);
|
|
MetaWaylandPointer *pointer = grab->pointer;
|
|
|
|
g_assert (pointer->focus_surface);
|
|
g_assert (pointer->focus_surface == constraint->surface);
|
|
|
|
meta_wayland_pointer_send_motion (pointer, event);
|
|
}
|
|
|
|
static void
|
|
confined_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab,
|
|
const ClutterEvent *event)
|
|
{
|
|
meta_wayland_pointer_send_button (grab->pointer, event);
|
|
}
|
|
|
|
static void
|
|
confined_pointer_grab_pointer_cancel (MetaWaylandPointerGrab *grab)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_container_of (grab, constraint, grab);
|
|
|
|
meta_wayland_pointer_constraint_deactivate (constraint);
|
|
}
|
|
|
|
static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface = {
|
|
confined_pointer_grab_pointer_focus,
|
|
confined_pointer_grab_pointer_motion,
|
|
confined_pointer_grab_pointer_button,
|
|
confined_pointer_grab_pointer_cancel,
|
|
};
|
|
|
|
static void
|
|
confined_pointer_destroy (struct wl_client *client,
|
|
struct wl_resource *resource)
|
|
{
|
|
wl_resource_destroy (resource);
|
|
}
|
|
|
|
static void
|
|
confined_pointer_set_region (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
struct wl_resource *region_resource)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
wl_resource_get_user_data (resource);
|
|
MetaWaylandRegion *region =
|
|
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
|
|
|
|
if (!constraint)
|
|
return;
|
|
|
|
meta_wayland_pointer_constraint_set_pending_region (constraint, region);
|
|
}
|
|
|
|
static const struct zwp_confined_pointer_v1_interface confined_pointer_interface = {
|
|
confined_pointer_destroy,
|
|
confined_pointer_set_region,
|
|
};
|
|
|
|
static void
|
|
pointer_constraints_confine_pointer (struct wl_client *client,
|
|
struct wl_resource *resource,
|
|
uint32_t id,
|
|
struct wl_resource *surface_resource,
|
|
struct wl_resource *pointer_resource,
|
|
struct wl_resource *region_resource,
|
|
uint32_t lifetime)
|
|
{
|
|
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
|
MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
|
|
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
|
|
MetaWaylandRegion *region =
|
|
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
|
|
|
|
init_pointer_constraint (resource, id, surface, seat, region, lifetime,
|
|
&zwp_confined_pointer_v1_interface,
|
|
&confined_pointer_interface,
|
|
&confined_pointer_grab_interface);
|
|
|
|
}
|
|
|
|
static const struct zwp_pointer_constraints_v1_interface pointer_constraints = {
|
|
pointer_constraints_destroy,
|
|
pointer_constraints_lock_pointer,
|
|
pointer_constraints_confine_pointer,
|
|
};
|
|
|
|
static void
|
|
bind_pointer_constraints (struct wl_client *client,
|
|
void *data,
|
|
uint32_t version,
|
|
uint32_t id)
|
|
{
|
|
MetaWaylandCompositor *compositor = data;
|
|
struct wl_resource *resource;
|
|
|
|
resource = wl_resource_create (client,
|
|
&zwp_pointer_constraints_v1_interface,
|
|
1, id);
|
|
|
|
wl_resource_set_implementation (resource,
|
|
&pointer_constraints,
|
|
compositor,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_finalize (GObject *object)
|
|
{
|
|
MetaWaylandPointerConstraint *constraint =
|
|
META_WAYLAND_POINTER_CONSTRAINT (object);
|
|
|
|
g_clear_signal_handler (&constraint->pointer_focus_surface_handler_id,
|
|
constraint->seat->pointer);
|
|
|
|
G_OBJECT_CLASS (meta_wayland_pointer_constraint_parent_class)->finalize (object);
|
|
}
|
|
|
|
void
|
|
meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor)
|
|
{
|
|
if (!wl_global_create (compositor->wayland_display,
|
|
&zwp_pointer_constraints_v1_interface, 1,
|
|
compositor, bind_pointer_constraints))
|
|
g_error ("Could not create wp_pointer_constraints global");
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_init (MetaWaylandPointerConstraint *constraint)
|
|
{
|
|
}
|
|
|
|
static void
|
|
meta_wayland_pointer_constraint_class_init (MetaWaylandPointerConstraintClass *klass)
|
|
{
|
|
GObjectClass *object_class;
|
|
|
|
object_class = G_OBJECT_CLASS (klass);
|
|
object_class->finalize = meta_wayland_pointer_constraint_finalize;
|
|
|
|
quark_pending_constraint_state =
|
|
g_quark_from_static_string ("-meta-wayland-pointer-constraint-pending_state");
|
|
quark_surface_pointer_constraints_data =
|
|
g_quark_from_static_string ("-meta-wayland-surface-constraints-data");
|
|
}
|