[PATCH weston v2 14/21] Introduce pointer locking and confinement protocol

Jonas Ådahl jadahl at gmail.com
Wed May 13 03:26:35 PDT 2015


This patch introduces a new protocol for locking and 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.

See pointer-lock.xml for details of the protocol.

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 interfaces are prefixed with an underscore in order to
avoid future incompatibilities with a future stable interface with an
equivalent name.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
 Makefile.am               |   3 +
 protocol/pointer-lock.xml | 208 +++++++++++++++++
 src/compositor.c          |   4 +
 src/compositor.h          |  21 ++
 src/input.c               | 576 ++++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 796 insertions(+), 16 deletions(-)
 create mode 100644 protocol/pointer-lock.xml

diff --git a/Makefile.am b/Makefile.am
index 70c436f..201b780 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -106,6 +106,8 @@ nodist_weston_SOURCES =					\
 	protocol/presentation_timing-server-protocol.h	\
 	protocol/scaler-protocol.c			\
 	protocol/scaler-server-protocol.h		\
+	protocol/pointer-lock-protocol.c		\
+	protocol/pointer-lock-server-protocol.h		\
 	protocol/relative-pointer-protocol.c		\
 	protocol/relative-pointer-server-protocol.h
 
@@ -1186,6 +1188,7 @@ EXTRA_DIST +=					\
 	protocol/scaler.xml			\
 	protocol/ivi-application.xml		\
 	protocol/ivi-hmi-controller.xml		\
+	protocol/pointer-lock.xml		\
 	protocol/relative-pointer.xml
 
 #
