/* -*- 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 */ #include "config.h" #include "wayland/meta-wayland-pointer-constraints.h" #include #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-subsurface.h" #include "wayland/meta-wayland-surface.h" #ifdef HAVE_XWAYLAND #include "wayland/meta-xwayland.h" #endif #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) { MetaDisplay *display = meta_window_get_display (window); MetaContext *context = meta_display_get_context (display); MetaWaylandCompositor *wayland_compositor = meta_context_get_wayland_compositor (context); 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); } #ifdef HAVE_XWAYLAND 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); } #endif 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); } #ifdef HAVE_XWAYLAND else if (meta_wayland_surface_is_xwayland (surface)) { data->window_associated_handler_id = g_signal_connect (surface->role, "window-associated", G_CALLBACK (window_associated), data); } #endif else { 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); #ifdef HAVE_XWAYLAND if (!window) { /* * Locks from Xwayland may come before we have had the opportunity to * associate the X11 Window with the wl_surface. * For subsurfaces the window of the ancestor might be gone already. */ #ifdef HAVE_XWAYLAND g_warn_if_fail (meta_wayland_surface_is_xwayland (constraint->surface) || META_IS_WAYLAND_SUBSURFACE (constraint->surface->role)); #endif return FALSE; } #endif if (window->unmanaging) return FALSE; if (constraint->seat->pointer->focus_surface != constraint->surface) return FALSE; if (meta_wayland_surface_is_xwayland (constraint->surface)) { MetaDisplay *display = meta_window_get_display (window); /* * 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; } 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 = meta_window_get_wayland_surface (window); 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_wayland_surface_is_xwayland (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; } MetaWaylandCompositor * meta_wayland_pointer_constraint_get_compositor (MetaWaylandPointerConstraint *constraint) { return constraint->surface->compositor; } 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"); }