[PATCH weston v4 12/20] Implement pointer locking and confinement

Jonas Ådahl jadahl at gmail.com
Tue Nov 17 02:10:58 PST 2015


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>
---

Changes since v3:

Uses the XML file from wayland-protocols.

Updated implementation given interface renaming.

Updated implementation given protocol changes (see protocol patch).


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

diff --git a/Makefile.am b/Makefile.am
index 08a3444..a640d74 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 12bd18e..23f54de 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -584,6 +584,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;
@@ -610,6 +611,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;
 }
 
@@ -1820,6 +1823,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;
@@ -1847,6 +1851,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);
 }
 
@@ -2761,6 +2770,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 46a4d1f..4b27197 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -58,6 +58,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),
@@ -350,6 +351,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;
@@ -424,6 +426,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
@@ -764,6 +769,8 @@ struct weston_compositor {
 
 	unsigned int activate_serial;
 
+	struct wl_global *pointer_constraints;
+
 	int exit_code;
 
 	void *user_data;
@@ -961,10 +968,41 @@ 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;
+	pixman_region32_t region;
+	pixman_region32_t pending_region;
+	bool pending_region_set;
+	struct wl_resource *resource;
+	struct weston_pointer_grab grab;
+	struct weston_pointer *pointer;
+
+	bool pending_hint_set;
+	wl_fixed_t pending_x_hint;
+	wl_fixed_t pending_y_hint;
+	bool hint_set;
+	wl_fixed_t x_hint;
+	wl_fixed_t y_hint;
+
+	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;
@@ -1044,6 +1082,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 97bffab..81cdd6f 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)
 {
@@ -341,12 +358,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;
@@ -359,44 +389,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) {
@@ -722,6 +759,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;
 
@@ -743,6 +781,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);
 
@@ -1310,6 +1350,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);
 
@@ -1318,7 +1359,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
@@ -2796,6 +2841,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
@@ -2818,6 +2927,642 @@ 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;
+}
+
+void
+weston_pointer_constraint_destroy(struct weston_pointer_constraint *constraint)
+{
+	if (is_pointer_constraint_enabled(constraint)) {
+		pointer_constraint_notify_deactivated(constraint);
+		weston_pointer_end_grab(constraint->grab.pointer);
+	}
+
+	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)
+{
+	weston_pointer_constraint_destroy(constraint);
+}
+
+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 constrain
+	 * 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, uint32_t axis, wl_fixed_t value)
+{
+	weston_pointer_send_axis(grab->pointer, time, axis, value);
+}
+
+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_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;
+
+	disable_pointer_constraint(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);
+
+	disable_pointer_constraint(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);
+
+	disable_pointer_constraint(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->pending_region_set) {
+		constraint->pending_region_set = false;
+		pixman_region32_copy(&constraint->region,
+				     &constraint->pending_region);
+		pixman_region32_fini(&constraint->pending_region);
+		pixman_region32_init(&constraint->pending_region);
+	}
+
+	if (constraint->pending_hint_set) {
+		constraint->pending_hint_set = false;
+
+		constraint->hint_set = true;
+		constraint->x_hint = constraint->pending_x_hint;
+		constraint->y_hint = constraint->pending_y_hint;
+	}
+
+	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,
+				 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;
+
+	pixman_region32_init(&constraint->region);
+	pixman_region32_init(&constraint->pending_region);
+	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_seat *seat,
+			struct weston_region *region,
+			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 weston_pointer *pointer = weston_seat_get_pointer(seat);
+	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 as already requested to be "
+				       "locked or confined on that 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,
+						      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
+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_hint = constraint->x_hint;
+	wl_fixed_t y_hint = constraint->y_hint;
+	wl_fixed_t x, y;
+
+	if (constraint->view && constraint->hint_set &&
+	    is_within_constraint_region(constraint, x_hint, y_hint)) {
+		weston_view_to_global_fixed(constraint->view,
+					    x_hint, y_hint,
+					    &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 already sent after the lock
+	 * was cancelled. */
+	if (!constraint->resource ||
+	    constraint->resource != resource)
+		return;
+
+	constraint->pending_hint_set = true;
+	constraint->pending_x_hint = surface_x;
+	constraint->pending_y_hint = 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->pending_region,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&constraint->pending_region);
+		region_init_infinite(&constraint->pending_region);
+	}
+	constraint->pending_region_set = 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 *seat_resource,
+				 struct wl_resource *region_resource)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+	struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+	struct weston_region *region = region_resource ?
+		wl_resource_get_user_data(region_resource) : NULL;
+
+	init_pointer_constraint(resource, id, surface, seat, region,
+				&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,
+				   uint32_t axis,
+				   wl_fixed_t value)
+{
+	weston_pointer_send_axis(grab->pointer, time, axis, value);
+}
+
+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_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->pending_region,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&constraint->pending_region);
+		region_init_infinite(&constraint->pending_region);
+	}
+	constraint->pending_region_set = 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 *seat_resource,
+				    struct wl_resource *region_resource)
+{
+	struct weston_surface *surface =
+		wl_resource_get_user_data(surface_resource);
+	struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+	struct weston_region *region = region_resource ?
+		wl_resource_get_user_data(region_resource) : NULL;
+
+	init_pointer_constraint(resource, id, surface, seat, region,
+				&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_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)
 {
@@ -2826,5 +3571,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 9398d24..242e614 100644
--- a/xwayland/window-manager.c
+++ b/xwayland/window-manager.c
@@ -775,7 +775,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