diff --git a/protocol/pointer-lock.xml b/protocol/pointer-lock.xml
new file mode 100644
index 0000000..5e7dd9c
--- /dev/null
+++ b/protocol/pointer-lock.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="pointer_lock">
+
+  <copyright>
+    Copyright © 2014      Jonas Ådahl
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+  <interface name="_wl_pointer_lock" version="1">
+    <description summary="lock pointer to a surface">
+      The global interface exposing pointer locking functionality. It exposes
+      two requests; lock_pointer for locking the pointer to its position, and
+      confine_pointer for locking the pointer to a region.
+
+      The lock_pointer and confine_pointer creates the objects wl_locked_pointer
+      and wl_confined_pointer respectively, and the client can use these objects
+      to interact with the lock.
+
+      There may not be another lock of any kind active when requesting a lock,
+      and if there is, an error will be raised.
+    </description>
+
+    <request name="lock_pointer">
+      <description summary="lock pointer to a position">
+        The lock_pointer request lets the client disable absolute pointer
+        movements, locking the pointer to a position.
+
+        There may not be another lock of any kind active when requesting a lock,
+        and if there is, an error will be raised.
+
+        The intersection of the region passed with this request and the input
+        region of the surface is used to determine where the pointer must be
+        in order for the lock to activate. It is up to the compositor to warp
+        the pointer, or require some kind of user interaction for the lock to
+        activate. If the region is null the surface input region is used.
+
+        The request will create a new object wl_locked_pointer which is used to
+        interact with the lock as well as receive updates about its state. See
+        the the description of wl_locked_pointer for further information.
+
+        Note that while a locked pointer doesn't move its absolute position, it
+        may still emit relative motion events via the wl_relative_pointer
+        object.
+      </description>
+
+      <arg name="id" type="new_id" interface="_wl_locked_pointer"/>
+      <arg name="surface" type="object" interface="wl_surface"
+           summary="surface to lock pointer to"/>
+      <arg name="seat" type="object" interface="wl_seat"
+           summary="seat where the pointer should be locked"/>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+           summary="region of surface"/>
+    </request>
+
+    <request name="confine_pointer">
+      <description summary="confine pointer to a region">
+        The confine_pointer request lets the client confine the pointer cursor
+        to a given region.
+
+        The intersection of the region passed with this request and the input
+        region of the surface is used to determine where the pointer must be
+        in order for the confinement to activate. It is up to the compositor to
+        warp the pointer, or require some kind of user interaction for the
+        confinement to activate. If the region is null the surface input region
+        is used.
+
+        The request will create a new object wl_confined_pointer which is used
+        to interact with the confinement as well as receive updates about its
+        state. See the the description of wl_confined_pointer for further
+        information.
+      </description>
+
+      <arg name="id" type="new_id" interface="_wl_confined_pointer"/>
+      <arg name="surface" type="object" interface="wl_surface"
+           summary="surface to lock pointer to"/>
+      <arg name="seat" type="object" interface="wl_seat"
+           summary="seat where the pointer should be locked"/>
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+           summary="region of surface"/>
+    </request>
+
+  </interface>
+
+  <interface name="_wl_locked_pointer" version="1">
+    <description summary="receive relative pointer motion events">
+      The wl_locked_pointer interface represents a locked pointer state.
+
+      While the lock of this object is active, the pointer of the associated
+      seat will not move.
+
+      This object will send the event 'locked' when the lock is activated.
+      Whenever the lock is activated, it is guaranteed that the locked surface
+      will already have received pointer focus and that the pointer will be
+      within the region passed to the request creating this object.
+
+      To unlock the pointer, send the destroy request. This will also destroy
+      the wl_locked_pointer object.
+
+      If the compositor decides to unlock the pointer the unlocked event is sent.
+      The wl_locked_pointer object is at this point defunct and should be
+      destroyed.
+
+      When unlocking, the compositor may or may not take the cursor position
+      hint provided using the set_cursor_position_hint request and warp the
+      pointer. If it does, it will not result in any relative motion events.
+    </description>
+
+    <request name="set_cursor_position_hint">
+      <description summary="set the pointer cursor position hint">
+        Set the cursor position hint relative to the top left corner of the
+        surface.
+
+        If the client is drawing its own cursor, it should update the position
+        hint to the position of its own cursor. A compositor may use this
+        information to warp the pointer upon unlock in order to avoid pointer
+        jumps.
+      </description>
+
+      <arg name="surface_x" type="fixed"
+           summary="x coordinate in surface-relative coordinates"/>
+      <arg name="surface_y" type="fixed"
+           summary="y coordinate in surface-relative coordinates"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the locked pointer object">
+        Destroy the locked pointer object. The compositor will unlock the
+        pointer.
+      </description>
+    </request>
+
+    <event name="locked">
+      <description summary="enter event">
+        Notification that the pointer lock of this seat's pointer is activated.
+      </description>
+    </event>
+
+    <event name="unlocked">
+      <description summary="leave event">
+        Notification that the pointer lock of seat's pointer is no longer
+        active. This object is no defunct and should be destroyed.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="_wl_confined_pointer" version="1">
+    <description summary="confined pointer object">
+      The wl_confined_pointer interface represents a confined pointer state.
+
+      This object will send the event 'confined' when the confinement is
+      activated. Whenever the confinement is activated, it is guaranteed that
+      the surface the pointer is confined to will already have received pointer
+      focus and that the pointer will be within the region passed to the request
+      creating this object. It is up to the compositor to decide whether this
+      requires some user interaction and if the pointer will warp to within the
+      passed region if outside.
+
+      To unconfine the pointer, send the destroy request. This will also destroy
+      the wl_confined_pointer object.
+
+      If the compositor decides to unconfine the pointer the unconfined event is
+      sent. The wl_confined_pointer object is at this point defunct and should
+      be destoryed.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the confined pointer object">
+        Destroy the confined pointer object. The compositor will unconfine the
+        pointer.
+      </description>
+    </request>
+
+    <event name="confined">
+      <description summary="enter event">
+        Notification that the pointer confinement of this seat's pointer is
+        activated.
+      </description>
+    </event>
+
+    <event name="unconfined">
+      <description summary="leave event">
+        Notification that the pointer confinement of seat's pointer is no
+        longer active. This object is no defunct and should be destroyed.
+      </description>
+    </event>
+  </interface>
+
+</protocol>
diff --git a/src/compositor.c b/src/compositor.c
index b462531..70eacc1 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -667,6 +667,8 @@ weston_surface_create(struct weston_compositor *compositor)
 	weston_matrix_init(&surface->buffer_to_surface_matrix);
 	weston_matrix_init(&surface->surface_to_buffer_matrix);
 
+	region_init_infinite(&surface->pointer_lock.region);
+
 	return surface;
 }
 
@@ -1897,6 +1899,8 @@ weston_surface_destroy(struct weston_surface *surface)
 
 	weston_presentation_feedback_discard_list(&surface->feedback_list);
 
+	pixman_region32_fini(&surface->pointer_lock.region);
+
 	free(surface);
 }
 
