[PATCH weston 2/3] Implement pointer locking and confinement

Jonas Ådahl jadahl at gmail.com
Tue Mar 15 13:14:14 UTC 2016


This patch implements the wp_pointer_constraints protocol used for
locking or confining a pointer. It consists of a new global object with
two requests; one for locking the surface to a position, one for
confining the pointer to a given region.

In this patch, only the locking part is fully implemented as in
specified in the protocol, while confinement is only implemented for
when the union of the passed region and the input region of the confined
surface is a single rectangle.

Note that the pointer constraints protocol is still unstable and as
such has the unstable protocol naming conventions applied.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
Acked-by: Peter Hutterer <peter.hutterer at who-t.net>
---

The changes since last version include:

* Updated to the new protocol:
  - Added oneshot vs persistent constraint lifetime
  - Changed the constraint creation input from wl_seat to wl_pointer


 Makefile.am               |   4 +-
 src/compositor.c          |  11 +
 src/compositor.h          |  44 +++
 src/input.c               | 850 ++++++++++++++++++++++++++++++++++++++++++++--
 xwayland/window-manager.c |   3 +-
 5 files changed, 888 insertions(+), 24 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 9a6bd9b..12ecc3c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -128,7 +128,9 @@ nodist_weston_SOURCES =					\
 	protocol/linux-dmabuf-unstable-v1-protocol.c	\
 	protocol/linux-dmabuf-unstable-v1-server-protocol.h		\
 	protocol/relative-pointer-unstable-v1-protocol.c		\
-	protocol/relative-pointer-unstable-v1-server-protocol.h
+	protocol/relative-pointer-unstable-v1-server-protocol.h		\
+	protocol/pointer-constraints-unstable-v1-protocol.c		\
+	protocol/pointer-constraints-unstable-v1-server-protocol.h
 
 BUILT_SOURCES += $(nodist_weston_SOURCES)
 
diff --git a/src/compositor.c b/src/compositor.c
index 68ce600..e2492db 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -586,6 +586,7 @@ weston_surface_create(struct weston_compositor *compositor)
 		return NULL;
 
 	wl_signal_init(&surface->destroy_signal);
+	wl_signal_init(&surface->commit_signal);
 
 	surface->compositor = compositor;
 	surface->ref_count = 1;
@@ -612,6 +613,8 @@ weston_surface_create(struct weston_compositor *compositor)
 	weston_matrix_init(&surface->buffer_to_surface_matrix);
 	weston_matrix_init(&surface->surface_to_buffer_matrix);
 
+	wl_list_init(&surface->pointer_constraints);
+
 	return surface;
 }
 
@@ -1928,6 +1931,7 @@ weston_surface_destroy(struct weston_surface *surface)
 {
 	struct weston_frame_callback *cb, *next;
 	struct weston_view *ev, *nv;
+	struct weston_pointer_constraint *constraint, *next_constraint;
 
 	if (--surface->ref_count > 0)
 		return;
@@ -1955,6 +1959,11 @@ weston_surface_destroy(struct weston_surface *surface)
 
 	weston_presentation_feedback_discard_list(&surface->feedback_list);
 
+	wl_list_for_each_safe(constraint, next_constraint,
+			      &surface->pointer_constraints,
+			      link)
+		weston_pointer_constraint_destroy(constraint);
+
 	free(surface);
 }
 
@@ -2926,6 +2935,8 @@ weston_surface_commit_state(struct weston_surface *surface,
 	wl_list_insert_list(&surface->feedback_list,
 			    &state->feedback_list);
 	wl_list_init(&state->feedback_list);
+
+	wl_signal_emit(&surface->commit_signal, surface);
 }
 
 static void
