mutter/src/wayland/meta-wayland-pointer-constraints.c
Carlos Garnacho 98ef6d0d05 wayland: Ensure pointer constraint region consistency
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>
2021-01-12 12:32:24 +01:00

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");
}