diff --git a/src/compositor.h b/src/compositor.h
index 55cc88f..80fa2d9 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -349,6 +349,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;
@@ -723,6 +724,8 @@ struct weston_compositor {
 	int exit_code;
 
 	unsigned int activate_serial;
+
+	struct wl_global *pointer_lock;
 };
 
 struct weston_buffer {
@@ -999,6 +1002,24 @@ struct weston_surface {
 	const char *role_name;
 
 	struct weston_timeline_object timeline;
+
+	struct {
+		struct weston_view *view;
+		pixman_region32_t region;
+		struct wl_resource *resource;
+		struct weston_pointer_grab grab;
+		struct weston_pointer *pointer;
+
+		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 keyboard_focus_listener;
+		struct wl_listener surface_activate_listener;
+	} pointer_lock;
 };
 
 struct weston_subsurface {
diff --git a/src/input.c b/src/input.c
index 47f3868..20df639 100644
--- a/src/input.c
+++ b/src/input.c
@@ -22,6 +22,7 @@
 
 #include "config.h"
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
@@ -34,6 +35,7 @@
 #include "../shared/os-compatibility.h"
 #include "../shared/util.h"
 #include "compositor.h"
+#include "protocol/pointer-lock-server-protocol.h"
 #include "protocol/relative-pointer-server-protocol.h"
 
 static void
@@ -43,6 +45,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 void unbind_resource(struct wl_resource *resource)
 {
 	wl_list_remove(wl_resource_get_link(resource));
@@ -243,12 +252,22 @@ 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;
+
+	resource_list = &pointer->focus_resource_list;
+	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;
@@ -262,40 +281,46 @@ default_grab_pointer_motion(struct weston_pointer_grab *grab, uint32_t time,
 	weston_pointer_move(pointer, event);
 
 	if (old_sx != pointer->sx || old_sy != pointer->sy) {
-		resource_list = &pointer->focus_resource_list;
-		wl_resource_for_each(resource, resource_list) {
-			wl_pointer_send_motion(resource, time,
-					       pointer->sx, 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_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;
+	struct wl_display *display = pointer->seat->compositor->wl_display;
 
 	resource_list = &pointer->focus_resource_list;
 	if (!wl_list_empty(resource_list)) {
 		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) {
@@ -587,6 +612,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;
 
@@ -605,6 +631,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);
 
@@ -2560,6 +2588,517 @@ weston_seat_release(struct weston_seat *seat)
 	wl_signal_emit(&seat->destroy_signal, seat);
 }
 
+static const struct _wl_locked_pointer_interface locked_pointer_interface;
+static const struct _wl_confined_pointer_interface confined_pointer_interface;
+
+static void
+pointer_lock_notify_activated(struct weston_surface *surface)
+{
+	struct wl_resource *resource = surface->pointer_lock.resource;
+
+	if (wl_resource_instance_of(resource,
+				    &_wl_locked_pointer_interface,
+				    &locked_pointer_interface)) {
+		_wl_locked_pointer_send_locked(resource);
+	} else if (wl_resource_instance_of(resource,
+					   &_wl_confined_pointer_interface,
+					   &confined_pointer_interface)) {
+		_wl_confined_pointer_send_confined(resource);
+	}
+}
+
+static void
+pointer_lock_notify_deactivated(struct weston_surface *surface)
+{
+	struct wl_resource *resource = surface->pointer_lock.resource;
+
+	if (wl_resource_instance_of(resource,
+				    &_wl_locked_pointer_interface,
+				    &locked_pointer_interface)) {
+		_wl_locked_pointer_send_unlocked(resource);
+	} else if (wl_resource_instance_of(resource,
+					   &_wl_confined_pointer_interface,
+					   &confined_pointer_interface)) {
+		_wl_confined_pointer_send_unconfined(resource);
+	}
+}
+
+static void
+enable_pointer_lock(struct weston_view *view,
+		    struct weston_pointer *pointer)
+{
+	struct weston_surface *surface = view->surface;
+
+	assert(surface->pointer_lock.view == NULL);
+	surface->pointer_lock.view = view;
+	pointer_lock_notify_activated(surface);
+	weston_pointer_start_grab(pointer, &surface->pointer_lock.grab);
+}
+
+static bool
+is_pointer_lock_enabled(struct weston_surface *surface)
+{
+	return surface->pointer_lock.view != NULL;
+}
+
+static void
+disable_pointer_lock(struct weston_surface *surface)
+{
+	if (is_pointer_lock_enabled(surface)) {
+		pointer_lock_notify_deactivated(surface);
+		weston_pointer_end_grab(surface->pointer_lock.grab.pointer);
+		surface->pointer_lock.view = NULL;
+	}
+
+	surface->pointer_lock.resource = NULL;
+	surface->pointer_lock.pointer = NULL;
+
+	surface->pointer_lock.hint_set = false;
+
+	wl_list_remove(&surface->pointer_lock.pointer_destroy_listener.link);
+	wl_list_remove(&surface->pointer_lock.surface_destroy_listener.link);
+	wl_list_remove(&surface->pointer_lock.surface_activate_listener.link);
+}
+
+static bool
+is_within_lock_region(struct weston_surface *surface,
+		      wl_fixed_t sx, wl_fixed_t sy)
+{
+	pixman_region32_t lock_region;
+	bool result;
+
+	pixman_region32_init(&lock_region);
+	pixman_region32_intersect(&lock_region,
+				  &surface->input,
+				  &surface->pointer_lock.region);
+	result = pixman_region32_contains_point(&lock_region,
+						wl_fixed_to_int(sx),
+						wl_fixed_to_int(sy),
+						NULL);
+	pixman_region32_fini(&lock_region);
+
+	return result;
+}
+
+static void
+maybe_enable_pointer_lock(struct weston_surface *surface)
+{
+	struct weston_view *vit;
+	struct weston_view *view = NULL;
+	struct weston_pointer *pointer = surface->pointer_lock.pointer;
+	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. */
+	if (seat->keyboard->focus != surface)
+		return;
+
+	/* Postpone lock if the pointer is not within the lock region. */
+	weston_view_from_global(view,
+				wl_fixed_to_int(pointer->x),
+				wl_fixed_to_int(pointer->y),
+				&x, &y);
+	if (!is_within_lock_region(surface,
+				   wl_fixed_from_int(x),
+				   wl_fixed_from_int(y)))
+		return;
+
+	enable_pointer_lock(view, pointer);
+}
+
+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_surface *surface =
+		container_of(grab, struct weston_surface, pointer_lock.grab);
+
+	disable_pointer_lock(surface);
+}
+
+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_lock_lock_resource_destroyed(struct wl_resource *resource)
+{
+	struct weston_surface *surface = wl_resource_get_user_data(resource);
+
+	if (!surface->pointer_lock.resource)
+		return;
+
+	disable_pointer_lock(surface);
+}
+
+static void
+pointer_lock_surface_activate(struct wl_listener *listener, void *data)
+{
+	struct weston_surface *focus = data;
+	struct weston_surface *locked_surface =
+		container_of(listener, struct weston_surface,
+			     pointer_lock.surface_activate_listener);
+
+	if (focus == locked_surface && !is_pointer_lock_enabled(locked_surface))
+		maybe_enable_pointer_lock(locked_surface);
+	else if (focus != locked_surface &&
+		 is_pointer_lock_enabled(locked_surface))
+		disable_pointer_lock(locked_surface);
+}
+
+static void
+pointer_lock_pointer_destroyed(struct wl_listener *listener, void *data)
+{
+	struct weston_surface *surface =
+		container_of(listener, struct weston_surface,
+			     pointer_lock.pointer_destroy_listener);
+
+	disable_pointer_lock(surface);
+}
+
+static void
+pointer_lock_surface_destroyed(struct wl_listener *listener, void *data)
+{
+	struct weston_surface *surface =
+		container_of(listener, struct weston_surface,
+			     pointer_lock.surface_destroy_listener);
+
+	disable_pointer_lock(surface);
+}
+
+static void
+init_pointer_lock(struct wl_resource *pointer_lock_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_lock_resource);
+	struct weston_pointer *pointer = seat->pointer;
+	struct wl_resource *cr;
+
+	if (surface->pointer_lock.resource) {
+		wl_resource_post_error(pointer_lock_resource,
+				       WL_DISPLAY_ERROR_INVALID_OBJECT,
+				       "pointer already locked or confined");
+		return;
+	}
+
+        cr = wl_resource_create(client, interface,
+				wl_resource_get_version(pointer_lock_resource),
+				id);
+	if (cr == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(cr, implementation, surface,
+				       pointer_lock_lock_resource_destroyed);
+
+	surface->pointer_lock.pointer = pointer;
+	surface->pointer_lock.resource = cr;
+	surface->pointer_lock.grab.interface = grab_interface;
+	if (region) {
+		pixman_region32_copy(&surface->pointer_lock.region,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&surface->pointer_lock.region);
+		region_init_infinite(&surface->pointer_lock.region);
+	}
+
+	surface->pointer_lock.surface_activate_listener.notify =
+		pointer_lock_surface_activate;
+	surface->pointer_lock.surface_destroy_listener.notify =
+		pointer_lock_surface_destroyed;
+	surface->pointer_lock.pointer_destroy_listener.notify =
+		pointer_lock_pointer_destroyed;
+
+	wl_signal_add(&surface->compositor->activate_signal,
+		      &surface->pointer_lock.surface_activate_listener);
+	wl_signal_add(&seat->pointer->destroy_signal,
+		      &surface->pointer_lock.pointer_destroy_listener);
+	wl_signal_add(&surface->destroy_signal,
+		      &surface->pointer_lock.surface_destroy_listener);
+
+	maybe_enable_pointer_lock(surface);
+}
+
+
+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_surface *surface = wl_resource_get_user_data(resource);
+
+	/* Ignore a set cursor hint that was already sent after the lock
+	 * was cancelled. */
+	if (!surface->pointer_lock.resource ||
+	    surface->pointer_lock.resource != resource)
+		return;
+
+	surface->pointer_lock.hint_set = true;
+	surface->pointer_lock.x_hint = surface_x;
+	surface->pointer_lock.y_hint = surface_y;
+}
+
+static void
+locked_pointer_destroy(struct wl_client *client,
+		       struct wl_resource *resource)
+{
+	struct weston_surface *surface = wl_resource_get_user_data(resource);
+	wl_fixed_t x_hint = surface->pointer_lock.x_hint;
+	wl_fixed_t y_hint = surface->pointer_lock.y_hint;
+	wl_fixed_t x, y;
+
+	if (surface->pointer_lock.view &&
+	    surface->pointer_lock.hint_set &&
+	    is_within_lock_region(surface, x_hint, y_hint)) {
+		weston_view_to_global_fixed(surface->pointer_lock.view,
+					    x_hint, y_hint,
+					    &x, &y);
+		weston_pointer_move_to(surface->pointer_lock.pointer, x, y);
+	}
+	wl_resource_destroy(resource);
+}
+
+static const struct _wl_locked_pointer_interface locked_pointer_interface = {
+	locked_pointer_set_cursor_position_hint,
+	locked_pointer_destroy,
+};
+
+static void
+pointer_lock_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_lock(resource, id, surface, seat, region,
+			  &_wl_locked_pointer_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
+confined_pointer_grab_pointer_motion(struct weston_pointer_grab *grab,
+				     uint32_t time,
+				     struct weston_pointer_motion_event *event)
+{
+	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->pointer_lock.pointer == pointer);
+
+	surface = pointer->focus->surface;
+
+	pixman_region32_init(&confine_region);
+	pixman_region32_intersect(&confine_region,
+				  &surface->input,
+				  &surface->pointer_lock.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_surface *surface =
+		container_of(grab, struct weston_surface, pointer_lock.grab);
+
+	disable_pointer_lock(surface);
+}
+
+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 const struct _wl_confined_pointer_interface confined_pointer_interface = {
+	confined_pointer_destroy,
+};
+
+static void
+pointer_lock_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;
+
+	if ((region && pixman_region32_n_rects(&region->region) != 1) ||
+	    pixman_region32_n_rects(&surface->input) != 1) {
+		weston_log("warning: confinement only implemented for"
+			   "rectangular regions\n");
+		return;
+	}
+
+	init_pointer_lock(resource, id, surface, seat, region,
+			  &_wl_confined_pointer_interface,
+			  &confined_pointer_interface,
+			  &confined_pointer_grab_interface);
+}
+
+static const struct _wl_pointer_lock_interface pointer_lock_interface = {
+	pointer_lock_lock_pointer,
+	pointer_lock_confine_pointer,
+};
+
+static void
+bind_pointer_lock(struct wl_client *client, void *data,
+		  uint32_t version, uint32_t id)
+{
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client, &_wl_pointer_lock_interface,
+				      1, id);
+	wl_resource_set_implementation(resource, &pointer_lock_interface,
+				       NULL, NULL);
+}
+
 int
 weston_input_init(struct weston_compositor *compositor)
 {
@@ -2568,5 +3107,10 @@ weston_input_init(struct weston_compositor *compositor)
 			      compositor, bind_relative_pointer_manager))
 		return -1;
 
+	if (!wl_global_create(compositor->wl_display,
+			      &_wl_pointer_lock_interface, 1,
+			      NULL, bind_pointer_lock))
+		return -1;
+
 	return 0;
 }
-- 
2.1.4



More information about the wayland-devel mailing list