diff --git a/src/compositor.h b/src/compositor.h
index ad98c67..20d2467 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -45,6 +45,8 @@ extern "C" {
 #include "zalloc.h"
 #include "timeline-object.h"
 
+#include "pointer-constraints-unstable-v1-server-protocol.h"
+
 struct weston_transform {
 	struct weston_matrix matrix;
 	struct wl_list link;
@@ -58,6 +60,7 @@ struct weston_output;
 struct input_method;
 struct weston_pointer;
 struct linux_dmabuf_buffer;
+struct weston_pointer_constraint;
 
 enum weston_keyboard_modifier {
 	MODIFIER_CTRL = (1 << 0),
@@ -368,6 +371,7 @@ struct weston_pointer {
 	struct wl_listener focus_resource_listener;
 	struct wl_signal focus_signal;
 	struct wl_signal motion_signal;
+	struct wl_signal destroy_signal;
 
 	struct weston_view *sprite;
 	struct wl_listener sprite_destroy_listener;
@@ -449,6 +453,9 @@ void
 weston_pointer_set_default_grab(struct weston_pointer *pointer,
 		const struct weston_pointer_grab_interface *interface);
 
+void
+weston_pointer_constraint_destroy(struct weston_pointer_constraint *constraint);
+
 struct weston_keyboard *
 weston_keyboard_create(void);
 void
@@ -790,6 +797,8 @@ struct weston_compositor {
 
 	unsigned int activate_serial;
 
+	struct wl_global *pointer_constraints;
+
 	int exit_code;
 
 	void *user_data;
@@ -989,10 +998,42 @@ struct weston_surface_state {
 	struct weston_buffer_viewport buffer_viewport;
 };
 
+struct weston_surface_activation_data {
+	struct weston_surface *surface;
+	struct weston_seat *seat;
+};
+
+struct weston_pointer_constraint {
+	struct wl_list link;
+
+	struct weston_surface *surface;
+	struct weston_view *view;
+	struct wl_resource *resource;
+	struct weston_pointer_grab grab;
+	struct weston_pointer *pointer;
+	enum zwp_pointer_constraints_v1_lifetime lifetime;
+
+	pixman_region32_t region;
+	pixman_region32_t region_pending;
+	bool region_is_pending;
+
+	wl_fixed_t hint_x;
+	wl_fixed_t hint_y;
+	wl_fixed_t hint_x_pending;
+	wl_fixed_t hint_y_pending;
+	bool hint_is_pending;
+
+	struct wl_listener pointer_destroy_listener;
+	struct wl_listener surface_destroy_listener;
+	struct wl_listener surface_commit_listener;
+	struct wl_listener surface_activate_listener;
+};
+
 struct weston_surface {
 	struct wl_resource *resource;
 	struct wl_signal destroy_signal; /* callback argument: this surface */
 	struct weston_compositor *compositor;
+	struct wl_signal commit_signal;
 
 	/** Damage in local coordinates from the client, for tex upload. */
 	pixman_region32_t damage;
@@ -1072,6 +1113,9 @@ struct weston_surface {
 	const char *role_name;
 
 	struct weston_timeline_object timeline;
+
+	/* An list of per seat pointer constraints. */
+	struct wl_list pointer_constraints;
 };
 
 struct weston_subsurface {
diff --git a/src/input.c b/src/input.c
index b5a3273..f141c25 100644
--- a/src/input.c
+++ b/src/input.c
@@ -25,6 +25,7 @@
 
 #include "config.h"
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
@@ -38,6 +39,15 @@
 #include "shared/os-compatibility.h"
 #include "compositor.h"
 #include "protocol/relative-pointer-unstable-v1-server-protocol.h"
+#include "protocol/pointer-constraints-unstable-v1-server-protocol.h"
+
+enum pointer_constraint_type {
+	POINTER_CONSTRAINT_TYPE_LOCK,
+	POINTER_CONSTRAINT_TYPE_CONFINE,
+};
+
+static void
+maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint);
 
 static void
 empty_region(pixman_region32_t *region)
@@ -46,6 +56,13 @@ empty_region(pixman_region32_t *region)
 	pixman_region32_init(region);
 }
 
+static void
+region_init_infinite(pixman_region32_t *region)
+{
+	pixman_region32_init_rect(region, INT32_MIN, INT32_MIN,
+				  UINT32_MAX, UINT32_MAX);
+}
+
 static struct weston_pointer_client *
 weston_pointer_client_create(struct wl_client *client)
 {
@@ -340,12 +357,25 @@ weston_pointer_send_relative_motion(struct weston_pointer *pointer,
 }
 
 static void
+weston_pointer_send_motion(struct weston_pointer *pointer, uint32_t time,
+			   wl_fixed_t sx, wl_fixed_t sy)
+{
+	struct wl_list *resource_list;
+	struct wl_resource *resource;
+
+	if (!pointer->focus_client)
+		return;
+
+	resource_list = &pointer->focus_client->pointer_resources;
+	wl_resource_for_each(resource, resource_list)
+		wl_pointer_send_motion(resource, time, sx, sy);
+}
+
+static void
 default_grab_pointer_motion(struct weston_pointer_grab *grab, uint32_t time,
 			    struct weston_pointer_motion_event *event)
 {
 	struct weston_pointer *pointer = grab->pointer;
-	struct wl_list *resource_list;
-	struct wl_resource *resource;
 	wl_fixed_t x, y;
 	wl_fixed_t old_sx = pointer->sx;
 	wl_fixed_t old_sy = pointer->sy;
@@ -358,44 +388,51 @@ default_grab_pointer_motion(struct weston_pointer_grab *grab, uint32_t time,
 
 	weston_pointer_move(pointer, event);
 
-	if (pointer->focus_client &&
-	    (old_sx != pointer->sx || old_sy != pointer->sy)) {
-		resource_list = &pointer->focus_client->pointer_resources;
-		wl_resource_for_each(resource, resource_list) {
-			wl_pointer_send_motion(resource, time,
-					       pointer->sx, pointer->sy);
-		}
+	if (old_sx != pointer->sx || old_sy != pointer->sy) {
+		weston_pointer_send_motion(pointer, time,
+					   pointer->sx, pointer->sy);
 	}
 
 	weston_pointer_send_relative_motion(pointer, time, event);
 }
 
 static void
-default_grab_pointer_button(struct weston_pointer_grab *grab,
-			    uint32_t time, uint32_t button, uint32_t state_w)
+weston_pointer_send_button(struct weston_pointer *pointer,
+			   uint32_t time, uint32_t button, uint32_t state_w)
 {
-	struct weston_pointer *pointer = grab->pointer;
-	struct weston_compositor *compositor = pointer->seat->compositor;
-	struct weston_view *view;
+	struct wl_display *display = pointer->seat->compositor->wl_display;
+	struct wl_list *resource_list;
 	struct wl_resource *resource;
 	uint32_t serial;
-	enum wl_pointer_button_state state = state_w;
-	struct wl_display *display = compositor->wl_display;
-	wl_fixed_t sx, sy;
-	struct wl_list *resource_list = NULL;
 
-	if (pointer->focus_client)
-		resource_list = &pointer->focus_client->pointer_resources;
+	if (!pointer->focus_client)
+		return;
+
+	resource_list = &pointer->focus_client->pointer_resources;
 	if (resource_list && !wl_list_empty(resource_list)) {
 		resource_list = &pointer->focus_client->pointer_resources;
 		serial = wl_display_next_serial(display);
-		wl_resource_for_each(resource, resource_list)
+		wl_resource_for_each(resource, resource_list) {
 			wl_pointer_send_button(resource,
 					       serial,
 					       time,
 					       button,
 					       state_w);
+		}
 	}
+}
+
+static void
+default_grab_pointer_button(struct weston_pointer_grab *grab,
+			    uint32_t time, uint32_t button, uint32_t state_w)
+{
+	struct weston_pointer *pointer = grab->pointer;
+	struct weston_compositor *compositor = pointer->seat->compositor;
+	struct weston_view *view;
+	enum wl_pointer_button_state state = state_w;
+	wl_fixed_t sx, sy;
+
+	weston_pointer_send_button(pointer, time, button, state_w);
 
 	if (pointer->button_count == 0 &&
 	    state == WL_POINTER_BUTTON_STATE_RELEASED) {
@@ -792,6 +829,7 @@ weston_pointer_create(struct weston_seat *seat)
 	wl_signal_init(&pointer->motion_signal);
 	wl_signal_init(&pointer->focus_signal);
 	wl_list_init(&pointer->focus_view_listener.link);
+	wl_signal_init(&pointer->destroy_signal);
 
 	pointer->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
 
@@ -813,6 +851,8 @@ weston_pointer_create(struct weston_seat *seat)
 WL_EXPORT void
 weston_pointer_destroy(struct weston_pointer *pointer)
 {
+	wl_signal_emit(&pointer->destroy_signal, pointer);
+
 	if (pointer->sprite)
 		pointer_unmap_sprite(pointer);
 
@@ -1382,6 +1422,7 @@ weston_surface_activate(struct weston_surface *surface,
 {
 	struct weston_compositor *compositor = seat->compositor;
 	struct weston_keyboard *keyboard = weston_seat_get_keyboard(seat);
+	struct weston_surface_activation_data activation_data;
 
 	inc_activate_serial(compositor);
 
@@ -1390,7 +1431,11 @@ weston_surface_activate(struct weston_surface *surface,
 		wl_data_device_set_keyboard_focus(seat);
 	}
 
-	wl_signal_emit(&compositor->activate_signal, surface);
+	activation_data = (struct weston_surface_activation_data) {
+		.surface = surface,
+		.seat = seat,
+	};
+	wl_signal_emit(&compositor->activate_signal, &activation_data);
 }
 
 WL_EXPORT void
@@ -2897,6 +2942,70 @@ weston_seat_get_pointer(struct weston_seat *seat)
 	return NULL;
 }
 
+static const struct zwp_locked_pointer_v1_interface locked_pointer_interface;
+static const struct zwp_confined_pointer_v1_interface confined_pointer_interface;
+
+static enum pointer_constraint_type
+pointer_constraint_get_type(struct weston_pointer_constraint *constraint)
+{
+	if (wl_resource_instance_of(constraint->resource,
+				    &zwp_locked_pointer_v1_interface,
+				    &locked_pointer_interface)) {
+		return POINTER_CONSTRAINT_TYPE_LOCK;
+	} else if (wl_resource_instance_of(constraint->resource,
+					   &zwp_confined_pointer_v1_interface,
+					   &confined_pointer_interface)) {
+		return POINTER_CONSTRAINT_TYPE_CONFINE;
+	}
+
+	abort();
+	return 0;
+}
+
+static void
+pointer_constraint_notify_activated(struct weston_pointer_constraint *constraint)
+{
+	struct wl_resource *resource = constraint->resource;
+
+	switch (pointer_constraint_get_type(constraint)) {
+	case POINTER_CONSTRAINT_TYPE_LOCK:
+		zwp_locked_pointer_v1_send_locked(resource);
+		break;
+	case POINTER_CONSTRAINT_TYPE_CONFINE:
+		zwp_confined_pointer_v1_send_confined(resource);
+		break;
+	}
+}
+
+static void
+pointer_constraint_notify_deactivated(struct weston_pointer_constraint *constraint)
+{
+	struct wl_resource *resource = constraint->resource;
+
+	switch (pointer_constraint_get_type(constraint)) {
+	case POINTER_CONSTRAINT_TYPE_LOCK:
+		zwp_locked_pointer_v1_send_unlocked(resource);
+		break;
+	case POINTER_CONSTRAINT_TYPE_CONFINE:
+		zwp_confined_pointer_v1_send_unconfined(resource);
+		break;
+	}
+}
+
+static struct weston_pointer_constraint *
+get_pointer_constraint_for_pointer(struct weston_surface *surface,
+				   struct weston_pointer *pointer)
+{
+	struct weston_pointer_constraint *constraint;
+
+	wl_list_for_each(constraint, &surface->pointer_constraints, link) {
+		if (constraint->pointer == pointer)
+			return constraint;
+	}
+
+	return NULL;
+}
+
 /** Get a seat's touch pointer
  *
  * \param seat The seat to query
@@ -2919,6 +3028,698 @@ weston_seat_get_touch(struct weston_seat *seat)
 	return NULL;
 }
 
+static void
+enable_pointer_constraint(struct weston_pointer_constraint *constraint,
+			  struct weston_view *view)
+{
+	assert(constraint->view == NULL);
+	constraint->view = view;
+	pointer_constraint_notify_activated(constraint);
+	weston_pointer_start_grab(constraint->pointer, &constraint->grab);
+}
+
+static bool
+is_pointer_constraint_enabled(struct weston_pointer_constraint *constraint)
+{
+	return constraint->view != NULL;
+}
+
+static void
+weston_pointer_constraint_disable(struct weston_pointer_constraint *constraint)
+{
+	constraint->view = NULL;
+	pointer_constraint_notify_deactivated(constraint);
+	weston_pointer_end_grab(constraint->grab.pointer);
+}
+
+void
+weston_pointer_constraint_destroy(struct weston_pointer_constraint *constraint)
+{
+	if (is_pointer_constraint_enabled(constraint))
+		weston_pointer_constraint_disable(constraint);
+
+	wl_list_remove(&constraint->pointer_destroy_listener.link);
+	wl_list_remove(&constraint->surface_destroy_listener.link);
+	wl_list_remove(&constraint->surface_commit_listener.link);
+	wl_list_remove(&constraint->surface_activate_listener.link);
+
+	wl_resource_set_user_data(constraint->resource, NULL);
+	pixman_region32_fini(&constraint->region);
+	wl_list_remove(&constraint->link);
+	free(constraint);
+}
+
+static void
+disable_pointer_constraint(struct weston_pointer_constraint *constraint)
+{
+	switch (constraint->lifetime) {
+	case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT:
+		weston_pointer_constraint_destroy(constraint);
+		break;
+	case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
+		weston_pointer_constraint_disable(constraint);
+		break;
+	}
+}
+
+static bool
+is_within_constraint_region(struct weston_pointer_constraint *constraint,
+			    wl_fixed_t sx, wl_fixed_t sy)
+{
+	struct weston_surface *surface = constraint->surface;
+	pixman_region32_t constraint_region;
+	bool result;
+
+	pixman_region32_init(&constraint_region);
+	pixman_region32_intersect(&constraint_region,
+				  &surface->input,
+				  &constraint->region);
+	result = pixman_region32_contains_point(&constraint_region,
+						wl_fixed_to_int(sx),
+						wl_fixed_to_int(sy),
+						NULL);
+	pixman_region32_fini(&constraint_region);
+
+	return result;
+}
+
+static void
+maybe_enable_pointer_constraint(struct weston_pointer_constraint *constraint)
+{
+	struct weston_surface *surface = constraint->surface;
+	struct weston_view *vit;
+	struct weston_view *view = NULL;
+	struct weston_pointer *pointer = constraint->pointer;
+	struct weston_keyboard *keyboard;
+	struct weston_seat *seat = pointer->seat;
+	int32_t x, y;
+
+	/* Postpone if no view of the surface was most recently clicked. */
+	wl_list_for_each(vit, &surface->views, surface_link) {
+		if (vit->click_to_activate_serial ==
+		    surface->compositor->activate_serial) {
+			view = vit;
+		}
+	}
+	if (view == NULL)
+		return;
+
+	/* Postpone if surface doesn't have keyboard focus. */
+	keyboard = weston_seat_get_keyboard(seat);
+	if (!keyboard || keyboard->focus != surface)
+		return;
+
+	/* Postpone constraint if the pointer is not within the
+	 * constraint region.
+	 */
+	weston_view_from_global(view,
+				wl_fixed_to_int(pointer->x),
+				wl_fixed_to_int(pointer->y),
+				&x, &y);
+	if (!is_within_constraint_region(constraint,
+					 wl_fixed_from_int(x),
+					 wl_fixed_from_int(y)))
+		return;
+
+	enable_pointer_constraint(constraint, view);
+}
+
+static void
+locked_pointer_grab_pointer_focus(struct weston_pointer_grab *grab)
+{
+}
+
+static void
+locked_pointer_grab_pointer_motion(struct weston_pointer_grab *grab,
+				   uint32_t time,
+				   struct weston_pointer_motion_event *event)
+{
+	weston_pointer_send_relative_motion(grab->pointer, time, event);
+}
+
+static void
+locked_pointer_grab_pointer_button(struct weston_pointer_grab *grab,
+				   uint32_t time,
+				   uint32_t button,
+				   uint32_t state_w)
+{
+	weston_pointer_send_button(grab->pointer, time, button, state_w);
+}
+
+static void
+locked_pointer_grab_pointer_axis(struct weston_pointer_grab *grab,
+				 uint32_t time,
+				 struct weston_pointer_axis_event *event)
+{
+	weston_pointer_send_axis(grab->pointer, time, event);
+}
+
+static void
+locked_pointer_grab_pointer_axis_source(struct weston_pointer_grab *grab,
+					uint32_t source)
+{
+	weston_pointer_send_axis_source(grab->pointer, source);
+}
+
+static void
+locked_pointer_grab_pointer_frame(struct weston_pointer_grab *grab)
+{
+	weston_pointer_send_frame(grab->pointer);
+}
+
+static void
+locked_pointer_grab_pointer_cancel(struct weston_pointer_grab *grab)
+{
+	struct weston_pointer_constraint *constraint =
+		container_of(grab, struct weston_pointer_constraint, grab);
+
+	disable_pointer_constraint(constraint);
+}
+
+static const struct weston_pointer_grab_interface
+				locked_pointer_grab_interface = {
+	locked_pointer_grab_pointer_focus,
+	locked_pointer_grab_pointer_motion,
+	locked_pointer_grab_pointer_button,
+	locked_pointer_grab_pointer_axis,
+	locked_pointer_grab_pointer_axis_source,
+	locked_pointer_grab_pointer_frame,
+	locked_pointer_grab_pointer_cancel,
+};
+
+static void
+pointer_constraint_constrain_resource_destroyed(struct wl_resource *resource)
+{
+	struct weston_pointer_constraint *constraint =
+		wl_resource_get_user_data(resource);
+
+	if (!constraint)
+		return;
+
+	weston_pointer_constraint_destroy(constraint);
+}
+
+static void
+pointer_constraint_surface_activate(struct wl_listener *listener, void *data)
+{
+	struct weston_surface_activation_data *activation = data;
+	struct weston_pointer *pointer;
+	struct weston_surface *focus = activation->surface;
+	struct weston_pointer_constraint *constraint =
+		container_of(listener, struct weston_pointer_constraint,
+			     surface_activate_listener);
+	bool is_constraint_surface;
+
+	pointer = weston_seat_get_pointer(activation->seat);
+	if (!pointer)
+		return;
+
+	is_constraint_surface =
+		get_pointer_constraint_for_pointer(focus, pointer) == constraint;
+
+	if (is_constraint_surface &&
+	    !is_pointer_constraint_enabled(constraint))
+		maybe_enable_pointer_constraint(constraint);
+	else if (!is_constraint_surface &&
+		 is_pointer_constraint_enabled(constraint))
+		disable_pointer_constraint(constraint);
+}
+
+static void
+pointer_constraint_pointer_destroyed(struct wl_listener *listener, void *data)
+{
+	struct weston_pointer_constraint *constraint =
+		container_of(listener, struct weston_pointer_constraint,
+			     pointer_destroy_listener);
+
+	weston_pointer_constraint_destroy(constraint);
+}
+
+static void
+pointer_constraint_surface_destroyed(struct wl_listener *listener, void *data)
+{
+	struct weston_pointer_constraint *constraint =
+		container_of(listener, struct weston_pointer_constraint,
+			     surface_destroy_listener);
+
+	weston_pointer_constraint_destroy(constraint);
+}
+
+static void
+pointer_constraint_surface_committed(struct wl_listener *listener, void *data)
+{
+	struct weston_pointer_constraint *constraint =
+		container_of(listener, struct weston_pointer_constraint,
+			     surface_commit_listener);
+
+	if (constraint->region_is_pending) {
+		constraint->region_is_pending = false;
+		pixman_region32_copy(&constraint->region,
+				     &constraint->region_pending);
+		pixman_region32_fini(&constraint->region_pending);
+		pixman_region32_init(&constraint->region_pending);
+	}
+
+	if (constraint->hint_is_pending) {
+		constraint->hint_is_pending = false;
+
+		constraint->hint_is_pending = true;
+		constraint->hint_x = constraint->hint_x_pending;
+		constraint->hint_y = constraint->hint_y_pending;
+	}
+
+	if (pointer_constraint_get_type(constraint) ==
+	    POINTER_CONSTRAINT_TYPE_CONFINE &&
+	    is_pointer_constraint_enabled(constraint))
+		maybe_warp_confined_pointer(constraint);
+}
+
+static struct weston_pointer_constraint *
+weston_pointer_constraint_create(struct weston_surface *surface,
+				 struct weston_pointer *pointer,
+				 struct weston_region *region,
+				 enum zwp_pointer_constraints_v1_lifetime lifetime,
+				 struct wl_resource *cr,
+				 const struct weston_pointer_grab_interface *grab_interface)
+{
+	struct weston_pointer_constraint *constraint;
+
+	constraint = zalloc(sizeof *constraint);
+	if (!constraint)
+		return NULL;
+
+	constraint->lifetime = lifetime;
+	pixman_region32_init(&constraint->region);
+	pixman_region32_init(&constraint->region_pending);
+	wl_list_insert(&surface->pointer_constraints, &constraint->link);
+	constraint->surface = surface;
+	constraint->pointer = pointer;
+	constraint->resource = cr;
+	constraint->grab.interface = grab_interface;
+	if (region) {
+		pixman_region32_copy(&constraint->region,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&constraint->region);
+		region_init_infinite(&constraint->region);
+	}
+
+	constraint->surface_activate_listener.notify =
+		pointer_constraint_surface_activate;
+	constraint->surface_destroy_listener.notify =
+		pointer_constraint_surface_destroyed;
+	constraint->surface_commit_listener.notify =
+		pointer_constraint_surface_committed;
+	constraint->pointer_destroy_listener.notify =
+		pointer_constraint_pointer_destroyed;
+
+	wl_signal_add(&surface->compositor->activate_signal,
+		      &constraint->surface_activate_listener);
+	wl_signal_add(&pointer->destroy_signal,
+		      &constraint->pointer_destroy_listener);
+	wl_signal_add(&surface->destroy_signal,
+		      &constraint->surface_destroy_listener);
+	wl_signal_add(&surface->commit_signal,
+		      &constraint->surface_commit_listener);
+
+	return constraint;
+}
+
+static void
+init_pointer_constraint(struct wl_resource *pointer_constraints_resource,
+			uint32_t id,
+			struct weston_surface *surface,
+			struct weston_pointer *pointer,
+			struct weston_region *region,
+			enum zwp_pointer_constraints_v1_lifetime lifetime,
+			const struct wl_interface *interface,
+			const void *implementation,
+			const struct weston_pointer_grab_interface *grab_interface)
+{
+	struct wl_client *client =
+		wl_resource_get_client(pointer_constraints_resource);
+	struct wl_resource *cr;
+	struct weston_pointer_constraint *constraint;
+
+	if (get_pointer_constraint_for_pointer(surface, pointer)) {
+		wl_resource_post_error(pointer_constraints_resource,
+				       ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED,
+				       "the pointer has a lock/confine request on this surface");
+		return;
+	}
+
+	cr = wl_resource_create(client, interface,
+				wl_resource_get_version(pointer_constraints_resource),
+				id);
+	if (cr == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	constraint = weston_pointer_constraint_create(surface, pointer,
+						      region, lifetime,
+						      cr, grab_interface);
+	if (constraint == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(cr, implementation, constraint,
+				       pointer_constraint_constrain_resource_destroyed);
+
+	maybe_enable_pointer_constraint(constraint);
+}
+
+static void
+pointer_constraints_destroy(struct wl_client *client,
+			    struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static void
+locked_pointer_destroy(struct wl_client *client,
+		       struct wl_resource *resource)
+{
+	struct weston_pointer_constraint *constraint =
+		wl_resource_get_user_data(resource);
+	wl_fixed_t x, y;
+
+	if (constraint && constraint->view && constraint->hint_is_pending &&
+	    is_within_constraint_region(constraint,
+					constraint->hint_x,
+					constraint->hint_y)) {
+		weston_view_to_global_fixed(constraint->view,
+					    constraint->hint_x,
+					    constraint->hint_y,
+					    &x, &y);
+		weston_pointer_move_to(constraint->pointer, x, y);
+	}
+	wl_resource_destroy(resource);
+}
+
+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)
+{
+	struct weston_pointer_constraint *constraint =
+		wl_resource_get_user_data(resource);
+
+	/* Ignore a set cursor hint that was sent after the lock was cancelled.
+	 */
+	if (!constraint->resource ||
+	    constraint->resource != resource)
+		return;
+
+	constraint->hint_is_pending = true;
+	constraint->hint_x_pending = surface_x;
+	constraint->hint_y_pending = surface_y;
+}
+
+static void
+locked_pointer_set_region(struct wl_client *client,
+			  struct wl_resource *resource,
+			  struct wl_resource *region_resource)
+{
+	struct weston_pointer_constraint *constraint =
+		wl_resource_get_user_data(resource);
+	struct weston_region *region = region_resource ?
+		wl_resource_get_user_data(region_resource) : NULL;
+
+	if (region) {
+		pixman_region32_copy(&constraint->region_pending,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&constraint->region_pending);
+		region_init_infinite(&constraint->region_pending);
+	}
+	constraint->region_is_pending = true;
+}
+
+
+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
+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)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+	struct weston_pointer *pointer = wl_resource_get_user_data(pointer_resource);
+	struct weston_region *region = region_resource ?
+		wl_resource_get_user_data(region_resource) : NULL;
+
+	init_pointer_constraint(resource, id, surface, pointer, region, lifetime,
+				&zwp_locked_pointer_v1_interface,
+				&locked_pointer_interface,
+				&locked_pointer_grab_interface);
+}
+
+static void
+confined_pointer_grab_pointer_focus(struct weston_pointer_grab *grab)
+{
+}
+
+static void
+weston_pointer_clamp_event_to_region(struct weston_pointer *pointer,
+				     struct weston_pointer_motion_event *event,
+				     pixman_region32_t *region,
+				     wl_fixed_t *clamped_x,
+				     wl_fixed_t *clamped_y)
+{
+	wl_fixed_t x, y;
+	wl_fixed_t sx, sy;
+	wl_fixed_t min_sx = wl_fixed_from_int(region->extents.x1);
+	wl_fixed_t max_sx = wl_fixed_from_int(region->extents.x2 - 1);
+	wl_fixed_t max_sy = wl_fixed_from_int(region->extents.y2 - 1);
+	wl_fixed_t min_sy = wl_fixed_from_int(region->extents.y1);
+
+	weston_pointer_motion_to_abs(pointer, event, &x, &y);
+	weston_view_from_global_fixed(pointer->focus, x, y, &sx, &sy);
+
+	if (sx < min_sx)
+		sx = min_sx;
+	else if (sx > max_sx)
+		sx = max_sx;
+
+	if (sy < min_sy)
+		sy = min_sy;
+	else if (sy > max_sy)
+		sy = max_sy;
+
+	weston_view_to_global_fixed(pointer->focus, sx, sy,
+				    clamped_x, clamped_y);
+}
+
+static void
+maybe_warp_confined_pointer(struct weston_pointer_constraint *constraint)
+{
+	wl_fixed_t x;
+	wl_fixed_t y;
+	wl_fixed_t sx;
+	wl_fixed_t sy;
+
+	weston_view_from_global_fixed(constraint->view,
+				      constraint->pointer->x,
+				      constraint->pointer->y,
+				      &sx,
+				      &sy);
+
+	if (!is_within_constraint_region(constraint, sx, sy)) {
+		pixman_region32_t *region = &constraint->region;
+		wl_fixed_t min_sx = wl_fixed_from_int(region->extents.x1);
+		wl_fixed_t max_sx = wl_fixed_from_int(region->extents.x2 - 1);
+		wl_fixed_t max_sy = wl_fixed_from_int(region->extents.y2 - 1);
+		wl_fixed_t min_sy = wl_fixed_from_int(region->extents.y1);
+
+		if (sx < min_sx)
+			sx = min_sx;
+		else if (sx > max_sx)
+			sx = max_sx;
+
+		if (sy < min_sy)
+			sy = min_sy;
+		else if (sy > max_sy)
+			sy = max_sy;
+
+		weston_view_to_global_fixed(constraint->view, sx, sy, &x, &y);
+		weston_pointer_move_to(constraint->pointer, x, y);
+	}
+}
+
+static void
+confined_pointer_grab_pointer_motion(struct weston_pointer_grab *grab,
+				     uint32_t time,
+				     struct weston_pointer_motion_event *event)
+{
+	struct weston_pointer_constraint *constraint =
+		container_of(grab, struct weston_pointer_constraint, grab);
+	struct weston_pointer *pointer = grab->pointer;
+	struct weston_surface *surface;
+	wl_fixed_t x, y;
+	wl_fixed_t old_sx = pointer->sx;
+	wl_fixed_t old_sy = pointer->sy;
+	pixman_region32_t confine_region;
+
+	assert(pointer->focus);
+	assert(pointer->focus->surface == constraint->surface);
+
+	surface = pointer->focus->surface;
+
+	pixman_region32_init(&confine_region);
+	pixman_region32_intersect(&confine_region,
+				  &surface->input,
+				  &constraint->region);
+	weston_pointer_clamp_event_to_region(pointer, event,
+					     &confine_region, &x, &y);
+	weston_pointer_move_to(pointer, x, y);
+	pixman_region32_fini(&confine_region);
+
+	weston_view_from_global_fixed(pointer->focus, x, y,
+				      &pointer->sx, &pointer->sy);
+
+	if (old_sx != pointer->sx || old_sy != pointer->sy) {
+		weston_pointer_send_motion(pointer, time,
+					   pointer->sx, pointer->sy);
+	}
+
+	weston_pointer_send_relative_motion(pointer, time, event);
+}
+
+static void
+confined_pointer_grab_pointer_button(struct weston_pointer_grab *grab,
+				     uint32_t time,
+				     uint32_t button,
+				     uint32_t state_w)
+{
+	weston_pointer_send_button(grab->pointer, time, button, state_w);
+}
+
+static void
+confined_pointer_grab_pointer_axis(struct weston_pointer_grab *grab,
+				   uint32_t time,
+				   struct weston_pointer_axis_event *event)
+{
+	weston_pointer_send_axis(grab->pointer, time, event);
+}
+
+static void
+confined_pointer_grab_pointer_axis_source(struct weston_pointer_grab *grab,
+					  uint32_t source)
+{
+	weston_pointer_send_axis_source(grab->pointer, source);
+}
+
+static void
+confined_pointer_grab_pointer_frame(struct weston_pointer_grab *grab)
+{
+	weston_pointer_send_frame(grab->pointer);
+}
+
+static void
+confined_pointer_grab_pointer_cancel(struct weston_pointer_grab *grab)
+{
+	struct weston_pointer_constraint *constraint =
+		container_of(grab, struct weston_pointer_constraint, grab);
+
+	disable_pointer_constraint(constraint);
+}
+
+static const struct weston_pointer_grab_interface
+				confined_pointer_grab_interface = {
+	confined_pointer_grab_pointer_focus,
+	confined_pointer_grab_pointer_motion,
+	confined_pointer_grab_pointer_button,
+	confined_pointer_grab_pointer_axis,
+	confined_pointer_grab_pointer_axis_source,
+	confined_pointer_grab_pointer_frame,
+	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)
+{
+	struct weston_pointer_constraint *constraint =
+		wl_resource_get_user_data(resource);
+	struct weston_region *region = region_resource ?
+		wl_resource_get_user_data(region_resource) : NULL;
+
+	if (region) {
+		pixman_region32_copy(&constraint->region_pending,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&constraint->region_pending);
+		region_init_infinite(&constraint->region_pending);
+	}
+	constraint->region_is_pending = true;
+}
+
+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)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+	struct weston_pointer *pointer = wl_resource_get_user_data(pointer_resource);
+	struct weston_region *region = region_resource ?
+		wl_resource_get_user_data(region_resource) : NULL;
+
+	init_pointer_constraint(resource, id, surface, pointer, region, lifetime,
+				&zwp_confined_pointer_v1_interface,
+				&confined_pointer_interface,
+				&confined_pointer_grab_interface);
+}
+
+static const struct zwp_pointer_constraints_v1_interface pointer_constraints_interface = {
+	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)
+{
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client,
+				      &zwp_pointer_constraints_v1_interface,
+				      1, id);
+
+	wl_resource_set_implementation(resource, &pointer_constraints_interface,
+				       NULL, NULL);
+}
+
 int
 weston_input_init(struct weston_compositor *compositor)
 {
@@ -2927,5 +3728,10 @@ weston_input_init(struct weston_compositor *compositor)
 			      compositor, bind_relative_pointer_manager))
 		return -1;
 
+	if (!wl_global_create(compositor->wl_display,
+			      &zwp_pointer_constraints_v1_interface, 1,
+			      NULL, bind_pointer_constraints))
+		return -1;
+
 	return 0;
 }
diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c
index f6f92bd..49d974e 100644
--- a/xwayland/window-manager.c
+++ b/xwayland/window-manager.c
@@ -778,7 +778,8 @@ weston_wm_send_focus_window(struct weston_wm *wm,
 static void
 weston_wm_window_activate(struct wl_listener *listener, void *data)
 {
-	struct weston_surface *surface = data;
+	struct weston_surface_activation_data *activation_data = data;
+	struct weston_surface *surface = activation_data->surface;
 	struct weston_wm_window *window = NULL;
 	struct weston_wm *wm =
 		container_of(listener, struct weston_wm, activate_listener);
-- 
2.4.3



More information about the wayland-devel mailing list