[v3,weston] Introduce pointer locking and confinement protocol

Submitted by Jonas Ådahl on June 26, 2015, 4:38 a.m.

Details

Message ID 1435293482-20979-7-git-send-email-jadahl@gmail.com
State Superseded
Delegated to: Daniel Stone
Headers show

Not browsing as part of any series.

Commit Message

Jonas Ådahl June 26, 2015, 4:38 a.m.
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@gmail.com>
---

Changes since v2:

 * Updated copyright.
 * Updated to the fixed license text.
 * Clarified that locks are on made on a certain surface from a given
   seat. The same surface may have many parallel locks; one per seat.
 * Added an error enum.
 * Changed the wording explaining that constraints for activating a lock
   is compositor specific.
 * Clarified that there is no guarantee that a lock will ever be
   activated. No error will be raised if the compositor will never
   activate the lock.
 * Changed wording related to pointer events (make them speak protocol,
   not abstract events).
 * Clarify what happens if a surface is destroyed.
 * Fixed various typos.
 * Implemented support for per-seat locks.
 * Clarified what happens when effective lock/confine regions change.
 * Added wl_locked_pointer.set_region and wl_confined_pointer.set_region
   (see below).
 * Implemented support for above mentioned new requests.
 * Changed the order of requests (put destroy first).
 * Added 'unstable protocol' warning and explained its semantics.

This version adds support for changing the lock/confine region after the
lock was created.

This is so that a client can change the lock region without being
exposed to a race condition. Depending on the type of lock
(lock/confine). For exampe, a client who maximizes wants to keep the
pending lock or pending/active confinement intact can use the set_region
request. The region is double buffered, meaning it is synchronized with
the surface input region.

For wl_locked_pointer, since a changed effective region has no effect on
the lock after it being activated, the set_region request only has effect
before the lock is activated. As such, it affects the region that is used
to activate the lock.

For pending wl_confined_pointer lock, the effect is the same as for
wl_locked_pointer. But for an already activated confinement, it affects the
pointer, i.e. warps the pointer to somewhere within the region.


Jonas


 Makefile.am               |   3 +
 protocol/pointer-lock.xml | 275 +++++++++++++++++
 src/compositor.c          |  11 +
 src/compositor.h          |  38 +++
 src/input.c               | 751 ++++++++++++++++++++++++++++++++++++++++++++--
 5 files changed, 1061 insertions(+), 17 deletions(-)
 create mode 100644 protocol/pointer-lock.xml

Patch hide | download patch | download mbox

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..ee5e274
--- /dev/null
+++ b/protocol/pointer-lock.xml
@@ -0,0 +1,275 @@ 
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="pointer_lock">
+
+  <copyright>
+    Copyright © 2014      Jonas Ådahl
+    Copyright © 2015      Red Hat Inc.
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE 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.
+
+      For any surface, only one lock or confinement per seat may be active at
+      any time. If a lock or confinement is requested when another lock or
+      confinement is active on that surface and seat, an 'already_locked' error
+      will be raised.
+
+      Warning! The protocol described in this file is experimental. Each version
+      of this protocol should be considered incompatible with any other version,
+      and a client binding to a version different to the one advertised will be
+      terminated. When the protocol is stabalized, backward compatibility is
+      guaranteed, the '_' prefix will be removed from the name and the version
+      will be reset to 1.
+    </description>
+
+    <enum name="error">
+      <description summary="wl_pointer_lock error values">
+        These errors can be emitted in response to wl_pointer_lock requests.
+      </description>
+      <entry name="already_locked" value="0" summary="pointer was already locked or confined"/>
+    </enum>
+
+    <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. In the future, when the
+        compositor deems implementation specific constraints are satisfied, the
+        pointer lock will be activated and the compositor sends a locked event.
+
+        The protocol provides no guarantee that the constraints are ever
+        satisfied, and does not require the compositor to send an error if the
+        constraints cannot ever be satisfied. It is thus possible to request a
+        lock that will never activate.
+
+        There may not be another lock of any kind requested or active on the
+        surface for the seat when requesting a lock, and if there is, an error
+        will be raised. See general pointer lock documentation for more details.
+
+        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.
+
+        A surface may receive pointer focus without the lock being activated.
+
+        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 pointer is locked, the wl_pointer objects of the
+        corresponding seat will not emit any motion events, but relative motion
+        events will still be emitted via wl_relative_pointer objects of the
+        same seat.
+      </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 wl_pointer objects of the
+      associated seat will not emit any motion events.
+
+      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 take the cursor position pointer. If
+      it does, it will not result in any relative motion events emitted via
+      wl_relative_motion.
+
+      If the surface lock was requested on is destroyed and the lock is not yet
+      activated, the wl_locked_pointer object is now defunct and must be
+      destroyed.
+    </description>
+
+    <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>
+
+    <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="set_region">
+      <description summary="set a new lock region">
+        Set a new region used to lock the pointer.
+
+        The new lock region is double-buffered. The new lock region will
+        only take effect when the associated surface gets its pending state
+        applied. See wl_surface.commit for details.
+
+        The new region has no effect on a lock that has already been activated.
+
+        For details about the lock region, see wl_locked_pointer.
+      </description>
+
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+           summary="region of surface"/>
+    </request>
+
+    <event name="locked">
+      <description summary="lock activation event">
+        Notification that the pointer lock of this seat's pointer is activated.
+      </description>
+    </event>
+
+    <event name="unlocked">
+      <description summary="lock deactivation event">
+        Notification that the pointer lock of seat's pointer is no longer
+        active. This object is now 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 destroyed.
+    </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>
+
+    <request name="set_region">
+      <description summary="set a new confine region">
+        Set a new region used to confine the pointer.
+
+        The new confine region is double-buffered. The new confine region will
+        only take effect when the associated surface gets its pending state
+        applied. See wl_surface.commit for details.
+
+        If the confinement is active when the new confinement region is applied
+        and the pointer ends up outside of newly applied region, the pointer is
+        warped to a position within the new confinement region. If warped, a
+        wl_pointer.motion event will be emitted, but no
+        wl_relative_pointer.relative_motion event.
+
+        For details about the confine region, see wl_confined_pointer.
+      </description>
+
+      <arg name="region" type="object" interface="wl_region" allow-null="true"
+           summary="region of surface"/>
+    </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..296be31 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -641,6 +641,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;
@@ -667,6 +668,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_locks);
+
 	return surface;
 }
 
@@ -1870,6 +1873,7 @@  weston_surface_destroy(struct weston_surface *surface)
 {
 	struct weston_frame_callback *cb, *next;
 	struct weston_view *ev, *nv;
+	struct weston_pointer_lock *pointer_lock, *next_pointer_lock;
 
 	if (--surface->ref_count > 0)
 		return;
@@ -1897,6 +1901,11 @@  weston_surface_destroy(struct weston_surface *surface)
 
 	weston_presentation_feedback_discard_list(&surface->feedback_list);
 
+	wl_list_for_each_safe(pointer_lock, next_pointer_lock,
+			      &surface->pointer_locks,
+			      link)
+		weston_pointer_lock_destroy(pointer_lock);
+
 	free(surface);
 }
 
@@ -2804,6 +2813,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 55cc88f..2322b33 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -63,6 +63,7 @@  struct shell_surface;
 struct weston_seat;
 struct weston_output;
 struct input_method;
+struct weston_pointer_lock;
 
 enum weston_keyboard_modifier {
 	MODIFIER_CTRL = (1 << 0),
@@ -349,6 +350,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;
@@ -421,6 +423,9 @@  void
 weston_pointer_set_default_grab(struct weston_pointer *pointer,
 		const struct weston_pointer_grab_interface *interface);
 
+void
+weston_pointer_lock_destroy(struct weston_pointer_lock *lock);
+
 struct weston_keyboard *
 weston_keyboard_create(void);
 void
@@ -723,6 +728,8 @@  struct weston_compositor {
 	int exit_code;
 
 	unsigned int activate_serial;
+
+	struct wl_global *pointer_lock;
 };
 
 struct weston_buffer {
@@ -916,10 +923,38 @@  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_lock {
+	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 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;
@@ -999,6 +1034,9 @@  struct weston_surface {
 	const char *role_name;
 
 	struct weston_timeline_object timeline;
+
+	/* An list of per seat pointer locks. */
+	struct wl_list pointer_locks;
 };
 
 struct weston_subsurface {
diff --git a/src/input.c b/src/input.c
index b48a6c9..4a78543 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,8 +35,17 @@ 
 #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"
 
+enum pointer_lock_type {
+	POINTER_LOCK_TYPE_LOCK,
+	POINTER_LOCK_TYPE_CONFINE,
+};
+
+static void
+maybe_warp_confined_pointer(struct weston_pointer_lock *lock);
+
 static void
 empty_region(pixman_region32_t *region)
 {
@@ -43,6 +53,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 +260,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 +289,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) {
@@ -598,6 +631,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;
 
@@ -616,6 +650,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);
 
@@ -1163,6 +1199,7 @@  weston_surface_activate(struct weston_surface *surface,
 			struct weston_seat *seat)
 {
 	struct weston_compositor *compositor = seat->compositor;
+	struct weston_surface_activation_data activation_data;
 
 	inc_activate_serial(compositor);
 
@@ -1171,7 +1208,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
@@ -2571,6 +2612,677 @@  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 enum pointer_lock_type
+pointer_lock_get_type(struct weston_pointer_lock *lock)
+{
+	if (wl_resource_instance_of(lock->resource,
+				    &_wl_locked_pointer_interface,
+				    &locked_pointer_interface)) {
+		return POINTER_LOCK_TYPE_LOCK;
+	} else if (wl_resource_instance_of(lock->resource,
+					   &_wl_confined_pointer_interface,
+					   &confined_pointer_interface)) {
+		return POINTER_LOCK_TYPE_CONFINE;
+	}
+
+	abort();
+	return 0;
+}
+
+static void
+pointer_lock_notify_activated(struct weston_pointer_lock *lock)
+{
+	struct wl_resource *resource = lock->resource;
+
+	switch (pointer_lock_get_type(lock)) {
+	case POINTER_LOCK_TYPE_LOCK:
+		_wl_locked_pointer_send_locked(resource);
+		break;
+	case POINTER_LOCK_TYPE_CONFINE:
+		_wl_confined_pointer_send_confined(resource);
+		break;
+	}
+}
+
+static void
+pointer_lock_notify_deactivated(struct weston_pointer_lock *lock)
+{
+	struct wl_resource *resource = lock->resource;
+
+	switch (pointer_lock_get_type(lock)) {
+	case POINTER_LOCK_TYPE_LOCK:
+		_wl_locked_pointer_send_unlocked(resource);
+		break;
+	case POINTER_LOCK_TYPE_CONFINE:
+		_wl_confined_pointer_send_unconfined(resource);
+		break;
+	}
+}
+
+static struct weston_pointer_lock *
+get_pointer_lock_for_pointer(struct weston_surface *surface,
+			     struct weston_pointer *pointer)
+{
+	struct weston_pointer_lock *lock;
+
+	wl_list_for_each(lock, &surface->pointer_locks, link) {
+		if (lock->pointer == pointer)
+			return lock;
+	}
+
+	return NULL;
+}
+
+static void
+enable_pointer_lock(struct weston_pointer_lock *lock,
+		    struct weston_view *view)
+{
+	assert(lock->view == NULL);
+	lock->view = view;
+	pointer_lock_notify_activated(lock);
+	weston_pointer_start_grab(lock->pointer, &lock->grab);
+}
+
+static bool
+is_pointer_lock_enabled(struct weston_pointer_lock *lock)
+{
+	return lock->view != NULL;
+}
+
+void
+weston_pointer_lock_destroy(struct weston_pointer_lock *lock)
+{
+	if (is_pointer_lock_enabled(lock)) {
+		pointer_lock_notify_deactivated(lock);
+		weston_pointer_end_grab(lock->grab.pointer);
+	}
+
+	wl_list_remove(&lock->pointer_destroy_listener.link);
+	wl_list_remove(&lock->surface_destroy_listener.link);
+	wl_list_remove(&lock->surface_commit_listener.link);
+	wl_list_remove(&lock->surface_activate_listener.link);
+
+	wl_resource_set_user_data(lock->resource, NULL);
+	pixman_region32_fini(&lock->region);
+	wl_list_remove(&lock->link);
+	free(lock);
+}
+
+static void
+disable_pointer_lock(struct weston_pointer_lock *lock)
+{
+	weston_pointer_lock_destroy(lock);
+}
+
+static bool
+is_within_lock_region(struct weston_pointer_lock *lock,
+		      wl_fixed_t sx, wl_fixed_t sy)
+{
+	struct weston_surface *surface = lock->surface;
+	pixman_region32_t lock_region;
+	bool result;
+
+	pixman_region32_init(&lock_region);
+	pixman_region32_intersect(&lock_region, &surface->input, &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_pointer_lock *lock)
+{
+	struct weston_surface *surface = lock->surface;
+	struct weston_view *vit;
+	struct weston_view *view = NULL;
+	struct weston_pointer *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(lock,
+				   wl_fixed_from_int(x),
+				   wl_fixed_from_int(y)))
+		return;
+
+	enable_pointer_lock(lock, 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_lock *lock =
+		container_of(grab, struct weston_pointer_lock, grab);
+
+	disable_pointer_lock(lock);
+}
+
+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_pointer_lock *lock = wl_resource_get_user_data(resource);
+
+	if (!lock)
+		return;
+
+	disable_pointer_lock(lock);
+}
+
+static void
+pointer_lock_surface_activate(struct wl_listener *listener, void *data)
+{
+	struct weston_surface_activation_data *activation = data;
+	struct weston_pointer *pointer = activation->seat->pointer;
+	struct weston_surface *focus = activation->surface;
+	struct weston_pointer_lock *lock =
+		container_of(listener, struct weston_pointer_lock,
+			     surface_activate_listener);
+	bool is_lock_surface;
+
+	is_lock_surface = get_pointer_lock_for_pointer(focus, pointer) == lock;
+
+	if (is_lock_surface &&  !is_pointer_lock_enabled(lock))
+		maybe_enable_pointer_lock(lock);
+	else if (!is_lock_surface && is_pointer_lock_enabled(lock))
+		disable_pointer_lock(lock);
+}
+
+static void
+pointer_lock_pointer_destroyed(struct wl_listener *listener, void *data)
+{
+	struct weston_pointer_lock *lock =
+		container_of(listener, struct weston_pointer_lock,
+			     pointer_destroy_listener);
+
+	disable_pointer_lock(lock);
+}
+
+static void
+pointer_lock_surface_destroyed(struct wl_listener *listener, void *data)
+{
+	struct weston_pointer_lock *lock =
+		container_of(listener, struct weston_pointer_lock,
+			     surface_destroy_listener);
+
+	disable_pointer_lock(lock);
+}
+
+static void
+pointer_lock_surface_committed(struct wl_listener *listener, void *data)
+{
+	struct weston_pointer_lock *lock =
+		container_of(listener, struct weston_pointer_lock,
+			     surface_commit_listener);
+
+	if (lock->pending_region_set) {
+		lock->pending_region_set = false;
+		pixman_region32_copy(&lock->region, &lock->pending_region);
+		pixman_region32_fini(&lock->pending_region);
+		pixman_region32_init(&lock->pending_region);
+	}
+
+	if (pointer_lock_get_type(lock) == POINTER_LOCK_TYPE_CONFINE &&
+	    is_pointer_lock_enabled(lock))
+		maybe_warp_confined_pointer(lock);
+}
+
+static struct weston_pointer_lock *
+weston_pointer_lock_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_lock *lock;
+
+	lock = zalloc(sizeof *lock);
+	if (!lock)
+		return NULL;
+
+	pixman_region32_init(&lock->region);
+	pixman_region32_init(&lock->pending_region);
+	wl_list_insert(&surface->pointer_locks, &lock->link);
+	lock->surface = surface;
+	lock->pointer = pointer;
+	lock->resource = cr;
+	lock->grab.interface = grab_interface;
+	if (region) {
+		pixman_region32_copy(&lock->region,
+				     &region->region);
+	} else {
+		pixman_region32_fini(&lock->region);
+		region_init_infinite(&lock->region);
+	}
+
+	lock->surface_activate_listener.notify = pointer_lock_surface_activate;
+	lock->surface_destroy_listener.notify = pointer_lock_surface_destroyed;
+	lock->surface_commit_listener.notify = pointer_lock_surface_committed;
+	lock->pointer_destroy_listener.notify = pointer_lock_pointer_destroyed;
+
+	wl_signal_add(&surface->compositor->activate_signal,
+		      &lock->surface_activate_listener);
+	wl_signal_add(&pointer->destroy_signal,
+		      &lock->pointer_destroy_listener);
+	wl_signal_add(&surface->destroy_signal,
+		      &lock->surface_destroy_listener);
+	wl_signal_add(&surface->commit_signal,
+		      &lock->surface_commit_listener);
+
+	return lock;
+}
+
+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;
+	struct weston_pointer_lock *lock;
+
+	if (get_pointer_lock_for_pointer(surface, pointer)) {
+		wl_resource_post_error(pointer_lock_resource,
+				       WL_DISPLAY_ERROR_INVALID_OBJECT,
+				       "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_lock_resource),
+				id);
+	if (cr == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	lock = weston_pointer_lock_create(surface, pointer, region,
+					  cr, grab_interface);
+	if (lock == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	wl_resource_set_implementation(cr, implementation, lock,
+				       pointer_lock_lock_resource_destroyed);
+
+	maybe_enable_pointer_lock(lock);
+}
+
+static void
+locked_pointer_destroy(struct wl_client *client,
+		       struct wl_resource *resource)
+{
+	struct weston_pointer_lock *lock = wl_resource_get_user_data(resource);
+	wl_fixed_t x_hint = lock->x_hint;
+	wl_fixed_t y_hint = lock->y_hint;
+	wl_fixed_t x, y;
+
+	if (lock->view && lock->hint_set &&
+	    is_within_lock_region(lock, x_hint, y_hint)) {
+		weston_view_to_global_fixed(lock->view,
+					    x_hint, y_hint,
+					    &x, &y);
+		weston_pointer_move_to(lock->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_lock *lock = wl_resource_get_user_data(resource);
+
+	/* Ignore a set cursor hint that was already sent after the lock
+	 * was cancelled. */
+	if (!lock->resource ||
+	    lock->resource != resource)
+		return;
+
+	lock->hint_set = true;
+	lock->x_hint = surface_x;
+	lock->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_lock *lock = 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(&lock->pending_region, &region->region);
+	} else {
+		pixman_region32_fini(&lock->pending_region);
+		region_init_infinite(&lock->pending_region);
+	}
+	lock->pending_region_set = true;
+}
+
+
+static const struct _wl_locked_pointer_interface locked_pointer_interface = {
+	locked_pointer_destroy,
+	locked_pointer_set_cursor_position_hint,
+	locked_pointer_set_region,
+};
+
+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
+maybe_warp_confined_pointer(struct weston_pointer_lock *lock)
+{
+	wl_fixed_t x;
+	wl_fixed_t y;
+	wl_fixed_t sx;
+	wl_fixed_t sy;
+
+	weston_view_from_global_fixed(lock->view,
+				      lock->pointer->x,
+				      lock->pointer->y,
+				      &sx,
+				      &sy);
+
+	if (!is_within_lock_region(lock, sx, sy)) {
+		pixman_region32_t *region = &lock->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(lock->view, sx, sy, &x, &y);
+		weston_pointer_move_to(lock->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_lock *lock =
+		container_of(grab, struct weston_pointer_lock, 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 == lock->surface);
+
+	surface = pointer->focus->surface;
+
+	pixman_region32_init(&confine_region);
+	pixman_region32_intersect(&confine_region,
+				  &surface->input,
+				  &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_pointer_lock *lock =
+		container_of(grab, struct weston_pointer_lock, grab);
+
+	disable_pointer_lock(lock);
+}
+
+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_lock *lock = 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(&lock->pending_region, &region->region);
+	} else {
+		pixman_region32_fini(&lock->pending_region);
+		region_init_infinite(&lock->pending_region);
+	}
+	lock->pending_region_set = true;
+}
+
+static const struct _wl_confined_pointer_interface confined_pointer_interface = {
+	confined_pointer_destroy,
+	confined_pointer_set_region,
+};
+
+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)
 {
@@ -2579,5 +3291,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;
 }

Comments

On Thu, Jun 25, 2015 at 9:38 PM, Jonas Ådahl <jadahl@gmail.com> wrote:

> This version adds support for changing the lock/confine region after the
> lock was created.
>
> For pending wl_confined_pointer lock, the effect is the same as for
> wl_locked_pointer. But for an already activated confinement, it affects the
> pointer, i.e. warps the pointer to somewhere within the region.
>

You do realize you have provided exactly what I have been requesting. The
client can now move the cursor arbitrarily around by sending 1x1
confinement regions. IMHO it would make more sense to fix the "hint" call
to do the same thing so you reduce the size of the code.

I am primarily concerned with reusing this api for "slow sliders", and for
more complex transformation of 2d movement to screen position, such as
locking the cursor to the surface of a rendered trackball. I believe it can
be done with the following changes:

- Replace set_cursor_position_hint with set_cursor_position. The result is
identical to setting the confine region to a very tiny one such that the
cursor cannot move. Note that this will not break anything: your current
api requires the client to hide the cursor, so the fact that this actually
moves it will not be visible!!!

- Add a third method to _wl_pointer_lock of making a lock that expires when
the mouse button is released (it is destroyed by exactly the same rules as
the automatic grab is expired). It takes an event identifier to indicate
which mouse button press it is for. Creation of this immediately activates
it (I'm not sure if the activation event should be sent or implied), then
deactivates it when the button is no longer pushed.

You should also merge the _wl_locked_pointer and _wl_confined_pointer
objects since they are nearly identical. The result has both
"set_cursor_position" and "set_region" methods. set_cursor_position is
exactly the same as setting a very tiny region (in case you are wondering
what it should do before "activation").

A fourth type of lock may be useful, too, though I am not very sure how it
should work. This is to allow the client to position the cursor in sync
with it's drawing even when the mouse is not held down. The difference is
that this lock is lost on an exit event, and something is done so that a
client continuously warping the cursor to the middle of the surface cannot
prevent these exit events. The idea is that this is somewhat less dangerous
than your original locking and thus the compositor is more likely to grant
it. This locking is useful for highlighting fine detail to show what will
be selected on click.
Hi Jonas,

Instead of a reasonable review, I'm going to mostly ask a bunch of noob
questions.  Feel free to disregard if you don't have time to enlighten me...

On 25/06/15 11:38 PM, Jonas Ådahl wrote:
> 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@gmail.com>
> ---
> 
> Changes since v2:
> 
>  * Updated copyright.
>  * Updated to the fixed license text.
>  * Clarified that locks are on made on a certain surface from a given
>    seat. The same surface may have many parallel locks; one per seat.
>  * Added an error enum.
>  * Changed the wording explaining that constraints for activating a lock
>    is compositor specific.
>  * Clarified that there is no guarantee that a lock will ever be
>    activated. No error will be raised if the compositor will never
>    activate the lock.
>  * Changed wording related to pointer events (make them speak protocol,
>    not abstract events).
>  * Clarify what happens if a surface is destroyed.
>  * Fixed various typos.
>  * Implemented support for per-seat locks.
>  * Clarified what happens when effective lock/confine regions change.
>  * Added wl_locked_pointer.set_region and wl_confined_pointer.set_region
>    (see below).
>  * Implemented support for above mentioned new requests.
>  * Changed the order of requests (put destroy first).
>  * Added 'unstable protocol' warning and explained its semantics.
> 
> This version adds support for changing the lock/confine region after the
> lock was created.
> 
> This is so that a client can change the lock region without being
> exposed to a race condition. Depending on the type of lock
> (lock/confine). For exampe, a client who maximizes wants to keep the
> pending lock or pending/active confinement intact can use the set_region
> request. The region is double buffered, meaning it is synchronized with
> the surface input region.
> 
> For wl_locked_pointer, since a changed effective region has no effect on
> the lock after it being activated, the set_region request only has effect
> before the lock is activated. As such, it affects the region that is used
> to activate the lock.
> 
> For pending wl_confined_pointer lock, the effect is the same as for
> wl_locked_pointer. But for an already activated confinement, it affects the
> pointer, i.e. warps the pointer to somewhere within the region.
> 
> 
> Jonas
> 
> 
>  Makefile.am               |   3 +
>  protocol/pointer-lock.xml | 275 +++++++++++++++++
>  src/compositor.c          |  11 +
>  src/compositor.h          |  38 +++
>  src/input.c               | 751 ++++++++++++++++++++++++++++++++++++++++++++--
>  5 files changed, 1061 insertions(+), 17 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..ee5e274
> --- /dev/null
> +++ b/protocol/pointer-lock.xml
> @@ -0,0 +1,275 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<protocol name="pointer_lock">
> +
> +  <copyright>
> +    Copyright © 2014      Jonas Ådahl
> +    Copyright © 2015      Red Hat Inc.
> +
> +    Permission is hereby granted, free of charge, to any person obtaining a
> +    copy of this software and associated documentation files (the "Software"),
> +    to deal in the Software without restriction, including without limitation
> +    the rights to use, copy, modify, merge, publish, distribute, sublicense,
> +    and/or sell copies of the Software, and to permit persons to whom the
> +    Software is furnished to do so, subject to the following conditions:
> +
> +    The above copyright notice and this permission notice (including the next
> +    paragraph) shall be included in all copies or substantial portions of the
> +    Software.
> +
> +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> +    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> +    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> +    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> +    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> +    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> +    DEALINGS IN THE 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.

Why have both confinement and lock in the same protocol?  It seems like
lock is perhaps simpler and could be more easily merged independently.

Are they combined in order to enforce the "only one lock/confinement per
surface+seat combination" policy?

Is this the same interface I'd use for constraining touch input?

> +      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.
> +
> +      For any surface, only one lock or confinement per seat may be active at
> +      any time. If a lock or confinement is requested when another lock or
> +      confinement is active on that surface and seat, an 'already_locked' error
> +      will be raised.

Why this limitation?

If I have a surface with a "slow scrollbar" and during its use I need to
add another scrollbar (I dunno, slow loading web page?), my app will
need to remember that it hasn't set up that new scrollbar's confinement
region until after the release of the active scrollbar?

(How would I implement a slow scrollbar?  if the compositor has
implementation defined semantics for the confinement, I guess I can't?)

Should there be a more explicit way for the application to control what
causes the activation?  (activate if the user clicks/drags within the
region vs activate if the mouse merely enters the region)

For lock in a FPS game, simply focusing the window should probably
trigger the lock - so the compositor should warp the pointer there if
the window becomes focused by any means, but are there any use cases
where such a warp would be incorrect behaviour?

Presumably I can use meta+tab to focus a different surface and break a
pointer lock/confinement.  Will that break the lock/confinement for that
surface, or will the lock/confinement be resumed next time that surface
obtains focus?

for weston specifically, I wonder what exposay's interaction with all of
this should be :)

> +      Warning! The protocol described in this file is experimental. Each version
> +      of this protocol should be considered incompatible with any other version,
> +      and a client binding to a version different to the one advertised will be
> +      terminated. When the protocol is stabalized, backward compatibility is
> +      guaranteed, the '_' prefix will be removed from the name and the version
> +      will be reset to 1.
> +    </description>
> +
> +    <enum name="error">
> +      <description summary="wl_pointer_lock error values">
> +        These errors can be emitted in response to wl_pointer_lock requests.
> +      </description>
> +      <entry name="already_locked" value="0" summary="pointer was already locked or confined"/>
> +    </enum>
> +
> +    <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. In the future, when the
> +        compositor deems implementation specific constraints are satisfied, the
> +        pointer lock will be activated and the compositor sends a locked event.

I'm very uneasy about this implementation defined behaviour... (comment
about FPS game vs other use case above)

> +
> +        The protocol provides no guarantee that the constraints are ever
> +        satisfied, and does not require the compositor to send an error if the
> +        constraints cannot ever be satisfied. It is thus possible to request a
> +        lock that will never activate.

Hmm, this bothered me at first, but since lock/confinement is
intersection of input region and lock region, I guess it's possible for
an "invalid" lock to become valid at some point after its creation?

> +
> +        There may not be another lock of any kind requested or active on the
> +        surface for the seat when requesting a lock, and if there is, an error
> +        will be raised. See general pointer lock documentation for more details.
> +
> +        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.
> +
> +        A surface may receive pointer focus without the lock being activated.
> +
> +        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 pointer is locked, the wl_pointer objects of the
> +        corresponding seat will not emit any motion events, but relative motion
> +        events will still be emitted via wl_relative_pointer objects of the
> +        same seat.

Back to my previous question about touch - should we include wl_touch here?

(I think lock is still relevant with touch?  dragging around in an image
viewer window perhaps...)

> +      </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 wl_pointer objects of the
> +      associated seat will not emit any motion events.
> +
> +      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 take the cursor position pointer. If
> +      it does, it will not result in any relative motion events emitted via
> +      wl_relative_motion.
> +
> +      If the surface lock was requested on is destroyed and the lock is not yet
> +      activated, the wl_locked_pointer object is now defunct and must be
> +      destroyed.
> +    </description>
> +
> +    <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>
> +
> +    <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.

Is this state double buffered in some way?  Or is it possible that a I
send the hint, update my buffer, get closed somehow before the buffer
hits this display and things are a little jarring?

Should this be clipped to the surface to prevent apps from throwing the
cursor across the screen on exit?

> +      </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="set_region">
> +      <description summary="set a new lock region">
> +        Set a new region used to lock the pointer.
> +
> +        The new lock region is double-buffered. The new lock region will
> +        only take effect when the associated surface gets its pending state
> +        applied. See wl_surface.commit for details.
> +
> +        The new region has no effect on a lock that has already been activated.

I guess even with double buffered state there's a potential for race
here?  Keyboard resize causes the lock to trigger...

> +
> +        For details about the lock region, see wl_locked_pointer.
> +      </description>
> +
> +      <arg name="region" type="object" interface="wl_region" allow-null="true"
> +           summary="region of surface"/>
> +    </request>
> +
> +    <event name="locked">
> +      <description summary="lock activation event">
> +        Notification that the pointer lock of this seat's pointer is activated.
> +      </description>
> +    </event>
> +
> +    <event name="unlocked">
> +      <description summary="lock deactivation event">
> +        Notification that the pointer lock of seat's pointer is no longer
> +        active. This object is now defunct and should be destroyed.
> +      </description>

Why is the object defunct on unlock?  I guess this is to prevent
immediate recapture?

Thanks,
Derek
(Nothing more below this point)

> +    </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 destroyed.
> +    </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>
> +
> +    <request name="set_region">
> +      <description summary="set a new confine region">
> +        Set a new region used to confine the pointer.
> +
> +        The new confine region is double-buffered. The new confine region will
> +        only take effect when the associated surface gets its pending state
> +        applied. See wl_surface.commit for details.
> +
> +        If the confinement is active when the new confinement region is applied
> +        and the pointer ends up outside of newly applied region, the pointer is
> +        warped to a position within the new confinement region. If warped, a
> +        wl_pointer.motion event will be emitted, but no
> +        wl_relative_pointer.relative_motion event.
> +
> +        For details about the confine region, see wl_confined_pointer.
> +      </description>
> +
> +      <arg name="region" type="object" interface="wl_region" allow-null="true"
> +           summary="region of surface"/>
> +    </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..296be31 100644
> --- a/src/compositor.c
> +++ b/src/compositor.c
> @@ -641,6 +641,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;
> @@ -667,6 +668,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_locks);
> +
>  	return surface;
>  }
>  
> @@ -1870,6 +1873,7 @@ weston_surface_destroy(struct weston_surface *surface)
>  {
>  	struct weston_frame_callback *cb, *next;
>  	struct weston_view *ev, *nv;
> +	struct weston_pointer_lock *pointer_lock, *next_pointer_lock;
>  
>  	if (--surface->ref_count > 0)
>  		return;
> @@ -1897,6 +1901,11 @@ weston_surface_destroy(struct weston_surface *surface)
>  
>  	weston_presentation_feedback_discard_list(&surface->feedback_list);
>  
> +	wl_list_for_each_safe(pointer_lock, next_pointer_lock,
> +			      &surface->pointer_locks,
> +			      link)
> +		weston_pointer_lock_destroy(pointer_lock);
> +
>  	free(surface);
>  }
>  
> @@ -2804,6 +2813,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 55cc88f..2322b33 100644
> --- a/src/compositor.h
> +++ b/src/compositor.h
> @@ -63,6 +63,7 @@ struct shell_surface;
>  struct weston_seat;
>  struct weston_output;
>  struct input_method;
> +struct weston_pointer_lock;
>  
>  enum weston_keyboard_modifier {
>  	MODIFIER_CTRL = (1 << 0),
> @@ -349,6 +350,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;
> @@ -421,6 +423,9 @@ void
>  weston_pointer_set_default_grab(struct weston_pointer *pointer,
>  		const struct weston_pointer_grab_interface *interface);
>  
> +void
> +weston_pointer_lock_destroy(struct weston_pointer_lock *lock);
> +
>  struct weston_keyboard *
>  weston_keyboard_create(void);
>  void
> @@ -723,6 +728,8 @@ struct weston_compositor {
>  	int exit_code;
>  
>  	unsigned int activate_serial;
> +
> +	struct wl_global *pointer_lock;
>  };
>  
>  struct weston_buffer {
> @@ -916,10 +923,38 @@ 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_lock {
> +	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 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;
> @@ -999,6 +1034,9 @@ struct weston_surface {
>  	const char *role_name;
>  
>  	struct weston_timeline_object timeline;
> +
> +	/* An list of per seat pointer locks. */
> +	struct wl_list pointer_locks;
>  };
>  
>  struct weston_subsurface {
> diff --git a/src/input.c b/src/input.c
> index b48a6c9..4a78543 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,8 +35,17 @@
>  #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"
>  
> +enum pointer_lock_type {
> +	POINTER_LOCK_TYPE_LOCK,
> +	POINTER_LOCK_TYPE_CONFINE,
> +};
> +
> +static void
> +maybe_warp_confined_pointer(struct weston_pointer_lock *lock);
> +
>  static void
>  empty_region(pixman_region32_t *region)
>  {
> @@ -43,6 +53,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 +260,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 +289,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) {
> @@ -598,6 +631,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;
>  
> @@ -616,6 +650,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);
>  
> @@ -1163,6 +1199,7 @@ weston_surface_activate(struct weston_surface *surface,
>  			struct weston_seat *seat)
>  {
>  	struct weston_compositor *compositor = seat->compositor;
> +	struct weston_surface_activation_data activation_data;
>  
>  	inc_activate_serial(compositor);
>  
> @@ -1171,7 +1208,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
> @@ -2571,6 +2612,677 @@ 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 enum pointer_lock_type
> +pointer_lock_get_type(struct weston_pointer_lock *lock)
> +{
> +	if (wl_resource_instance_of(lock->resource,
> +				    &_wl_locked_pointer_interface,
> +				    &locked_pointer_interface)) {
> +		return POINTER_LOCK_TYPE_LOCK;
> +	} else if (wl_resource_instance_of(lock->resource,
> +					   &_wl_confined_pointer_interface,
> +					   &confined_pointer_interface)) {
> +		return POINTER_LOCK_TYPE_CONFINE;
> +	}
> +
> +	abort();
> +	return 0;
> +}
> +
> +static void
> +pointer_lock_notify_activated(struct weston_pointer_lock *lock)
> +{
> +	struct wl_resource *resource = lock->resource;
> +
> +	switch (pointer_lock_get_type(lock)) {
> +	case POINTER_LOCK_TYPE_LOCK:
> +		_wl_locked_pointer_send_locked(resource);
> +		break;
> +	case POINTER_LOCK_TYPE_CONFINE:
> +		_wl_confined_pointer_send_confined(resource);
> +		break;
> +	}
> +}
> +
> +static void
> +pointer_lock_notify_deactivated(struct weston_pointer_lock *lock)
> +{
> +	struct wl_resource *resource = lock->resource;
> +
> +	switch (pointer_lock_get_type(lock)) {
> +	case POINTER_LOCK_TYPE_LOCK:
> +		_wl_locked_pointer_send_unlocked(resource);
> +		break;
> +	case POINTER_LOCK_TYPE_CONFINE:
> +		_wl_confined_pointer_send_unconfined(resource);
> +		break;
> +	}
> +}
> +
> +static struct weston_pointer_lock *
> +get_pointer_lock_for_pointer(struct weston_surface *surface,
> +			     struct weston_pointer *pointer)
> +{
> +	struct weston_pointer_lock *lock;
> +
> +	wl_list_for_each(lock, &surface->pointer_locks, link) {
> +		if (lock->pointer == pointer)
> +			return lock;
> +	}
> +
> +	return NULL;
> +}
> +
> +static void
> +enable_pointer_lock(struct weston_pointer_lock *lock,
> +		    struct weston_view *view)
> +{
> +	assert(lock->view == NULL);
> +	lock->view = view;
> +	pointer_lock_notify_activated(lock);
> +	weston_pointer_start_grab(lock->pointer, &lock->grab);
> +}
> +
> +static bool
> +is_pointer_lock_enabled(struct weston_pointer_lock *lock)
> +{
> +	return lock->view != NULL;
> +}
> +
> +void
> +weston_pointer_lock_destroy(struct weston_pointer_lock *lock)
> +{
> +	if (is_pointer_lock_enabled(lock)) {
> +		pointer_lock_notify_deactivated(lock);
> +		weston_pointer_end_grab(lock->grab.pointer);
> +	}
> +
> +	wl_list_remove(&lock->pointer_destroy_listener.link);
> +	wl_list_remove(&lock->surface_destroy_listener.link);
> +	wl_list_remove(&lock->surface_commit_listener.link);
> +	wl_list_remove(&lock->surface_activate_listener.link);
> +
> +	wl_resource_set_user_data(lock->resource, NULL);
> +	pixman_region32_fini(&lock->region);
> +	wl_list_remove(&lock->link);
> +	free(lock);
> +}
> +
> +static void
> +disable_pointer_lock(struct weston_pointer_lock *lock)
> +{
> +	weston_pointer_lock_destroy(lock);
> +}
> +
> +static bool
> +is_within_lock_region(struct weston_pointer_lock *lock,
> +		      wl_fixed_t sx, wl_fixed_t sy)
> +{
> +	struct weston_surface *surface = lock->surface;
> +	pixman_region32_t lock_region;
> +	bool result;
> +
> +	pixman_region32_init(&lock_region);
> +	pixman_region32_intersect(&lock_region, &surface->input, &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_pointer_lock *lock)
> +{
> +	struct weston_surface *surface = lock->surface;
> +	struct weston_view *vit;
> +	struct weston_view *view = NULL;
> +	struct weston_pointer *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(lock,
> +				   wl_fixed_from_int(x),
> +				   wl_fixed_from_int(y)))
> +		return;
> +
> +	enable_pointer_lock(lock, 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_lock *lock =
> +		container_of(grab, struct weston_pointer_lock, grab);
> +
> +	disable_pointer_lock(lock);
> +}
> +
> +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_pointer_lock *lock = wl_resource_get_user_data(resource);
> +
> +	if (!lock)
> +		return;
> +
> +	disable_pointer_lock(lock);
> +}
> +
> +static void
> +pointer_lock_surface_activate(struct wl_listener *listener, void *data)
> +{
> +	struct weston_surface_activation_data *activation = data;
> +	struct weston_pointer *pointer = activation->seat->pointer;
> +	struct weston_surface *focus = activation->surface;
> +	struct weston_pointer_lock *lock =
> +		container_of(listener, struct weston_pointer_lock,
> +			     surface_activate_listener);
> +	bool is_lock_surface;
> +
> +	is_lock_surface = get_pointer_lock_for_pointer(focus, pointer) == lock;
> +
> +	if (is_lock_surface &&  !is_pointer_lock_enabled(lock))
> +		maybe_enable_pointer_lock(lock);
> +	else if (!is_lock_surface && is_pointer_lock_enabled(lock))
> +		disable_pointer_lock(lock);
> +}
> +
> +static void
> +pointer_lock_pointer_destroyed(struct wl_listener *listener, void *data)
> +{
> +	struct weston_pointer_lock *lock =
> +		container_of(listener, struct weston_pointer_lock,
> +			     pointer_destroy_listener);
> +
> +	disable_pointer_lock(lock);
> +}
> +
> +static void
> +pointer_lock_surface_destroyed(struct wl_listener *listener, void *data)
> +{
> +	struct weston_pointer_lock *lock =
> +		container_of(listener, struct weston_pointer_lock,
> +			     surface_destroy_listener);
> +
> +	disable_pointer_lock(lock);
> +}
> +
> +static void
> +pointer_lock_surface_committed(struct wl_listener *listener, void *data)
> +{
> +	struct weston_pointer_lock *lock =
> +		container_of(listener, struct weston_pointer_lock,
> +			     surface_commit_listener);
> +
> +	if (lock->pending_region_set) {
> +		lock->pending_region_set = false;
> +		pixman_region32_copy(&lock->region, &lock->pending_region);
> +		pixman_region32_fini(&lock->pending_region);
> +		pixman_region32_init(&lock->pending_region);
> +	}
> +
> +	if (pointer_lock_get_type(lock) == POINTER_LOCK_TYPE_CONFINE &&
> +	    is_pointer_lock_enabled(lock))
> +		maybe_warp_confined_pointer(lock);
> +}
> +
> +static struct weston_pointer_lock *
> +weston_pointer_lock_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_lock *lock;
> +
> +	lock = zalloc(sizeof *lock);
> +	if (!lock)
> +		return NULL;
> +
> +	pixman_region32_init(&lock->region);
> +	pixman_region32_init(&lock->pending_region);
> +	wl_list_insert(&surface->pointer_locks, &lock->link);
> +	lock->surface = surface;
> +	lock->pointer = pointer;
> +	lock->resource = cr;
> +	lock->grab.interface = grab_interface;
> +	if (region) {
> +		pixman_region32_copy(&lock->region,
> +				     &region->region);
> +	} else {
> +		pixman_region32_fini(&lock->region);
> +		region_init_infinite(&lock->region);
> +	}
> +
> +	lock->surface_activate_listener.notify = pointer_lock_surface_activate;
> +	lock->surface_destroy_listener.notify = pointer_lock_surface_destroyed;
> +	lock->surface_commit_listener.notify = pointer_lock_surface_committed;
> +	lock->pointer_destroy_listener.notify = pointer_lock_pointer_destroyed;
> +
> +	wl_signal_add(&surface->compositor->activate_signal,
> +		      &lock->surface_activate_listener);
> +	wl_signal_add(&pointer->destroy_signal,
> +		      &lock->pointer_destroy_listener);
> +	wl_signal_add(&surface->destroy_signal,
> +		      &lock->surface_destroy_listener);
> +	wl_signal_add(&surface->commit_signal,
> +		      &lock->surface_commit_listener);
> +
> +	return lock;
> +}
> +
> +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;
> +	struct weston_pointer_lock *lock;
> +
> +	if (get_pointer_lock_for_pointer(surface, pointer)) {
> +		wl_resource_post_error(pointer_lock_resource,
> +				       WL_DISPLAY_ERROR_INVALID_OBJECT,
> +				       "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_lock_resource),
> +				id);
> +	if (cr == NULL) {
> +		wl_client_post_no_memory(client);
> +		return;
> +	}
> +
> +	lock = weston_pointer_lock_create(surface, pointer, region,
> +					  cr, grab_interface);
> +	if (lock == NULL) {
> +		wl_client_post_no_memory(client);
> +		return;
> +	}
> +
> +	wl_resource_set_implementation(cr, implementation, lock,
> +				       pointer_lock_lock_resource_destroyed);
> +
> +	maybe_enable_pointer_lock(lock);
> +}
> +
> +static void
> +locked_pointer_destroy(struct wl_client *client,
> +		       struct wl_resource *resource)
> +{
> +	struct weston_pointer_lock *lock = wl_resource_get_user_data(resource);
> +	wl_fixed_t x_hint = lock->x_hint;
> +	wl_fixed_t y_hint = lock->y_hint;
> +	wl_fixed_t x, y;
> +
> +	if (lock->view && lock->hint_set &&
> +	    is_within_lock_region(lock, x_hint, y_hint)) {
> +		weston_view_to_global_fixed(lock->view,
> +					    x_hint, y_hint,
> +					    &x, &y);
> +		weston_pointer_move_to(lock->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_lock *lock = wl_resource_get_user_data(resource);
> +
> +	/* Ignore a set cursor hint that was already sent after the lock
> +	 * was cancelled. */
> +	if (!lock->resource ||
> +	    lock->resource != resource)
> +		return;
> +
> +	lock->hint_set = true;
> +	lock->x_hint = surface_x;
> +	lock->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_lock *lock = 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(&lock->pending_region, &region->region);
> +	} else {
> +		pixman_region32_fini(&lock->pending_region);
> +		region_init_infinite(&lock->pending_region);
> +	}
> +	lock->pending_region_set = true;
> +}
> +
> +
> +static const struct _wl_locked_pointer_interface locked_pointer_interface = {
> +	locked_pointer_destroy,
> +	locked_pointer_set_cursor_position_hint,
> +	locked_pointer_set_region,
> +};
> +
> +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
> +maybe_warp_confined_pointer(struct weston_pointer_lock *lock)
> +{
> +	wl_fixed_t x;
> +	wl_fixed_t y;
> +	wl_fixed_t sx;
> +	wl_fixed_t sy;
> +
> +	weston_view_from_global_fixed(lock->view,
> +				      lock->pointer->x,
> +				      lock->pointer->y,
> +				      &sx,
> +				      &sy);
> +
> +	if (!is_within_lock_region(lock, sx, sy)) {
> +		pixman_region32_t *region = &lock->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(lock->view, sx, sy, &x, &y);
> +		weston_pointer_move_to(lock->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_lock *lock =
> +		container_of(grab, struct weston_pointer_lock, 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 == lock->surface);
> +
> +	surface = pointer->focus->surface;
> +
> +	pixman_region32_init(&confine_region);
> +	pixman_region32_intersect(&confine_region,
> +				  &surface->input,
> +				  &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_pointer_lock *lock =
> +		container_of(grab, struct weston_pointer_lock, grab);
> +
> +	disable_pointer_lock(lock);
> +}
> +
> +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_lock *lock = 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(&lock->pending_region, &region->region);
> +	} else {
> +		pixman_region32_fini(&lock->pending_region);
> +		region_init_infinite(&lock->pending_region);
> +	}
> +	lock->pending_region_set = true;
> +}
> +
> +static const struct _wl_confined_pointer_interface confined_pointer_interface = {
> +	confined_pointer_destroy,
> +	confined_pointer_set_region,
> +};
> +
> +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)
>  {
> @@ -2579,5 +3291,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;
>  }
>
On Sat, Aug 22, 2015 at 06:49:15PM -0500, Derek Foreman wrote:
> Hi Jonas,
> 
> Instead of a reasonable review, I'm going to mostly ask a bunch of noob
> questions.  Feel free to disregard if you don't have time to enlighten me...

Thanks for the input. I'll answer inline.

> 
> On 25/06/15 11:38 PM, Jonas Ådahl wrote:
> > 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@gmail.com>
> > ---
> > 
> > Changes since v2:
> > 
> >  * Updated copyright.
> >  * Updated to the fixed license text.
> >  * Clarified that locks are on made on a certain surface from a given
> >    seat. The same surface may have many parallel locks; one per seat.
> >  * Added an error enum.
> >  * Changed the wording explaining that constraints for activating a lock
> >    is compositor specific.
> >  * Clarified that there is no guarantee that a lock will ever be
> >    activated. No error will be raised if the compositor will never
> >    activate the lock.
> >  * Changed wording related to pointer events (make them speak protocol,
> >    not abstract events).
> >  * Clarify what happens if a surface is destroyed.
> >  * Fixed various typos.
> >  * Implemented support for per-seat locks.
> >  * Clarified what happens when effective lock/confine regions change.
> >  * Added wl_locked_pointer.set_region and wl_confined_pointer.set_region
> >    (see below).
> >  * Implemented support for above mentioned new requests.
> >  * Changed the order of requests (put destroy first).
> >  * Added 'unstable protocol' warning and explained its semantics.
> > 
> > This version adds support for changing the lock/confine region after the
> > lock was created.
> > 
> > This is so that a client can change the lock region without being
> > exposed to a race condition. Depending on the type of lock
> > (lock/confine). For exampe, a client who maximizes wants to keep the
> > pending lock or pending/active confinement intact can use the set_region
> > request. The region is double buffered, meaning it is synchronized with
> > the surface input region.
> > 
> > For wl_locked_pointer, since a changed effective region has no effect on
> > the lock after it being activated, the set_region request only has effect
> > before the lock is activated. As such, it affects the region that is used
> > to activate the lock.
> > 
> > For pending wl_confined_pointer lock, the effect is the same as for
> > wl_locked_pointer. But for an already activated confinement, it affects the
> > pointer, i.e. warps the pointer to somewhere within the region.
> > 
> > 
> > Jonas
> > 
> > 
> >  Makefile.am               |   3 +
> >  protocol/pointer-lock.xml | 275 +++++++++++++++++
> >  src/compositor.c          |  11 +
> >  src/compositor.h          |  38 +++
> >  src/input.c               | 751 ++++++++++++++++++++++++++++++++++++++++++++--
> >  5 files changed, 1061 insertions(+), 17 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..ee5e274
> > --- /dev/null
> > +++ b/protocol/pointer-lock.xml
> > @@ -0,0 +1,275 @@
> > +<?xml version="1.0" encoding="UTF-8"?>
> > +<protocol name="pointer_lock">
> > +
> > +  <copyright>
> > +    Copyright © 2014      Jonas Ådahl
> > +    Copyright © 2015      Red Hat Inc.
> > +
> > +    Permission is hereby granted, free of charge, to any person obtaining a
> > +    copy of this software and associated documentation files (the "Software"),
> > +    to deal in the Software without restriction, including without limitation
> > +    the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > +    and/or sell copies of the Software, and to permit persons to whom the
> > +    Software is furnished to do so, subject to the following conditions:
> > +
> > +    The above copyright notice and this permission notice (including the next
> > +    paragraph) shall be included in all copies or substantial portions of the
> > +    Software.
> > +
> > +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > +    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > +    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > +    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > +    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > +    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> > +    DEALINGS IN THE 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.
> 
> Why have both confinement and lock in the same protocol?  It seems like
> lock is perhaps simpler and could be more easily merged independently.
> 
> Are they combined in order to enforce the "only one lock/confinement per
> surface+seat combination" policy?

Of course we could just wait with confinement, but as you say, the
belong together, and adding confinement I'd say should just be added in
the same way as it is now, because they both apply similar types of
constraints to pointer movements. I've been thinking of renaming
"wl_pointer_lock" to "wl_pointer_constraint" to make it more clear that
it's different ways of constraining the pointer.

> 
> Is this the same interface I'd use for constraining touch input?

No. Touch input can't really be constrained except with physical
borders. Are you referring to touch grabs? That's something completely
different.

> 
> > +      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.
> > +
> > +      For any surface, only one lock or confinement per seat may be active at
> > +      any time. If a lock or confinement is requested when another lock or
> > +      confinement is active on that surface and seat, an 'already_locked' error
> > +      will be raised.
> 
> Why this limitation?

Because anything else is simply not possible. While one surface has
locked the cursor to a position, it is not possible that another surface
also has the focus of the same pointer. These paragraphs are just
clarifying reality.

> 
> If I have a surface with a "slow scrollbar" and during its use I need to
> add another scrollbar (I dunno, slow loading web page?), my app will
> need to remember that it hasn't set up that new scrollbar's confinement
> region until after the release of the active scrollbar?
> 
> (How would I implement a slow scrollbar?  if the compositor has
> implementation defined semantics for the confinement, I guess I can't?)

I would need to know exactly what a slow scrollbar is according to you
before saying how to implement it, but the two ones I can imagine:

1. the pointer "attaches" to the UI element.

One would create a lock region on the scroll handle. When enabled one
would hide the cursor, create an identical cursor surface and add it as
a subsurface positioning relative to the scroll handle. One would now
listen for events from wl_relative_pointer, update the subsurface
position and the scroll handle widget under it. When done, one would
unlock the lock, destroy the subsurface, and reset the pointer cursor.
During the moving, one would set the cursor position hint so when
resetting the cursor position would be updated.

This is the only way to guarantee the "attachment" effect. There is one
unsolved issue which is that the cursor sprite and the subsurface might
be visible at the same time, and we'd need some kind of "transaction"
protocol to solve this. There has been ideas to have a "set_cursor_pos"
but this method would never give us any guarantees about "attachment".

2. the pointer hides and the UI element moves slow

This would work just the same as above, but without the subsurface.


The point of the implementation defined semantics are to let the
compositor implement protection against clients stealing the pointer.
Example: the compositor uses sloppy focus, a client has a lock/confine
request, the user moves the pointer over such a window with the
intention of interacting with a window on the other side. We want to
enable the compositor to try to avoid the surface in beween to steal the
pointer, which it would if we specify the protocol to always activate a
lock when the pointer enters a certain region, or as a guaranteed result
of a client request.

Also, we shouldn't IMO specify exactly what semantics is needed (like,
click to activate etc) because then we'd make it impossible to improve
this behaviour without updating the protocol without breaking semantical
backward compatibiliy.

> 
> Should there be a more explicit way for the application to control what
> causes the activation?  (activate if the user clicks/drags within the
> region vs activate if the mouse merely enters the region)

If the client wants to control the activation, it can simply request the
lock exactly at the point it wants it to be activated. resize.c in
weston is an example of this - it activates the lock on a pointer button
event.

> 
> For lock in a FPS game, simply focusing the window should probably
> trigger the lock - so the compositor should warp the pointer there if
> the window becomes focused by any means, but are there any use cases
> where such a warp would be incorrect behaviour?

A VM UI might want this. If one alt-tabs to the VM UI, one might want to
interact with the controls. For games, maybe, but how would one make it
possible have window controls? For a fullscreen game it's not an issue
since alt-tab:ing would most likely make the pointer end up in a place
so that it'd be inside the region anyway.

> 
> Presumably I can use meta+tab to focus a different surface and break a
> pointer lock/confinement.  Will that break the lock/confinement for that
> surface, or will the lock/confinement be resumed next time that surface
> obtains focus?

It'll break. The client will now need to request the lock again.

> 
> for weston specifically, I wonder what exposay's interaction with all of
> this should be :)

I imagine exposay windows should behave as fake previews, not
locking/confining anything until in "normal" mode. I don't think we need
to specify this in the protocol.

> 
> > +      Warning! The protocol described in this file is experimental. Each version
> > +      of this protocol should be considered incompatible with any other version,
> > +      and a client binding to a version different to the one advertised will be
> > +      terminated. When the protocol is stabalized, backward compatibility is
> > +      guaranteed, the '_' prefix will be removed from the name and the version
> > +      will be reset to 1.
> > +    </description>
> > +
> > +    <enum name="error">
> > +      <description summary="wl_pointer_lock error values">
> > +        These errors can be emitted in response to wl_pointer_lock requests.
> > +      </description>
> > +      <entry name="already_locked" value="0" summary="pointer was already locked or confined"/>
> > +    </enum>
> > +
> > +    <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. In the future, when the
> > +        compositor deems implementation specific constraints are satisfied, the
> > +        pointer lock will be activated and the compositor sends a locked event.
> 
> I'm very uneasy about this implementation defined behaviour... (comment
> about FPS game vs other use case above)

Explained above. This is to make it possible to implement
anti-pointer-stealing semantics.

> > +
> > +        The protocol provides no guarantee that the constraints are ever
> > +        satisfied, and does not require the compositor to send an error if the
> > +        constraints cannot ever be satisfied. It is thus possible to request a
> > +        lock that will never activate.
> 
> Hmm, this bothered me at first, but since lock/confinement is
> intersection of input region and lock region, I guess it's possible for
> an "invalid" lock to become valid at some point after its creation?

Well, the compositor can have a setting called "no_pointer_locking", and
all locks will be unactivateable/invalid. That setting might change, and
they became activatable/valid again.

Validity due to client input can change as well, for example setting an
empty input region.

> 
> > +
> > +        There may not be another lock of any kind requested or active on the
> > +        surface for the seat when requesting a lock, and if there is, an error
> > +        will be raised. See general pointer lock documentation for more details.
> > +
> > +        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.
> > +
> > +        A surface may receive pointer focus without the lock being activated.
> > +
> > +        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 pointer is locked, the wl_pointer objects of the
> > +        corresponding seat will not emit any motion events, but relative motion
> > +        events will still be emitted via wl_relative_pointer objects of the
> > +        same seat.
> 
> Back to my previous question about touch - should we include wl_touch here?
> 
> (I think lock is still relevant with touch?  dragging around in an image
> viewer window perhaps...)

No. This has nothing to do with touch. Touch can't be constrained.

> 
> > +      </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 wl_pointer objects of the
> > +      associated seat will not emit any motion events.
> > +
> > +      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 take the cursor position pointer. If
> > +      it does, it will not result in any relative motion events emitted via
> > +      wl_relative_motion.
> > +
> > +      If the surface lock was requested on is destroyed and the lock is not yet
> > +      activated, the wl_locked_pointer object is now defunct and must be
> > +      destroyed.
> > +    </description>
> > +
> > +    <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>
> > +
> > +    <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.
> 
> Is this state double buffered in some way?  Or is it possible that a I
> send the hint, update my buffer, get closed somehow before the buffer
> hits this display and things are a little jarring?

At the current state, no it is not. I guess it could be made to be.

> 
> Should this be clipped to the surface to prevent apps from throwing the
> cursor across the screen on exit?

Indeed. Its actually clipped to the lock region in the implementation,
but that might not be good. Surface region makes sense.

> 
> > +      </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="set_region">
> > +      <description summary="set a new lock region">
> > +        Set a new region used to lock the pointer.
> > +
> > +        The new lock region is double-buffered. The new lock region will
> > +        only take effect when the associated surface gets its pending state
> > +        applied. See wl_surface.commit for details.
> > +
> > +        The new region has no effect on a lock that has already been activated.
> 
> I guess even with double buffered state there's a potential for race
> here?  Keyboard resize causes the lock to trigger...

What's a keyboard resize? The point of this is to make it possible to
update the surface state (input region) and lock state atomically.

> 
> > +
> > +        For details about the lock region, see wl_locked_pointer.
> > +      </description>
> > +
> > +      <arg name="region" type="object" interface="wl_region" allow-null="true"
> > +           summary="region of surface"/>
> > +    </request>
> > +
> > +    <event name="locked">
> > +      <description summary="lock activation event">
> > +        Notification that the pointer lock of this seat's pointer is activated.
> > +      </description>
> > +    </event>
> > +
> > +    <event name="unlocked">
> > +      <description summary="lock deactivation event">
> > +        Notification that the pointer lock of seat's pointer is no longer
> > +        active. This object is now defunct and should be destroyed.
> > +      </description>
> 
> Why is the object defunct on unlock?  I guess this is to prevent
> immediate recapture?

It is defunct because the client is expected to create a new lock object
if it wants to lock again

Thanks again for the input.


Jonas


> 
> Thanks,
> Derek
> (Nothing more below this point)
> 
> > +    </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 destroyed.
> > +    </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>
> > +
> > +    <request name="set_region">
> > +      <description summary="set a new confine region">
> > +        Set a new region used to confine the pointer.
> > +
> > +        The new confine region is double-buffered. The new confine region will
> > +        only take effect when the associated surface gets its pending state
> > +        applied. See wl_surface.commit for details.
> > +
> > +        If the confinement is active when the new confinement region is applied
> > +        and the pointer ends up outside of newly applied region, the pointer is
> > +        warped to a position within the new confinement region. If warped, a
> > +        wl_pointer.motion event will be emitted, but no
> > +        wl_relative_pointer.relative_motion event.
> > +
> > +        For details about the confine region, see wl_confined_pointer.
> > +      </description>
> > +
> > +      <arg name="region" type="object" interface="wl_region" allow-null="true"
> > +           summary="region of surface"/>
> > +    </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..296be31 100644
> > --- a/src/compositor.c
> > +++ b/src/compositor.c
> > @@ -641,6 +641,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;
> > @@ -667,6 +668,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_locks);
> > +
> >  	return surface;
> >  }
> >  
> > @@ -1870,6 +1873,7 @@ weston_surface_destroy(struct weston_surface *surface)
> >  {
> >  	struct weston_frame_callback *cb, *next;
> >  	struct weston_view *ev, *nv;
> > +	struct weston_pointer_lock *pointer_lock, *next_pointer_lock;
> >  
> >  	if (--surface->ref_count > 0)
> >  		return;
> > @@ -1897,6 +1901,11 @@ weston_surface_destroy(struct weston_surface *surface)
> >  
> >  	weston_presentation_feedback_discard_list(&surface->feedback_list);
> >  
> > +	wl_list_for_each_safe(pointer_lock, next_pointer_lock,
> > +			      &surface->pointer_locks,
> > +			      link)
> > +		weston_pointer_lock_destroy(pointer_lock);
> > +
> >  	free(surface);
> >  }
> >  
> > @@ -2804,6 +2813,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 55cc88f..2322b33 100644
> > --- a/src/compositor.h
> > +++ b/src/compositor.h
> > @@ -63,6 +63,7 @@ struct shell_surface;
> >  struct weston_seat;
> >  struct weston_output;
> >  struct input_method;
> > +struct weston_pointer_lock;
> >  
> >  enum weston_keyboard_modifier {
> >  	MODIFIER_CTRL = (1 << 0),
> > @@ -349,6 +350,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;
> > @@ -421,6 +423,9 @@ void
> >  weston_pointer_set_default_grab(struct weston_pointer *pointer,
> >  		const struct weston_pointer_grab_interface *interface);
> >  
> > +void
> > +weston_pointer_lock_destroy(struct weston_pointer_lock *lock);
> > +
> >  struct weston_keyboard *
> >  weston_keyboard_create(void);
> >  void
> > @@ -723,6 +728,8 @@ struct weston_compositor {
> >  	int exit_code;
> >  
> >  	unsigned int activate_serial;
> > +
> > +	struct wl_global *pointer_lock;
> >  };
> >  
> >  struct weston_buffer {
> > @@ -916,10 +923,38 @@ 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_lock {
> > +	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 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;
> > @@ -999,6 +1034,9 @@ struct weston_surface {
> >  	const char *role_name;
> >  
> >  	struct weston_timeline_object timeline;
> > +
> > +	/* An list of per seat pointer locks. */
> > +	struct wl_list pointer_locks;
> >  };
> >  
> >  struct weston_subsurface {
> > diff --git a/src/input.c b/src/input.c
> > index b48a6c9..4a78543 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,8 +35,17 @@
> >  #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"
> >  
> > +enum pointer_lock_type {
> > +	POINTER_LOCK_TYPE_LOCK,
> > +	POINTER_LOCK_TYPE_CONFINE,
> > +};
> > +
> > +static void
> > +maybe_warp_confined_pointer(struct weston_pointer_lock *lock);
> > +
> >  static void
> >  empty_region(pixman_region32_t *region)
> >  {
> > @@ -43,6 +53,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 +260,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 +289,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) {
> > @@ -598,6 +631,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;
> >  
> > @@ -616,6 +650,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);
> >  
> > @@ -1163,6 +1199,7 @@ weston_surface_activate(struct weston_surface *surface,
> >  			struct weston_seat *seat)
> >  {
> >  	struct weston_compositor *compositor = seat->compositor;
> > +	struct weston_surface_activation_data activation_data;
> >  
> >  	inc_activate_serial(compositor);
> >  
> > @@ -1171,7 +1208,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
> > @@ -2571,6 +2612,677 @@ 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 enum pointer_lock_type
> > +pointer_lock_get_type(struct weston_pointer_lock *lock)
> > +{
> > +	if (wl_resource_instance_of(lock->resource,
> > +				    &_wl_locked_pointer_interface,
> > +				    &locked_pointer_interface)) {
> > +		return POINTER_LOCK_TYPE_LOCK;
> > +	} else if (wl_resource_instance_of(lock->resource,
> > +					   &_wl_confined_pointer_interface,
> > +					   &confined_pointer_interface)) {
> > +		return POINTER_LOCK_TYPE_CONFINE;
> > +	}
> > +
> > +	abort();
> > +	return 0;
> > +}
> > +
> > +static void
> > +pointer_lock_notify_activated(struct weston_pointer_lock *lock)
> > +{
> > +	struct wl_resource *resource = lock->resource;
> > +
> > +	switch (pointer_lock_get_type(lock)) {
> > +	case POINTER_LOCK_TYPE_LOCK:
> > +		_wl_locked_pointer_send_locked(resource);
> > +		break;
> > +	case POINTER_LOCK_TYPE_CONFINE:
> > +		_wl_confined_pointer_send_confined(resource);
> > +		break;
> > +	}
> > +}
> > +
> > +static void
> > +pointer_lock_notify_deactivated(struct weston_pointer_lock *lock)
> > +{
> > +	struct wl_resource *resource = lock->resource;
> > +
> > +	switch (pointer_lock_get_type(lock)) {
> > +	case POINTER_LOCK_TYPE_LOCK:
> > +		_wl_locked_pointer_send_unlocked(resource);
> > +		break;
> > +	case POINTER_LOCK_TYPE_CONFINE:
> > +		_wl_confined_pointer_send_unconfined(resource);
> > +		break;
> > +	}
> > +}
> > +
> > +static struct weston_pointer_lock *
> > +get_pointer_lock_for_pointer(struct weston_surface *surface,
> > +			     struct weston_pointer *pointer)
> > +{
> > +	struct weston_pointer_lock *lock;
> > +
> > +	wl_list_for_each(lock, &surface->pointer_locks, link) {
> > +		if (lock->pointer == pointer)
> > +			return lock;
> > +	}
> > +
> > +	return NULL;
> > +}
> > +
> > +static void
> > +enable_pointer_lock(struct weston_pointer_lock *lock,
> > +		    struct weston_view *view)
> > +{
> > +	assert(lock->view == NULL);
> > +	lock->view = view;
> > +	pointer_lock_notify_activated(lock);
> > +	weston_pointer_start_grab(lock->pointer, &lock->grab);
> > +}
> > +
> > +static bool
> > +is_pointer_lock_enabled(struct weston_pointer_lock *lock)
> > +{
> > +	return lock->view != NULL;
> > +}
> > +
> > +void
> > +weston_pointer_lock_destroy(struct weston_pointer_lock *lock)
> > +{
> > +	if (is_pointer_lock_enabled(lock)) {
> > +		pointer_lock_notify_deactivated(lock);
> > +		weston_pointer_end_grab(lock->grab.pointer);
> > +	}
> > +
> > +	wl_list_remove(&lock->pointer_destroy_listener.link);
> > +	wl_list_remove(&lock->surface_destroy_listener.link);
> > +	wl_list_remove(&lock->surface_commit_listener.link);
> > +	wl_list_remove(&lock->surface_activate_listener.link);
> > +
> > +	wl_resource_set_user_data(lock->resource, NULL);
> > +	pixman_region32_fini(&lock->region);
> > +	wl_list_remove(&lock->link);
> > +	free(lock);
> > +}
> > +
> > +static void
> > +disable_pointer_lock(struct weston_pointer_lock *lock)
> > +{
> > +	weston_pointer_lock_destroy(lock);
> > +}
> > +
> > +static bool
> > +is_within_lock_region(struct weston_pointer_lock *lock,
> > +		      wl_fixed_t sx, wl_fixed_t sy)
> > +{
> > +	struct weston_surface *surface = lock->surface;
> > +	pixman_region32_t lock_region;
> > +	bool result;
> > +
> > +	pixman_region32_init(&lock_region);
> > +	pixman_region32_intersect(&lock_region, &surface->input, &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_pointer_lock *lock)
> > +{
> > +	struct weston_surface *surface = lock->surface;
> > +	struct weston_view *vit;
> > +	struct weston_view *view = NULL;
> > +	struct weston_pointer *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(lock,
> > +				   wl_fixed_from_int(x),
> > +				   wl_fixed_from_int(y)))
> > +		return;
> > +
> > +	enable_pointer_lock(lock, 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_lock *lock =
> > +		container_of(grab, struct weston_pointer_lock, grab);
> > +
> > +	disable_pointer_lock(lock);
> > +}
> > +
> > +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_pointer_lock *lock = wl_resource_get_user_data(resource);
> > +
> > +	if (!lock)
> > +		return;
> > +
> > +	disable_pointer_lock(lock);
> > +}
> > +
> > +static void
> > +pointer_lock_surface_activate(struct wl_listener *listener, void *data)
> > +{
> > +	struct weston_surface_activation_data *activation = data;
> > +	struct weston_pointer *pointer = activation->seat->pointer;
> > +	struct weston_surface *focus = activation->surface;
> > +	struct weston_pointer_lock *lock =
> > +		container_of(listener, struct weston_pointer_lock,
> > +			     surface_activate_listener);
> > +	bool is_lock_surface;
> > +
> > +	is_lock_surface = get_pointer_lock_for_pointer(focus, pointer) == lock;
> > +
> > +	if (is_lock_surface &&  !is_pointer_lock_enabled(lock))
> > +		maybe_enable_pointer_lock(lock);
> > +	else if (!is_lock_surface && is_pointer_lock_enabled(lock))
> > +		disable_pointer_lock(lock);
> > +}
> > +
> > +static void
> > +pointer_lock_pointer_destroyed(struct wl_listener *listener, void *data)
> > +{
> > +	struct weston_pointer_lock *lock =
> > +		container_of(listener, struct weston_pointer_lock,
> > +			     pointer_destroy_listener);
> > +
> > +	disable_pointer_lock(lock);
> > +}
> > +
> > +static void
> > +pointer_lock_surface_destroyed(struct wl_listener *listener, void *data)
> > +{
> > +	struct weston_pointer_lock *lock =
> > +		container_of(listener, struct weston_pointer_lock,
> > +			     surface_destroy_listener);
> > +
> > +	disable_pointer_lock(lock);
> > +}
> > +
> > +static void
> > +pointer_lock_surface_committed(struct wl_listener *listener, void *data)
> > +{
> > +	struct weston_pointer_lock *lock =
> > +		container_of(listener, struct weston_pointer_lock,
> > +			     surface_commit_listener);
> > +
> > +	if (lock->pending_region_set) {
> > +		lock->pending_region_set = false;
> > +		pixman_region32_copy(&lock->region, &lock->pending_region);
> > +		pixman_region32_fini(&lock->pending_region);
> > +		pixman_region32_init(&lock->pending_region);
> > +	}
> > +
> > +	if (pointer_lock_get_type(lock) == POINTER_LOCK_TYPE_CONFINE &&
> > +	    is_pointer_lock_enabled(lock))
> > +		maybe_warp_confined_pointer(lock);
> > +}
> > +
> > +static struct weston_pointer_lock *
> > +weston_pointer_lock_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_lock *lock;
> > +
> > +	lock = zalloc(sizeof *lock);
> > +	if (!lock)
> > +		return NULL;
> > +
> > +	pixman_region32_init(&lock->region);
> > +	pixman_region32_init(&lock->pending_region);
> > +	wl_list_insert(&surface->pointer_locks, &lock->link);
> > +	lock->surface = surface;
> > +	lock->pointer = pointer;
> > +	lock->resource = cr;
> > +	lock->grab.interface = grab_interface;
> > +	if (region) {
> > +		pixman_region32_copy(&lock->region,
> > +				     &region->region);
> > +	} else {
> > +		pixman_region32_fini(&lock->region);
> > +		region_init_infinite(&lock->region);
> > +	}
> > +
> > +	lock->surface_activate_listener.notify = pointer_lock_surface_activate;
> > +	lock->surface_destroy_listener.notify = pointer_lock_surface_destroyed;
> > +	lock->surface_commit_listener.notify = pointer_lock_surface_committed;
> > +	lock->pointer_destroy_listener.notify = pointer_lock_pointer_destroyed;
> > +
> > +	wl_signal_add(&surface->compositor->activate_signal,
> > +		      &lock->surface_activate_listener);
> > +	wl_signal_add(&pointer->destroy_signal,
> > +		      &lock->pointer_destroy_listener);
> > +	wl_signal_add(&surface->destroy_signal,
> > +		      &lock->surface_destroy_listener);
> > +	wl_signal_add(&surface->commit_signal,
> > +		      &lock->surface_commit_listener);
> > +
> > +	return lock;
> > +}
> > +
> > +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;
> > +	struct weston_pointer_lock *lock;
> > +
> > +	if (get_pointer_lock_for_pointer(surface, pointer)) {
> > +		wl_resource_post_error(pointer_lock_resource,
> > +				       WL_DISPLAY_ERROR_INVALID_OBJECT,
> > +				       "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_lock_resource),
> > +				id);
> > +	if (cr == NULL) {
> > +		wl_client_post_no_memory(client);
> > +		return;
> > +	}
> > +
> > +	lock = weston_pointer_lock_create(surface, pointer, region,
> > +					  cr, grab_interface);
> > +	if (lock == NULL) {
> > +		wl_client_post_no_memory(client);
> > +		return;
> > +	}
> > +
> > +	wl_resource_set_implementation(cr, implementation, lock,
> > +				       pointer_lock_lock_resource_destroyed);
> > +
> > +	maybe_enable_pointer_lock(lock);
> > +}
> > +
> > +static void
> > +locked_pointer_destroy(struct wl_client *client,
> > +		       struct wl_resource *resource)
> > +{
> > +	struct weston_pointer_lock *lock = wl_resource_get_user_data(resource);
> > +	wl_fixed_t x_hint = lock->x_hint;
> > +	wl_fixed_t y_hint = lock->y_hint;
> > +	wl_fixed_t x, y;
> > +
> > +	if (lock->view && lock->hint_set &&
> > +	    is_within_lock_region(lock, x_hint, y_hint)) {
> > +		weston_view_to_global_fixed(lock->view,
> > +					    x_hint, y_hint,
> > +					    &x, &y);
> > +		weston_pointer_move_to(lock->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_lock *lock = wl_resource_get_user_data(resource);
> > +
> > +	/* Ignore a set cursor hint that was already sent after the lock
> > +	 * was cancelled. */
> > +	if (!lock->resource ||
> > +	    lock->resource != resource)
> > +		return;
> > +
> > +	lock->hint_set = true;
> > +	lock->x_hint = surface_x;
> > +	lock->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_lock *lock = 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(&lock->pending_region, &region->region);
> > +	} else {
> > +		pixman_region32_fini(&lock->pending_region);
> > +		region_init_infinite(&lock->pending_region);
> > +	}
> > +	lock->pending_region_set = true;
> > +}
> > +
> > +
> > +static const struct _wl_locked_pointer_interface locked_pointer_interface = {
> > +	locked_pointer_destroy,
> > +	locked_pointer_set_cursor_position_hint,
> > +	locked_pointer_set_region,
> > +};
> > +
> > +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
> > +maybe_warp_confined_pointer(struct weston_pointer_lock *lock)
> > +{
> > +	wl_fixed_t x;
> > +	wl_fixed_t y;
> > +	wl_fixed_t sx;
> > +	wl_fixed_t sy;
> > +
> > +	weston_view_from_global_fixed(lock->view,
> > +				      lock->pointer->x,
> > +				      lock->pointer->y,
> > +				      &sx,
> > +				      &sy);
> > +
> > +	if (!is_within_lock_region(lock, sx, sy)) {
> > +		pixman_region32_t *region = &lock->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(lock->view, sx, sy, &x, &y);
> > +		weston_pointer_move_to(lock->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_lock *lock =
> > +		container_of(grab, struct weston_pointer_lock, 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 == lock->surface);
> > +
> > +	surface = pointer->focus->surface;
> > +
> > +	pixman_region32_init(&confine_region);
> > +	pixman_region32_intersect(&confine_region,
> > +				  &surface->input,
> > +				  &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_pointer_lock *lock =
> > +		container_of(grab, struct weston_pointer_lock, grab);
> > +
> > +	disable_pointer_lock(lock);
> > +}
> > +
> > +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_lock *lock = 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(&lock->pending_region, &region->region);
> > +	} else {
> > +		pixman_region32_fini(&lock->pending_region);
> > +		region_init_infinite(&lock->pending_region);
> > +	}
> > +	lock->pending_region_set = true;
> > +}
> > +
> > +static const struct _wl_confined_pointer_interface confined_pointer_interface = {
> > +	confined_pointer_destroy,
> > +	confined_pointer_set_region,
> > +};
> > +
> > +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)
> >  {
> > @@ -2579,5 +3291,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;
> >  }
> > 
>
On Sun, Aug 23, 2015 at 8:19 PM, Jonas Ådahl <jadahl@gmail.com> wrote:

>
> 1. the pointer "attaches" to the UI element.
>
> One would create a lock region on the scroll handle. When enabled one
> would hide the cursor, create an identical cursor surface and add it as
> a subsurface positioning relative to the scroll handle. One would now
> listen for events from wl_relative_pointer, update the subsurface
> position and the scroll handle widget under it. When done, one would
> unlock the lock, destroy the subsurface, and reset the pointer cursor.
> During the moving, one would set the cursor position hint so when
> resetting the cursor position would be updated.
>

This is the first sign that you have actually thought about this. However
you seem blind to the trivial solution. Here is my rewrite of the above
paragraph with the set_cursor_pos proposal:

"One would create a lock region on the scroll handle. When enabled one
would now listen for events from wl_relative_pointer, update the cursor
position and the scroll handle widget under it. When done, one would unlock
the lock."

This is the only way to guarantee the "attachment" effect. There is one
> unsolved issue which is that the cursor sprite and the subsurface might
> be visible at the same time, and we'd need some kind of "transaction"
> protocol to solve this. There has been ideas to have a "set_cursor_pos"
> but this method would never give us any guarantees about "attachment".
>

WTF???. Your proposal is that the client draw a fake cursor in a subsurface
and setting that to some x,y position to move the cursor. I propose that
the same x,y are sent to set_cursor_pos. THE EXACT SAME NUMBERS!  It is
impossible for "attachement" to be different between the two proposals!!!!

All I can guess is that you think that there is going to be synchronization
between subsurface positioning and the surface image, but for some reason
such synchronization will be missing for set_cursor_pos. But why? The
cursor surface belongs to the locking client just as much as any
subsurface, so any rules for subsurfaces can be applied to it as well. I'd
also like to know why suddenly this is important, when you have repeatedly
claimed that avoiding latency is more important that sync image updating.

If the client wants to control the activation, it can simply request the
> lock exactly at the point it wants it to be activated. resize.c in
> weston is an example of this - it activates the lock on a pointer button
> event.
>

This is great if it works, however I hope you realize that this will be the
ONLY way that any clients with small lock regions will turn on pointer
lock. Having to update the "activation region" for every widget on every
layout change is extremely bad, it will send large amounts of data to the
server for no reason, kind of like old X11 toolkits that sent every widget
as a subwindow. This was known to be a mistake back in the mid 80's when
lightweight widgets were added to x intrinsics.
On Mon, Aug 24, 2015 at 10:44:55AM -0700, Bill Spitzak wrote:
> On Sun, Aug 23, 2015 at 8:19 PM, Jonas Ådahl <jadahl@gmail.com> wrote:
> 
> >
> > 1. the pointer "attaches" to the UI element.
> >
> > One would create a lock region on the scroll handle. When enabled one
> > would hide the cursor, create an identical cursor surface and add it as
> > a subsurface positioning relative to the scroll handle. One would now
> > listen for events from wl_relative_pointer, update the subsurface
> > position and the scroll handle widget under it. When done, one would
> > unlock the lock, destroy the subsurface, and reset the pointer cursor.
> > During the moving, one would set the cursor position hint so when
> > resetting the cursor position would be updated.
> >
> 
> This is the first sign that you have actually thought about this. However
> you seem blind to the trivial solution. Here is my rewrite of the above
> paragraph with the set_cursor_pos proposal:
> 
> "One would create a lock region on the scroll handle. When enabled one
> would now listen for events from wl_relative_pointer, update the cursor
> position and the scroll handle widget under it. When done, one would unlock
> the lock."
> 
> This is the only way to guarantee the "attachment" effect. There is one
> > unsolved issue which is that the cursor sprite and the subsurface might
> > be visible at the same time, and we'd need some kind of "transaction"
> > protocol to solve this. There has been ideas to have a "set_cursor_pos"
> > but this method would never give us any guarantees about "attachment".
> >
> 
> WTF???. Your proposal is that the client draw a fake cursor in a subsurface
> and setting that to some x,y position to move the cursor. I propose that
> the same x,y are sent to set_cursor_pos. THE EXACT SAME NUMBERS!  It is
> impossible for "attachement" to be different between the two proposals!!!!
> 
> All I can guess is that you think that there is going to be synchronization
> between subsurface positioning and the surface image, but for some reason
> such synchronization will be missing for set_cursor_pos. But why? The
> cursor surface belongs to the locking client just as much as any
> subsurface, so any rules for subsurfaces can be applied to it as well. I'd
> also like to know why suddenly this is important, when you have repeatedly
> claimed that avoiding latency is more important that sync image updating.

Not going to go down this road again.

> 
> If the client wants to control the activation, it can simply request the
> > lock exactly at the point it wants it to be activated. resize.c in
> > weston is an example of this - it activates the lock on a pointer button
> > event.
> >
> 
> This is great if it works, however I hope you realize that this will be the
> ONLY way that any clients with small lock regions will turn on pointer
> lock. Having to update the "activation region" for every widget on every
> layout change is extremely bad, it will send large amounts of data to the
> server for no reason, kind of like old X11 toolkits that sent every widget
> as a subwindow. This was known to be a mistake back in the mid 80's when
> lightweight widgets were added to x intrinsics.

Requesting a lock as a response to a click sounds like a reasonable way
to implement it to me.


Jonas
On 23/08/15 10:19 PM, Jonas Ådahl wrote:
> On Sat, Aug 22, 2015 at 06:49:15PM -0500, Derek Foreman wrote:
>> Hi Jonas,
>>
>> Instead of a reasonable review, I'm going to mostly ask a bunch of noob
>> questions.  Feel free to disregard if you don't have time to enlighten me...
> 
> Thanks for the input. I'll answer inline.
> 
>>
>> On 25/06/15 11:38 PM, Jonas Ådahl wrote:
>>> 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@gmail.com>
>>> ---
>>>
>>> Changes since v2:
>>>
>>>  * Updated copyright.
>>>  * Updated to the fixed license text.
>>>  * Clarified that locks are on made on a certain surface from a given
>>>    seat. The same surface may have many parallel locks; one per seat.
>>>  * Added an error enum.
>>>  * Changed the wording explaining that constraints for activating a lock
>>>    is compositor specific.
>>>  * Clarified that there is no guarantee that a lock will ever be
>>>    activated. No error will be raised if the compositor will never
>>>    activate the lock.
>>>  * Changed wording related to pointer events (make them speak protocol,
>>>    not abstract events).
>>>  * Clarify what happens if a surface is destroyed.
>>>  * Fixed various typos.
>>>  * Implemented support for per-seat locks.
>>>  * Clarified what happens when effective lock/confine regions change.
>>>  * Added wl_locked_pointer.set_region and wl_confined_pointer.set_region
>>>    (see below).
>>>  * Implemented support for above mentioned new requests.
>>>  * Changed the order of requests (put destroy first).
>>>  * Added 'unstable protocol' warning and explained its semantics.
>>>
>>> This version adds support for changing the lock/confine region after the
>>> lock was created.
>>>
>>> This is so that a client can change the lock region without being
>>> exposed to a race condition. Depending on the type of lock
>>> (lock/confine). For exampe, a client who maximizes wants to keep the
>>> pending lock or pending/active confinement intact can use the set_region
>>> request. The region is double buffered, meaning it is synchronized with
>>> the surface input region.
>>>
>>> For wl_locked_pointer, since a changed effective region has no effect on
>>> the lock after it being activated, the set_region request only has effect
>>> before the lock is activated. As such, it affects the region that is used
>>> to activate the lock.
>>>
>>> For pending wl_confined_pointer lock, the effect is the same as for
>>> wl_locked_pointer. But for an already activated confinement, it affects the
>>> pointer, i.e. warps the pointer to somewhere within the region.
>>>
>>>
>>> Jonas
>>>
>>>
>>>  Makefile.am               |   3 +
>>>  protocol/pointer-lock.xml | 275 +++++++++++++++++
>>>  src/compositor.c          |  11 +
>>>  src/compositor.h          |  38 +++
>>>  src/input.c               | 751 ++++++++++++++++++++++++++++++++++++++++++++--
>>>  5 files changed, 1061 insertions(+), 17 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..ee5e274
>>> --- /dev/null
>>> +++ b/protocol/pointer-lock.xml
>>> @@ -0,0 +1,275 @@
>>> +<?xml version="1.0" encoding="UTF-8"?>
>>> +<protocol name="pointer_lock">
>>> +
>>> +  <copyright>
>>> +    Copyright © 2014      Jonas Ådahl
>>> +    Copyright © 2015      Red Hat Inc.
>>> +
>>> +    Permission is hereby granted, free of charge, to any person obtaining a
>>> +    copy of this software and associated documentation files (the "Software"),
>>> +    to deal in the Software without restriction, including without limitation
>>> +    the rights to use, copy, modify, merge, publish, distribute, sublicense,
>>> +    and/or sell copies of the Software, and to permit persons to whom the
>>> +    Software is furnished to do so, subject to the following conditions:
>>> +
>>> +    The above copyright notice and this permission notice (including the next
>>> +    paragraph) shall be included in all copies or substantial portions of the
>>> +    Software.
>>> +
>>> +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>>> +    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>>> +    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>>> +    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>>> +    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>>> +    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>>> +    DEALINGS IN THE 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.
>>
>> Why have both confinement and lock in the same protocol?  It seems like
>> lock is perhaps simpler and could be more easily merged independently.
>>
>> Are they combined in order to enforce the "only one lock/confinement per
>> surface+seat combination" policy?
> 
> Of course we could just wait with confinement, but as you say, the
> belong together, and adding confinement I'd say should just be added in
> the same way as it is now, because they both apply similar types of
> constraints to pointer movements. I've been thinking of renaming
> "wl_pointer_lock" to "wl_pointer_constraint" to make it more clear that
> it's different ways of constraining the pointer.

Fwiw, renaming to wl_pointer_constraint does seem clearer to me - and I
can just say "constraint" instead of "lock/confinement" :)

>>
>> Is this the same interface I'd use for constraining touch input?
> 
> No. Touch input can't really be constrained except with physical
> borders. Are you referring to touch grabs? That's something completely
> different.

Yes, sorry, you're right - I've mixed up the concepts.

How is pointer locking different than a pointer grab?  For the duration
of the grab I'm given motion events even if the cursor leaves my window...

I guess without the lock the pointer would eventually hit the edge of
the output and stop moving (breaking FPS games), but that seems like
it's solved by relative motion protocol and not actually by the lock.

Just wondering out loud why pointer locking is needed at all instead of
just a new way to perform a grab?

>>
>>> +      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.
>>> +
>>> +      For any surface, only one lock or confinement per seat may be active at
>>> +      any time. If a lock or confinement is requested when another lock or
>>> +      confinement is active on that surface and seat, an 'already_locked' error
>>> +      will be raised.
>>
>> Why this limitation?
> 
> Because anything else is simply not possible. While one surface has
> locked the cursor to a position, it is not possible that another surface
> also has the focus of the same pointer. These paragraphs are just
> clarifying reality.

Sorry, the part I didn't get was why "already locked" would be returned
if I tried to set up new lock regions while I already have a lock.  I
don't see why that would/should be impossible while a lock is active?

The lock I'm trying to set up may be intended to trigger later, some
time after the currently active lock has been released and destroyed...

I get that two locks can't trigger at the same time - and I think it's
good to explicitly state this (as you've done) so nobody gets any funny
ideas about what happens if lock regions overlap :)

It may be worth stating in the protocol doc that overlapping lock
regions have implementation defined behavior?  I think it's reasonable
to go so far as to state that in the case of overlap one of the locks
will trigger, but which it is is implementation defined.

>>
>> If I have a surface with a "slow scrollbar" and during its use I need to
>> add another scrollbar (I dunno, slow loading web page?), my app will
>> need to remember that it hasn't set up that new scrollbar's confinement
>> region until after the release of the active scrollbar?
>>
>> (How would I implement a slow scrollbar?  if the compositor has
>> implementation defined semantics for the confinement, I guess I can't?)
> 
> I would need to know exactly what a slow scrollbar is according to you
> before saying how to implement it, but the two ones I can imagine:
> 
> 1. the pointer "attaches" to the UI element.
> 
> One would create a lock region on the scroll handle. When enabled one
> would hide the cursor, create an identical cursor surface and add it as
> a subsurface positioning relative to the scroll handle. One would now
> listen for events from wl_relative_pointer, update the subsurface
> position and the scroll handle widget under it. When done, one would
> unlock the lock, destroy the subsurface, and reset the pointer cursor.
> During the moving, one would set the cursor position hint so when
> resetting the cursor position would be updated.
> 
> This is the only way to guarantee the "attachment" effect. There is one
> unsolved issue which is that the cursor sprite and the subsurface might
> be visible at the same time, and we'd need some kind of "transaction"
> protocol to solve this. There has been ideas to have a "set_cursor_pos"
> but this method would never give us any guarantees about "attachment".

(Thanks, this is a very clear example.)

> 2. the pointer hides and the UI element moves slow
> 
> This would work just the same as above, but without the subsurface.
> 
> 
> The point of the implementation defined semantics are to let the
> compositor implement protection against clients stealing the pointer.
> Example: the compositor uses sloppy focus, a client has a lock/confine
> request, the user moves the pointer over such a window with the
> intention of interacting with a window on the other side. We want to
> enable the compositor to try to avoid the surface in beween to steal the
> pointer, which it would if we specify the protocol to always activate a
> lock when the pointer enters a certain region, or as a guaranteed result
> of a client request.
> 
> Also, we shouldn't IMO specify exactly what semantics is needed (like,
> click to activate etc) because then we'd make it impossible to improve
> this behaviour without updating the protocol without breaking semantical
> backward compatibiliy.

Ok, I get it.  I can't imagine any way to handle sloppy focus + fps game
in a window that won't annoy someone, so I guess it's best to let the
compositor writers be responsible for those bug reports.

>>
>> Should there be a more explicit way for the application to control what
>> causes the activation?  (activate if the user clicks/drags within the
>> region vs activate if the mouse merely enters the region)
> 
> If the client wants to control the activation, it can simply request the
> lock exactly at the point it wants it to be activated. resize.c in
> weston is an example of this - it activates the lock on a pointer button
> event.

I couldn't find a resize.c, but found resizor.c...

It calls window_lock_pointer() from a button handler,
window_lock_pointer() calls _wl_pointer_lock_lock_pointer() with a NULL
region.

Since everything is async, can't the pointer have moved outside of the
window before the client gets around to setting up that lock?  (If I
click while moving the mouse)

The lock will still trigger even though the pointer is outside the input
region at the time the lock is requested?  Should the compositor warp it
back into the client window?

>>
>> For lock in a FPS game, simply focusing the window should probably
>> trigger the lock - so the compositor should warp the pointer there if
>> the window becomes focused by any means, but are there any use cases
>> where such a warp would be incorrect behaviour?
> 
> A VM UI might want this. If one alt-tabs to the VM UI, one might want to
> interact with the controls. For games, maybe, but how would one make it
> possible have window controls? For a fullscreen game it's not an issue
> since alt-tab:ing would most likely make the pointer end up in a place
> so that it'd be inside the region anyway.
> 
>>
>> Presumably I can use meta+tab to focus a different surface and break a
>> pointer lock/confinement.  Will that break the lock/confinement for that
>> surface, or will the lock/confinement be resumed next time that surface
>> obtains focus?
> 
> It'll break. The client will now need to request the lock again.
> 
>>
>> for weston specifically, I wonder what exposay's interaction with all of
>> this should be :)
> 
> I imagine exposay windows should behave as fake previews, not
> locking/confining anything until in "normal" mode. I don't think we need
> to specify this in the protocol.

Definitely a weston implementation issue and not the protocol's problem.
 exposay windows aren't really fake at all though, if you have
multi-seat input devices set up you can happily type away into a
weston-terminal while the other seat triggers exposay.

Under exposay apps still receive motion events.

I figure something there is going to break. :)

We can fix it when it does.

>>
>>> +      Warning! The protocol described in this file is experimental. Each version
>>> +      of this protocol should be considered incompatible with any other version,
>>> +      and a client binding to a version different to the one advertised will be
>>> +      terminated. When the protocol is stabalized, backward compatibility is
>>> +      guaranteed, the '_' prefix will be removed from the name and the version
>>> +      will be reset to 1.
>>> +    </description>
>>> +
>>> +    <enum name="error">
>>> +      <description summary="wl_pointer_lock error values">
>>> +        These errors can be emitted in response to wl_pointer_lock requests.
>>> +      </description>
>>> +      <entry name="already_locked" value="0" summary="pointer was already locked or confined"/>
>>> +    </enum>
>>> +
>>> +    <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. In the future, when the
>>> +        compositor deems implementation specific constraints are satisfied, the
>>> +        pointer lock will be activated and the compositor sends a locked event.
>>
>> I'm very uneasy about this implementation defined behaviour... (comment
>> about FPS game vs other use case above)
> 
> Explained above. This is to make it possible to implement
> anti-pointer-stealing semantics.
> 
>>> +
>>> +        The protocol provides no guarantee that the constraints are ever
>>> +        satisfied, and does not require the compositor to send an error if the
>>> +        constraints cannot ever be satisfied. It is thus possible to request a
>>> +        lock that will never activate.
>>
>> Hmm, this bothered me at first, but since lock/confinement is
>> intersection of input region and lock region, I guess it's possible for
>> an "invalid" lock to become valid at some point after its creation?
> 
> Well, the compositor can have a setting called "no_pointer_locking", and
> all locks will be unactivateable/invalid. That setting might change, and
> they became activatable/valid again.
> 
> Validity due to client input can change as well, for example setting an
> empty input region.
> 
>>
>>> +
>>> +        There may not be another lock of any kind requested or active on the
>>> +        surface for the seat when requesting a lock, and if there is, an error
>>> +        will be raised. See general pointer lock documentation for more details.
>>> +
>>> +        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.
>>> +
>>> +        A surface may receive pointer focus without the lock being activated.
>>> +
>>> +        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 pointer is locked, the wl_pointer objects of the
>>> +        corresponding seat will not emit any motion events, but relative motion
>>> +        events will still be emitted via wl_relative_pointer objects of the
>>> +        same seat.
>>
>> Back to my previous question about touch - should we include wl_touch here?
>>
>> (I think lock is still relevant with touch?  dragging around in an image
>> viewer window perhaps...)
> 
> No. This has nothing to do with touch. Touch can't be constrained.
> 
>>
>>> +      </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 wl_pointer objects of the
>>> +      associated seat will not emit any motion events.
>>> +
>>> +      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 take the cursor position pointer. If
>>> +      it does, it will not result in any relative motion events emitted via
>>> +      wl_relative_motion.
>>> +
>>> +      If the surface lock was requested on is destroyed and the lock is not yet
>>> +      activated, the wl_locked_pointer object is now defunct and must be
>>> +      destroyed.
>>> +    </description>
>>> +
>>> +    <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>
>>> +
>>> +    <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.
>>
>> Is this state double buffered in some way?  Or is it possible that a I
>> send the hint, update my buffer, get closed somehow before the buffer
>> hits this display and things are a little jarring?
> 
> At the current state, no it is not. I guess it could be made to be.

I'll defer to your judgment on whether it's worth the effort.

>>
>> Should this be clipped to the surface to prevent apps from throwing the
>> cursor across the screen on exit?
> 
> Indeed. Its actually clipped to the lock region in the implementation,
> but that might not be good. Surface region makes sense.
> 
>>
>>> +      </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="set_region">
>>> +      <description summary="set a new lock region">
>>> +        Set a new region used to lock the pointer.
>>> +
>>> +        The new lock region is double-buffered. The new lock region will
>>> +        only take effect when the associated surface gets its pending state
>>> +        applied. See wl_surface.commit for details.
>>> +
>>> +        The new region has no effect on a lock that has already been activated.
>>
>> I guess even with double buffered state there's a potential for race
>> here?  Keyboard resize causes the lock to trigger...
> 
> What's a keyboard resize? The point of this is to make it possible to
> update the surface state (input region) and lock state atomically.

I don't remember if weston has keybinds for resizing windows, but
enlightenment does.  But that's unimportant because there really isn't
actually a problem there.  Sorry for the noise.

I was trying to think of a way for the lock to trigger while the app is
in the middle of reconfiguring it - but I as trying too hard, it can
happen any time.  (using a pointer from a second seat to resize an app
under the other cursor is probably a reasonably ugly case)

I guess if an app is actually reconfiguring regions it should check to
make sure it didn't accidentally acquire the lock just before it tried
to reconfigure, or make sure the locked pointer is actually inside the
region it really wanted?

Is it really worthwhile to have set_region instead of expecting the app
to destroy and create a lock with a new region?

>>
>>> +
>>> +        For details about the lock region, see wl_locked_pointer.
>>> +      </description>
>>> +
>>> +      <arg name="region" type="object" interface="wl_region" allow-null="true"
>>> +           summary="region of surface"/>
>>> +    </request>
>>> +
>>> +    <event name="locked">
>>> +      <description summary="lock activation event">
>>> +        Notification that the pointer lock of this seat's pointer is activated.
>>> +      </description>
>>> +    </event>
>>> +
>>> +    <event name="unlocked">
>>> +      <description summary="lock deactivation event">
>>> +        Notification that the pointer lock of seat's pointer is no longer
>>> +        active. This object is now defunct and should be destroyed.
>>> +      </description>
>>
>> Why is the object defunct on unlock?  I guess this is to prevent
>> immediate recapture?
> 
> It is defunct because the client is expected to create a new lock object
> if it wants to lock again

Ok - I just figure a primary use case will be full screen FPS games, and
I figure immediately after the lock is destroyed they're going to create
exactly the same lock over again.

But I guess that's really not a big deal.

> Thanks again for the input.

Thanks for the explanations,
Derek

> 
> Jonas
> 
> 
>>
>> Thanks,
>> Derek
>> (Nothing more below this point)
>>
>>> +    </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 destroyed.
>>> +    </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>
>>> +
>>> +    <request name="set_region">
>>> +      <description summary="set a new confine region">
>>> +        Set a new region used to confine the pointer.
>>> +
>>> +        The new confine region is double-buffered. The new confine region will
>>> +        only take effect when the associated surface gets its pending state
>>> +        applied. See wl_surface.commit for details.
>>> +
>>> +        If the confinement is active when the new confinement region is applied
>>> +        and the pointer ends up outside of newly applied region, the pointer is
>>> +        warped to a position within the new confinement region. If warped, a
>>> +        wl_pointer.motion event will be emitted, but no
>>> +        wl_relative_pointer.relative_motion event.
>>> +
>>> +        For details about the confine region, see wl_confined_pointer.
>>> +      </description>
>>> +
>>> +      <arg name="region" type="object" interface="wl_region" allow-null="true"
>>> +           summary="region of surface"/>
>>> +    </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..296be31 100644
>>> --- a/src/compositor.c
>>> +++ b/src/compositor.c
>>> @@ -641,6 +641,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;
>>> @@ -667,6 +668,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_locks);
>>> +
>>>  	return surface;
>>>  }
>>>  
>>> @@ -1870,6 +1873,7 @@ weston_surface_destroy(struct weston_surface *surface)
>>>  {
>>>  	struct weston_frame_callback *cb, *next;
>>>  	struct weston_view *ev, *nv;
>>> +	struct weston_pointer_lock *pointer_lock, *next_pointer_lock;
>>>  
>>>  	if (--surface->ref_count > 0)
>>>  		return;
>>> @@ -1897,6 +1901,11 @@ weston_surface_destroy(struct weston_surface *surface)
>>>  
>>>  	weston_presentation_feedback_discard_list(&surface->feedback_list);
>>>  
>>> +	wl_list_for_each_safe(pointer_lock, next_pointer_lock,
>>> +			      &surface->pointer_locks,
>>> +			      link)
>>> +		weston_pointer_lock_destroy(pointer_lock);
>>> +
>>>  	free(surface);
>>>  }
>>>  
>>> @@ -2804,6 +2813,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 55cc88f..2322b33 100644
>>> --- a/src/compositor.h
>>> +++ b/src/compositor.h
>>> @@ -63,6 +63,7 @@ struct shell_surface;
>>>  struct weston_seat;
>>>  struct weston_output;
>>>  struct input_method;
>>> +struct weston_pointer_lock;
>>>  
>>>  enum weston_keyboard_modifier {
>>>  	MODIFIER_CTRL = (1 << 0),
>>> @@ -349,6 +350,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;
>>> @@ -421,6 +423,9 @@ void
>>>  weston_pointer_set_default_grab(struct weston_pointer *pointer,
>>>  		const struct weston_pointer_grab_interface *interface);
>>>  
>>> +void
>>> +weston_pointer_lock_destroy(struct weston_pointer_lock *lock);
>>> +
>>>  struct weston_keyboard *
>>>  weston_keyboard_create(void);
>>>  void
>>> @@ -723,6 +728,8 @@ struct weston_compositor {
>>>  	int exit_code;
>>>  
>>>  	unsigned int activate_serial;
>>> +
>>> +	struct wl_global *pointer_lock;
>>>  };
>>>  
>>>  struct weston_buffer {
>>> @@ -916,10 +923,38 @@ 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_lock {
>>> +	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 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;
>>> @@ -999,6 +1034,9 @@ struct weston_surface {
>>>  	const char *role_name;
>>>  
>>>  	struct weston_timeline_object timeline;
>>> +
>>> +	/* An list of per seat pointer locks. */
>>> +	struct wl_list pointer_locks;
>>>  };
>>>  
>>>  struct weston_subsurface {
>>> diff --git a/src/input.c b/src/input.c
>>> index b48a6c9..4a78543 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,8 +35,17 @@
>>>  #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"
>>>  
>>> +enum pointer_lock_type {
>>> +	POINTER_LOCK_TYPE_LOCK,
>>> +	POINTER_LOCK_TYPE_CONFINE,
>>> +};
>>> +
>>> +static void
>>> +maybe_warp_confined_pointer(struct weston_pointer_lock *lock);
>>> +
>>>  static void
>>>  empty_region(pixman_region32_t *region)
>>>  {
>>> @@ -43,6 +53,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 +260,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 +289,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) {
>>> @@ -598,6 +631,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;
>>>  
>>> @@ -616,6 +650,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);
>>>  
>>> @@ -1163,6 +1199,7 @@ weston_surface_activate(struct weston_surface *surface,
>>>  			struct weston_seat *seat)
>>>  {
>>>  	struct weston_compositor *compositor = seat->compositor;
>>> +	struct weston_surface_activation_data activation_data;
>>>  
>>>  	inc_activate_serial(compositor);
>>>  
>>> @@ -1171,7 +1208,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
>>> @@ -2571,6 +2612,677 @@ 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 enum pointer_lock_type
>>> +pointer_lock_get_type(struct weston_pointer_lock *lock)
>>> +{
>>> +	if (wl_resource_instance_of(lock->resource,
>>> +				    &_wl_locked_pointer_interface,
>>> +				    &locked_pointer_interface)) {
>>> +		return POINTER_LOCK_TYPE_LOCK;
>>> +	} else if (wl_resource_instance_of(lock->resource,
>>> +					   &_wl_confined_pointer_interface,
>>> +					   &confined_pointer_interface)) {
>>> +		return POINTER_LOCK_TYPE_CONFINE;
>>> +	}
>>> +
>>> +	abort();
>>> +	return 0;
>>> +}
>>> +
>>> +static void
>>> +pointer_lock_notify_activated(struct weston_pointer_lock *lock)
>>> +{
>>> +	struct wl_resource *resource = lock->resource;
>>> +
>>> +	switch (pointer_lock_get_type(lock)) {
>>> +	case POINTER_LOCK_TYPE_LOCK:
>>> +		_wl_locked_pointer_send_locked(resource);
>>> +		break;
>>> +	case POINTER_LOCK_TYPE_CONFINE:
>>> +		_wl_confined_pointer_send_confined(resource);
>>> +		break;
>>> +	}
>>> +}
>>> +
>>> +static void
>>> +pointer_lock_notify_deactivated(struct weston_pointer_lock *lock)
>>> +{
>>> +	struct wl_resource *resource = lock->resource;
>>> +
>>> +	switch (pointer_lock_get_type(lock)) {
>>> +	case POINTER_LOCK_TYPE_LOCK:
>>> +		_wl_locked_pointer_send_unlocked(resource);
>>> +		break;
>>> +	case POINTER_LOCK_TYPE_CONFINE:
>>> +		_wl_confined_pointer_send_unconfined(resource);
>>> +		break;
>>> +	}
>>> +}
>>> +
>>> +static struct weston_pointer_lock *
>>> +get_pointer_lock_for_pointer(struct weston_surface *surface,
>>> +			     struct weston_pointer *pointer)
>>> +{
>>> +	struct weston_pointer_lock *lock;
>>> +
>>> +	wl_list_for_each(lock, &surface->pointer_locks, link) {
>>> +		if (lock->pointer == pointer)
>>> +			return lock;
>>> +	}
>>> +
>>> +	return NULL;
>>> +}
>>> +
>>> +static void
>>> +enable_pointer_lock(struct weston_pointer_lock *lock,
>>> +		    struct weston_view *view)
>>> +{
>>> +	assert(lock->view == NULL);
>>> +	lock->view = view;
>>> +	pointer_lock_notify_activated(lock);
>>> +	weston_pointer_start_grab(lock->pointer, &lock->grab);
>>> +}
>>> +
>>> +static bool
>>> +is_pointer_lock_enabled(struct weston_pointer_lock *lock)
>>> +{
>>> +	return lock->view != NULL;
>>> +}
>>> +
>>> +void
>>> +weston_pointer_lock_destroy(struct weston_pointer_lock *lock)
>>> +{
>>> +	if (is_pointer_lock_enabled(lock)) {
>>> +		pointer_lock_notify_deactivated(lock);
>>> +		weston_pointer_end_grab(lock->grab.pointer);
>>> +	}
>>> +
>>> +	wl_list_remove(&lock->pointer_destroy_listener.link);
>>> +	wl_list_remove(&lock->surface_destroy_listener.link);
>>> +	wl_list_remove(&lock->surface_commit_listener.link);
>>> +	wl_list_remove(&lock->surface_activate_listener.link);
>>> +
>>> +	wl_resource_set_user_data(lock->resource, NULL);
>>> +	pixman_region32_fini(&lock->region);
>>> +	wl_list_remove(&lock->link);
>>> +	free(lock);
>>> +}
>>> +
>>> +static void
>>> +disable_pointer_lock(struct weston_pointer_lock *lock)
>>> +{
>>> +	weston_pointer_lock_destroy(lock);
>>> +}
>>> +
>>> +static bool
>>> +is_within_lock_region(struct weston_pointer_lock *lock,
>>> +		      wl_fixed_t sx, wl_fixed_t sy)
>>> +{
>>> +	struct weston_surface *surface = lock->surface;
>>> +	pixman_region32_t lock_region;
>>> +	bool result;
>>> +
>>> +	pixman_region32_init(&lock_region);
>>> +	pixman_region32_intersect(&lock_region, &surface->input, &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_pointer_lock *lock)
>>> +{
>>> +	struct weston_surface *surface = lock->surface;
>>> +	struct weston_view *vit;
>>> +	struct weston_view *view = NULL;
>>> +	struct weston_pointer *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(lock,
>>> +				   wl_fixed_from_int(x),
>>> +				   wl_fixed_from_int(y)))
>>> +		return;
>>> +
>>> +	enable_pointer_lock(lock, 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_lock *lock =
>>> +		container_of(grab, struct weston_pointer_lock, grab);
>>> +
>>> +	disable_pointer_lock(lock);
>>> +}
>>> +
>>> +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_pointer_lock *lock = wl_resource_get_user_data(resource);
>>> +
>>> +	if (!lock)
>>> +		return;
>>> +
>>> +	disable_pointer_lock(lock);
>>> +}
>>> +
>>> +static void
>>> +pointer_lock_surface_activate(struct wl_listener *listener, void *data)
>>> +{
>>> +	struct weston_surface_activation_data *activation = data;
>>> +	struct weston_pointer *pointer = activation->seat->pointer;
>>> +	struct weston_surface *focus = activation->surface;
>>> +	struct weston_pointer_lock *lock =
>>> +		container_of(listener, struct weston_pointer_lock,
>>> +			     surface_activate_listener);
>>> +	bool is_lock_surface;
>>> +
>>> +	is_lock_surface = get_pointer_lock_for_pointer(focus, pointer) == lock;
>>> +
>>> +	if (is_lock_surface &&  !is_pointer_lock_enabled(lock))
>>> +		maybe_enable_pointer_lock(lock);
>>> +	else if (!is_lock_surface && is_pointer_lock_enabled(lock))
>>> +		disable_pointer_lock(lock);
>>> +}
>>> +
>>> +static void
>>> +pointer_lock_pointer_destroyed(struct wl_listener *listener, void *data)
>>> +{
>>> +	struct weston_pointer_lock *lock =
>>> +		container_of(listener, struct weston_pointer_lock,
>>> +			     pointer_destroy_listener);
>>> +
>>> +	disable_pointer_lock(lock);
>>> +}
>>> +
>>> +static void
>>> +pointer_lock_surface_destroyed(struct wl_listener *listener, void *data)
>>> +{
>>> +	struct weston_pointer_lock *lock =
>>> +		container_of(listener, struct weston_pointer_lock,
>>> +			     surface_destroy_listener);
>>> +
>>> +	disable_pointer_lock(lock);
>>> +}
>>> +
>>> +static void
>>> +pointer_lock_surface_committed(struct wl_listener *listener, void *data)
>>> +{
>>> +	struct weston_pointer_lock *lock =
>>> +		container_of(listener, struct weston_pointer_lock,
>>> +			     surface_commit_listener);
>>> +
>>> +	if (lock->pending_region_set) {
>>> +		lock->pending_region_set = false;
>>> +		pixman_region32_copy(&lock->region, &lock->pending_region);
>>> +		pixman_region32_fini(&lock->pending_region);
>>> +		pixman_region32_init(&lock->pending_region);
>>> +	}
>>> +
>>> +	if (pointer_lock_get_type(lock) == POINTER_LOCK_TYPE_CONFINE &&
>>> +	    is_pointer_lock_enabled(lock))
>>> +		maybe_warp_confined_pointer(lock);
>>> +}
>>> +
>>> +static struct weston_pointer_lock *
>>> +weston_pointer_lock_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_lock *lock;
>>> +
>>> +	lock = zalloc(sizeof *lock);
>>> +	if (!lock)
>>> +		return NULL;
>>> +
>>> +	pixman_region32_init(&lock->region);
>>> +	pixman_region32_init(&lock->pending_region);
>>> +	wl_list_insert(&surface->pointer_locks, &lock->link);
>>> +	lock->surface = surface;
>>> +	lock->pointer = pointer;
>>> +	lock->resource = cr;
>>> +	lock->grab.interface = grab_interface;
>>> +	if (region) {
>>> +		pixman_region32_copy(&lock->region,
>>> +				     &region->region);
>>> +	} else {
>>> +		pixman_region32_fini(&lock->region);
>>> +		region_init_infinite(&lock->region);
>>> +	}
>>> +
>>> +	lock->surface_activate_listener.notify = pointer_lock_surface_activate;
>>> +	lock->surface_destroy_listener.notify = pointer_lock_surface_destroyed;
>>> +	lock->surface_commit_listener.notify = pointer_lock_surface_committed;
>>> +	lock->pointer_destroy_listener.notify = pointer_lock_pointer_destroyed;
>>> +
>>> +	wl_signal_add(&surface->compositor->activate_signal,
>>> +		      &lock->surface_activate_listener);
>>> +	wl_signal_add(&pointer->destroy_signal,
>>> +		      &lock->pointer_destroy_listener);
>>> +	wl_signal_add(&surface->destroy_signal,
>>> +		      &lock->surface_destroy_listener);
>>> +	wl_signal_add(&surface->commit_signal,
>>> +		      &lock->surface_commit_listener);
>>> +
>>> +	return lock;
>>> +}
>>> +
>>> +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;
>>> +	struct weston_pointer_lock *lock;
>>> +
>>> +	if (get_pointer_lock_for_pointer(surface, pointer)) {
>>> +		wl_resource_post_error(pointer_lock_resource,
>>> +				       WL_DISPLAY_ERROR_INVALID_OBJECT,
>>> +				       "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_lock_resource),
>>> +				id);
>>> +	if (cr == NULL) {
>>> +		wl_client_post_no_memory(client);
>>> +		return;
>>> +	}
>>> +
>>> +	lock = weston_pointer_lock_create(surface, pointer, region,
>>> +					  cr, grab_interface);
>>> +	if (lock == NULL) {
>>> +		wl_client_post_no_memory(client);
>>> +		return;
>>> +	}
>>> +
>>> +	wl_resource_set_implementation(cr, implementation, lock,
>>> +				       pointer_lock_lock_resource_destroyed);
>>> +
>>> +	maybe_enable_pointer_lock(lock);
>>> +}
>>> +
>>> +static void
>>> +locked_pointer_destroy(struct wl_client *client,
>>> +		       struct wl_resource *resource)
>>> +{
>>> +	struct weston_pointer_lock *lock = wl_resource_get_user_data(resource);
>>> +	wl_fixed_t x_hint = lock->x_hint;
>>> +	wl_fixed_t y_hint = lock->y_hint;
>>> +	wl_fixed_t x, y;
>>> +
>>> +	if (lock->view && lock->hint_set &&
>>> +	    is_within_lock_region(lock, x_hint, y_hint)) {
>>> +		weston_view_to_global_fixed(lock->view,
>>> +					    x_hint, y_hint,
>>> +					    &x, &y);
>>> +		weston_pointer_move_to(lock->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_lock *lock = wl_resource_get_user_data(resource);
>>> +
>>> +	/* Ignore a set cursor hint that was already sent after the lock
>>> +	 * was cancelled. */
>>> +	if (!lock->resource ||
>>> +	    lock->resource != resource)
>>> +		return;
>>> +
>>> +	lock->hint_set = true;
>>> +	lock->x_hint = surface_x;
>>> +	lock->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_lock *lock = 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(&lock->pending_region, &region->region);
>>> +	} else {
>>> +		pixman_region32_fini(&lock->pending_region);
>>> +		region_init_infinite(&lock->pending_region);
>>> +	}
>>> +	lock->pending_region_set = true;
>>> +}
>>> +
>>> +
>>> +static const struct _wl_locked_pointer_interface locked_pointer_interface = {
>>> +	locked_pointer_destroy,
>>> +	locked_pointer_set_cursor_position_hint,
>>> +	locked_pointer_set_region,
>>> +};
>>> +
>>> +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
>>> +maybe_warp_confined_pointer(struct weston_pointer_lock *lock)
>>> +{
>>> +	wl_fixed_t x;
>>> +	wl_fixed_t y;
>>> +	wl_fixed_t sx;
>>> +	wl_fixed_t sy;
>>> +
>>> +	weston_view_from_global_fixed(lock->view,
>>> +				      lock->pointer->x,
>>> +				      lock->pointer->y,
>>> +				      &sx,
>>> +				      &sy);
>>> +
>>> +	if (!is_within_lock_region(lock, sx, sy)) {
>>> +		pixman_region32_t *region = &lock->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(lock->view, sx, sy, &x, &y);
>>> +		weston_pointer_move_to(lock->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_lock *lock =
>>> +		container_of(grab, struct weston_pointer_lock, 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 == lock->surface);
>>> +
>>> +	surface = pointer->focus->surface;
>>> +
>>> +	pixman_region32_init(&confine_region);
>>> +	pixman_region32_intersect(&confine_region,
>>> +				  &surface->input,
>>> +				  &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_pointer_lock *lock =
>>> +		container_of(grab, struct weston_pointer_lock, grab);
>>> +
>>> +	disable_pointer_lock(lock);
>>> +}
>>> +
>>> +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_lock *lock = 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(&lock->pending_region, &region->region);
>>> +	} else {
>>> +		pixman_region32_fini(&lock->pending_region);
>>> +		region_init_infinite(&lock->pending_region);
>>> +	}
>>> +	lock->pending_region_set = true;
>>> +}
>>> +
>>> +static const struct _wl_confined_pointer_interface confined_pointer_interface = {
>>> +	confined_pointer_destroy,
>>> +	confined_pointer_set_region,
>>> +};
>>> +
>>> +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)
>>>  {
>>> @@ -2579,5 +3291,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;
>>>  }
>>>
>>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
>
On Wed, Aug 26, 2015 at 04:14:05PM -0500, Derek Foreman wrote:
> On 23/08/15 10:19 PM, Jonas Ådahl wrote:
> > On Sat, Aug 22, 2015 at 06:49:15PM -0500, Derek Foreman wrote:
> >> Hi Jonas,
> >>
> >> Instead of a reasonable review, I'm going to mostly ask a bunch of noob
> >> questions.  Feel free to disregard if you don't have time to enlighten me...
> > 
> > Thanks for the input. I'll answer inline.
> > 
> >>
> >> On 25/06/15 11:38 PM, Jonas Ådahl wrote:
> >>> 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@gmail.com>
> >>> ---
> >>>
> >>> Changes since v2:
> >>>
> >>>  * Updated copyright.
> >>>  * Updated to the fixed license text.
> >>>  * Clarified that locks are on made on a certain surface from a given
> >>>    seat. The same surface may have many parallel locks; one per seat.
> >>>  * Added an error enum.
> >>>  * Changed the wording explaining that constraints for activating a lock
> >>>    is compositor specific.
> >>>  * Clarified that there is no guarantee that a lock will ever be
> >>>    activated. No error will be raised if the compositor will never
> >>>    activate the lock.
> >>>  * Changed wording related to pointer events (make them speak protocol,
> >>>    not abstract events).
> >>>  * Clarify what happens if a surface is destroyed.
> >>>  * Fixed various typos.
> >>>  * Implemented support for per-seat locks.
> >>>  * Clarified what happens when effective lock/confine regions change.
> >>>  * Added wl_locked_pointer.set_region and wl_confined_pointer.set_region
> >>>    (see below).
> >>>  * Implemented support for above mentioned new requests.
> >>>  * Changed the order of requests (put destroy first).
> >>>  * Added 'unstable protocol' warning and explained its semantics.
> >>>
> >>> This version adds support for changing the lock/confine region after the
> >>> lock was created.
> >>>
> >>> This is so that a client can change the lock region without being
> >>> exposed to a race condition. Depending on the type of lock
> >>> (lock/confine). For exampe, a client who maximizes wants to keep the
> >>> pending lock or pending/active confinement intact can use the set_region
> >>> request. The region is double buffered, meaning it is synchronized with
> >>> the surface input region.
> >>>
> >>> For wl_locked_pointer, since a changed effective region has no effect on
> >>> the lock after it being activated, the set_region request only has effect
> >>> before the lock is activated. As such, it affects the region that is used
> >>> to activate the lock.
> >>>
> >>> For pending wl_confined_pointer lock, the effect is the same as for
> >>> wl_locked_pointer. But for an already activated confinement, it affects the
> >>> pointer, i.e. warps the pointer to somewhere within the region.
> >>>
> >>>
> >>> Jonas
> >>>
> >>>
> >>>  Makefile.am               |   3 +
> >>>  protocol/pointer-lock.xml | 275 +++++++++++++++++
> >>>  src/compositor.c          |  11 +
> >>>  src/compositor.h          |  38 +++
> >>>  src/input.c               | 751 ++++++++++++++++++++++++++++++++++++++++++++--
> >>>  5 files changed, 1061 insertions(+), 17 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..ee5e274
> >>> --- /dev/null
> >>> +++ b/protocol/pointer-lock.xml
> >>> @@ -0,0 +1,275 @@
> >>> +<?xml version="1.0" encoding="UTF-8"?>
> >>> +<protocol name="pointer_lock">
> >>> +
> >>> +  <copyright>
> >>> +    Copyright © 2014      Jonas Ådahl
> >>> +    Copyright © 2015      Red Hat Inc.
> >>> +
> >>> +    Permission is hereby granted, free of charge, to any person obtaining a
> >>> +    copy of this software and associated documentation files (the "Software"),
> >>> +    to deal in the Software without restriction, including without limitation
> >>> +    the rights to use, copy, modify, merge, publish, distribute, sublicense,
> >>> +    and/or sell copies of the Software, and to permit persons to whom the
> >>> +    Software is furnished to do so, subject to the following conditions:
> >>> +
> >>> +    The above copyright notice and this permission notice (including the next
> >>> +    paragraph) shall be included in all copies or substantial portions of the
> >>> +    Software.
> >>> +
> >>> +    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> >>> +    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> >>> +    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> >>> +    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> >>> +    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> >>> +    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> >>> +    DEALINGS IN THE 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.
> >>
> >> Why have both confinement and lock in the same protocol?  It seems like
> >> lock is perhaps simpler and could be more easily merged independently.
> >>
> >> Are they combined in order to enforce the "only one lock/confinement per
> >> surface+seat combination" policy?
> > 
> > Of course we could just wait with confinement, but as you say, the
> > belong together, and adding confinement I'd say should just be added in
> > the same way as it is now, because they both apply similar types of
> > constraints to pointer movements. I've been thinking of renaming
> > "wl_pointer_lock" to "wl_pointer_constraint" to make it more clear that
> > it's different ways of constraining the pointer.
> 
> Fwiw, renaming to wl_pointer_constraint does seem clearer to me - and I
> can just say "constraint" instead of "lock/confinement" :)
> 
> >>
> >> Is this the same interface I'd use for constraining touch input?
> > 
> > No. Touch input can't really be constrained except with physical
> > borders. Are you referring to touch grabs? That's something completely
> > different.
> 
> Yes, sorry, you're right - I've mixed up the concepts.
> 
> How is pointer locking different than a pointer grab?  For the duration
> of the grab I'm given motion events even if the cursor leaves my window...

A grab does not effect the pointer motions, just the focus. A constraint
has a direct effect on the actual motions (i.e. constraining it to a
region/point). One could say that it is an special type of explicitly
requested grab with certain side effects, but what would the point be of
making "grab" so broadly defined?

> 
> I guess without the lock the pointer would eventually hit the edge of
> the output and stop moving (breaking FPS games), but that seems like
> it's solved by relative motion protocol and not actually by the lock.

It would hit the edge of one output, and maybe continue to the next, or
not. It's not really specified what happens, and probably good that it
is not. I don't think its good to rely on anything like it for not
loosing the pointer focus.

> 
> Just wondering out loud why pointer locking is needed at all instead of
> just a new way to perform a grab?

Consider the implications of just doing a grab with no locking. The
pointer would effectively travel around on the screen semi randomly.
When broken, either by the client or by the compositor, the pointer
would be placed in a completely random place on some random output. I
don't think thats very nice. It would also mean a client could take the
grab, then just make the desktop appear not taking any input. It doesn't
seem like something that would be a good thing to support. Generally
speaking, the interfaces in Wayland has been thought to be designed to
fix a use case, not as a low level building block for all use cases.
We don't give out global positions, or let the client stack itself
however it wants, but we allow it to hint its intention and then
implement the behavior the way it should be in that environment.

> 
> >>
> >>> +      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.
> >>> +
> >>> +      For any surface, only one lock or confinement per seat may be active at
> >>> +      any time. If a lock or confinement is requested when another lock or
> >>> +      confinement is active on that surface and seat, an 'already_locked' error
> >>> +      will be raised.
> >>
> >> Why this limitation?
> > 
> > Because anything else is simply not possible. While one surface has
> > locked the cursor to a position, it is not possible that another surface
> > also has the focus of the same pointer. These paragraphs are just
> > clarifying reality.
> 
> Sorry, the part I didn't get was why "already locked" would be returned
> if I tried to set up new lock regions while I already have a lock.  I
> don't see why that would/should be impossible while a lock is active?

If the client wants to change the lock, it can do so with the set_region
request. Re-creating the lock is racy, but can be done by destroying and
recreating.

> 
> The lock I'm trying to set up may be intended to trigger later, some
> time after the currently active lock has been released and destroyed...
> 
> I get that two locks can't trigger at the same time - and I think it's
> good to explicitly state this (as you've done) so nobody gets any funny
> ideas about what happens if lock regions overlap :)
> 
> It may be worth stating in the protocol doc that overlapping lock
> regions have implementation defined behavior?  I think it's reasonable
> to go so far as to state that in the case of overlap one of the locks
> will trigger, but which it is is implementation defined.

Well, one can only request one lock per surface, per seat. Overlapping
regions would not ambiguous because for a given seat, it'd look like
there is only one lock. So there is no overlapping regions really. If
one wants to activate a lock under special circumstances, one should
request the lock when it should be activated.

> 
> >>
> >> If I have a surface with a "slow scrollbar" and during its use I need to
> >> add another scrollbar (I dunno, slow loading web page?), my app will
> >> need to remember that it hasn't set up that new scrollbar's confinement
> >> region until after the release of the active scrollbar?
> >>
> >> (How would I implement a slow scrollbar?  if the compositor has
> >> implementation defined semantics for the confinement, I guess I can't?)
> > 
> > I would need to know exactly what a slow scrollbar is according to you
> > before saying how to implement it, but the two ones I can imagine:
> > 
> > 1. the pointer "attaches" to the UI element.
> > 
> > One would create a lock region on the scroll handle. When enabled one
> > would hide the cursor, create an identical cursor surface and add it as
> > a subsurface positioning relative to the scroll handle. One would now
> > listen for events from wl_relative_pointer, update the subsurface
> > position and the scroll handle widget under it. When done, one would
> > unlock the lock, destroy the subsurface, and reset the pointer cursor.
> > During the moving, one would set the cursor position hint so when
> > resetting the cursor position would be updated.
> > 
> > This is the only way to guarantee the "attachment" effect. There is one
> > unsolved issue which is that the cursor sprite and the subsurface might
> > be visible at the same time, and we'd need some kind of "transaction"
> > protocol to solve this. There has been ideas to have a "set_cursor_pos"
> > but this method would never give us any guarantees about "attachment".
> 
> (Thanks, this is a very clear example.)
> 
> > 2. the pointer hides and the UI element moves slow
> > 
> > This would work just the same as above, but without the subsurface.
> > 
> > 
> > The point of the implementation defined semantics are to let the
> > compositor implement protection against clients stealing the pointer.
> > Example: the compositor uses sloppy focus, a client has a lock/confine
> > request, the user moves the pointer over such a window with the
> > intention of interacting with a window on the other side. We want to
> > enable the compositor to try to avoid the surface in beween to steal the
> > pointer, which it would if we specify the protocol to always activate a
> > lock when the pointer enters a certain region, or as a guaranteed result
> > of a client request.
> > 
> > Also, we shouldn't IMO specify exactly what semantics is needed (like,
> > click to activate etc) because then we'd make it impossible to improve
> > this behaviour without updating the protocol without breaking semantical
> > backward compatibiliy.
> 
> Ok, I get it.  I can't imagine any way to handle sloppy focus + fps game
> in a window that won't annoy someone, so I guess it's best to let the
> compositor writers be responsible for those bug reports.
> 
> >>
> >> Should there be a more explicit way for the application to control what
> >> causes the activation?  (activate if the user clicks/drags within the
> >> region vs activate if the mouse merely enters the region)
> > 
> > If the client wants to control the activation, it can simply request the
> > lock exactly at the point it wants it to be activated. resize.c in
> > weston is an example of this - it activates the lock on a pointer button
> > event.
> 
> I couldn't find a resize.c, but found resizor.c...
> 
> It calls window_lock_pointer() from a button handler,
> window_lock_pointer() calls _wl_pointer_lock_lock_pointer() with a NULL
> region.
> 
> Since everything is async, can't the pointer have moved outside of the
> window before the client gets around to setting up that lock?  (If I
> click while moving the mouse)
> 
> The lock will still trigger even though the pointer is outside the input
> region at the time the lock is requested?  Should the compositor warp it
> back into the client window?

I suppose a compositor could allow to lock when there is a grab and warp
if it wants. It'd be under the "compositor specific implementation"
umbrella.

> 
> >>
> >> For lock in a FPS game, simply focusing the window should probably
> >> trigger the lock - so the compositor should warp the pointer there if
> >> the window becomes focused by any means, but are there any use cases
> >> where such a warp would be incorrect behaviour?
> > 
> > A VM UI might want this. If one alt-tabs to the VM UI, one might want to
> > interact with the controls. For games, maybe, but how would one make it
> > possible have window controls? For a fullscreen game it's not an issue
> > since alt-tab:ing would most likely make the pointer end up in a place
> > so that it'd be inside the region anyway.
> > 
> >>
> >> Presumably I can use meta+tab to focus a different surface and break a
> >> pointer lock/confinement.  Will that break the lock/confinement for that
> >> surface, or will the lock/confinement be resumed next time that surface
> >> obtains focus?
> > 
> > It'll break. The client will now need to request the lock again.
> > 
> >>
> >> for weston specifically, I wonder what exposay's interaction with all of
> >> this should be :)
> > 
> > I imagine exposay windows should behave as fake previews, not
> > locking/confining anything until in "normal" mode. I don't think we need
> > to specify this in the protocol.
> 
> Definitely a weston implementation issue and not the protocol's problem.
>  exposay windows aren't really fake at all though, if you have
> multi-seat input devices set up you can happily type away into a
> weston-terminal while the other seat triggers exposay.
> 
> Under exposay apps still receive motion events.
> 
> I figure something there is going to break. :)
> 
> We can fix it when it does.

Indeed. Such details is not relevant to the protocol really.


Jonas

> 
> >>
> >>> +      Warning! The protocol described in this file is experimental. Each version
> >>> +      of this protocol should be considered incompatible with any other version,
> >>> +      and a client binding to a version different to the one advertised will be
> >>> +      terminated. When the protocol is stabalized, backward compatibility is
> >>> +      guaranteed, the '_' prefix will be removed from the name and the version
> >>> +      will be reset to 1.
> >>> +    </description>
> >>> +
> >>> +    <enum name="error">
> >>> +      <description summary="wl_pointer_lock error values">
> >>> +        These errors can be emitted in response to wl_pointer_lock requests.
> >>> +      </description>
> >>> +      <entry name="already_locked" value="0" summary="pointer was already locked or confined"/>
> >>> +    </enum>
> >>> +
> >>> +    <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. In the future, when the
> >>> +        compositor deems implementation specific constraints are satisfied, the
> >>> +        pointer lock will be activated and the compositor sends a locked event.
> >>
> >> I'm very uneasy about this implementation defined behaviour... (comment
> >> about FPS game vs other use case above)
> > 
> > Explained above. This is to make it possible to implement
> > anti-pointer-stealing semantics.
> > 
> >>> +
> >>> +        The protocol provides no guarantee that the constraints are ever
> >>> +        satisfied, and does not require the compositor to send an error if the
> >>> +        constraints cannot ever be satisfied. It is thus possible to request a
> >>> +        lock that will never activate.
> >>
> >> Hmm, this bothered me at first, but since lock/confinement is
> >> intersection of input region and lock region, I guess it's possible for
> >> an "invalid" lock to become valid at some point after its creation?
> > 
> > Well, the compositor can have a setting called "no_pointer_locking", and
> > all locks will be unactivateable/invalid. That setting might change, and
> > they became activatable/valid again.
> > 
> > Validity due to client input can change as well, for example setting an
> > empty input region.
> > 
> >>
> >>> +
> >>> +        There may not be another lock of any kind requested or active on the
> >>> +        surface for the seat when requesting a lock, and if there is, an error
> >>> +        will be raised. See general pointer lock documentation for more details.
> >>> +
> >>> +        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.
> >>> +
> >>> +        A surface may receive pointer focus without the lock being activated.
> >>> +
> >>> +        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 pointer is locked, the wl_pointer objects of the
> >>> +        corresponding seat will not emit any motion events, but relative motion
> >>> +        events will still be emitted via wl_relative_pointer objects of the
> >>> +        same seat.
> >>
> >> Back to my previous question about touch - should we include wl_touch here?
> >>
> >> (I think lock is still relevant with touch?  dragging around in an image
> >> viewer window perhaps...)
> > 
> > No. This has nothing to do with touch. Touch can't be constrained.
> > 
> >>
> >>> +      </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 wl_pointer objects of the
> >>> +      associated seat will not emit any motion events.
> >>> +
> >>> +      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 take the cursor position pointer. If
> >>> +      it does, it will not result in any relative motion events emitted via
> >>> +      wl_relative_motion.
> >>> +
> >>> +      If the surface lock was requested on is destroyed and the lock is not yet
> >>> +      activated, the wl_locked_pointer object is now defunct and must be
> >>> +      destroyed.
> >>> +    </description>
> >>> +
> >>> +    <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>
> >>> +
> >>> +    <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.
> >>
> >> Is this state double buffered in some way?  Or is it possible that a I
> >> send the hint, update my buffer, get closed somehow before the buffer
> >> hits this display and things are a little jarring?
> > 
> > At the current state, no it is not. I guess it could be made to be.
> 
> I'll defer to your judgment on whether it's worth the effort.
> 
> >>
> >> Should this be clipped to the surface to prevent apps from throwing the
> >> cursor across the screen on exit?
> > 
> > Indeed. Its actually clipped to the lock region in the implementation,
> > but that might not be good. Surface region makes sense.
> > 
> >>
> >>> +      </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="set_region">
> >>> +      <description summary="set a new lock region">
> >>> +        Set a new region used to lock the pointer.
> >>> +
> >>> +        The new lock region is double-buffered. The new lock region will
> >>> +        only take effect when the associated surface gets its pending state
> >>> +        applied. See wl_surface.commit for details.
> >>> +
> >>> +        The new region has no effect on a lock that has already been activated.
> >>
> >> I guess even with double buffered state there's a potential for race
> >> here?  Keyboard resize causes the lock to trigger...
> > 
> > What's a keyboard resize? The point of this is to make it possible to
> > update the surface state (input region) and lock state atomically.
> 
> I don't remember if weston has keybinds for resizing windows, but
> enlightenment does.  But that's unimportant because there really isn't
> actually a problem there.  Sorry for the noise.
> 
> I was trying to think of a way for the lock to trigger while the app is
> in the middle of reconfiguring it - but I as trying too hard, it can
> happen any time.  (using a pointer from a second seat to resize an app
> under the other cursor is probably a reasonably ugly case)
> 
> I guess if an app is actually reconfiguring regions it should check to
> make sure it didn't accidentally acquire the lock just before it tried
> to reconfigure, or make sure the locked pointer is actually inside the
> region it really wanted?
> 
> Is it really worthwhile to have set_region instead of expecting the app
> to destroy and create a lock with a new region?

As mentioned above, destroy->create is racy.

> 
> >>
> >>> +
> >>> +        For details about the lock region, see wl_locked_pointer.
> >>> +      </description>
> >>> +
> >>> +      <arg name="region" type="object" interface="wl_region" allow-null="true"
> >>> +           summary="region of surface"/>
> >>> +    </request>
> >>> +
> >>> +    <event name="locked">
> >>> +      <description summary="lock activation event">
> >>> +        Notification that the pointer lock of this seat's pointer is activated.
> >>> +      </description>
> >>> +    </event>
> >>> +
> >>> +    <event name="unlocked">
> >>> +      <description summary="lock deactivation event">
> >>> +        Notification that the pointer lock of seat's pointer is no longer
> >>> +        active. This object is now defunct and should be destroyed.
> >>> +      </description>
> >>
> >> Why is the object defunct on unlock?  I guess this is to prevent
> >> immediate recapture?
> > 
> > It is defunct because the client is expected to create a new lock object
> > if it wants to lock again
> 
> Ok - I just figure a primary use case will be full screen FPS games, and
> I figure immediately after the lock is destroyed they're going to create
> exactly the same lock over again.
> 
> But I guess that's really not a big deal.
> 
> > Thanks again for the input.
> 
> Thanks for the explanations,
> Derek
> 
> > 
> > Jonas
> > 
> > 
> >>
> >> Thanks,
> >> Derek
> >> (Nothing more below this point)
> >>
> >>> +    </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 destroyed.
> >>> +    </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>
> >>> +
> >>> +    <request name="set_region">
> >>> +      <description summary="set a new confine region">
> >>> +        Set a new region used to confine the pointer.
> >>> +
> >>> +        The new confine region is double-buffered. The new confine region will
> >>> +        only take effect when the associated surface gets its pending state
> >>> +        applied. See wl_surface.commit for details.
> >>> +
> >>> +        If the confinement is active when the new confinement region is applied
> >>> +        and the pointer ends up outside of newly applied region, the pointer is
> >>> +        warped to a position within the new confinement region. If warped, a
> >>> +        wl_pointer.motion event will be emitted, but no
> >>> +        wl_relative_pointer.relative_motion event.
> >>> +
> >>> +        For details about the confine region, see wl_confined_pointer.
> >>> +      </description>
> >>> +
> >>> +      <arg name="region" type="object" interface="wl_region" allow-null="true"
> >>> +           summary="region of surface"/>
> >>> +    </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..296be31 100644
> >>> --- a/src/compositor.c
> >>> +++ b/src/compositor.c
> >>> @@ -641,6 +641,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;
> >>> @@ -667,6 +668,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_locks);
> >>> +
> >>>  	return surface;
> >>>  }
> >>>  
> >>> @@ -1870,6 +1873,7 @@ weston_surface_destroy(struct weston_surface *surface)
> >>>  {
> >>>  	struct weston_frame_callback *cb, *next;
> >>>  	struct weston_view *ev, *nv;
> >>> +	struct weston_pointer_lock *pointer_lock, *next_pointer_lock;
> >>>  
> >>>  	if (--surface->ref_count > 0)
> >>>  		return;
> >>> @@ -1897,6 +1901,11 @@ weston_surface_destroy(struct weston_surface *surface)
> >>>  
> >>>  	weston_presentation_feedback_discard_list(&surface->feedback_list);
> >>>  
> >>> +	wl_list_for_each_safe(pointer_lock, next_pointer_lock,
> >>> +			      &surface->pointer_locks,
> >>> +			      link)
> >>> +		weston_pointer_lock_destroy(pointer_lock);
> >>> +
> >>>  	free(surface);
> >>>  }
> >>>  
> >>> @@ -2804,6 +2813,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 55cc88f..2322b33 100644
> >>> --- a/src/compositor.h
> >>> +++ b/src/compositor.h
> >>> @@ -63,6 +63,7 @@ struct shell_surface;
> >>>  struct weston_seat;
> >>>  struct weston_output;
> >>>  struct input_method;
> >>> +struct weston_pointer_lock;
> >>>  
> >>>  enum weston_keyboard_modifier {
> >>>  	MODIFIER_CTRL = (1 << 0),
> >>> @@ -349,6 +350,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;
> >>> @@ -421,6 +423,9 @@ void
> >>>  weston_pointer_set_default_grab(struct weston_pointer *pointer,
> >>>  		const struct weston_pointer_grab_interface *interface);
> >>>  
> >>> +void
> >>> +weston_pointer_lock_destroy(struct weston_pointer_lock *lock);
> >>> +
> >>>  struct weston_keyboard *
> >>>  weston_keyboard_create(void);
> >>>  void
> >>> @@ -723,6 +728,8 @@ struct weston_compositor {
> >>>  	int exit_code;
> >>>  
> >>>  	unsigned int activate_serial;
> >>> +
> >>> +	struct wl_global *pointer_lock;
> >>>  };
> >>>  
> >>>  struct weston_buffer {
> >>> @@ -916,10 +923,38 @@ 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_lock {
> >>> +	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 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;
> >>> @@ -999,6 +1034,9 @@ struct weston_surface {
> >>>  	const char *role_name;
> >>>  
> >>>  	struct weston_timeline_object timeline;
> >>> +
> >>> +	/* An list of per seat pointer locks. */
> >>> +	struct wl_list pointer_locks;
> >>>  };
> >>>  
> >>>  struct weston_subsurface {
> >>> diff --git a/src/input.c b/src/input.c
> >>> index b48a6c9..4a78543 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,8 +35,17 @@
> >>>  #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"
> >>>  
> >>> +enum pointer_lock_type {
> >>> +	POINTER_LOCK_TYPE_LOCK,
> >>> +	POINTER_LOCK_TYPE_CONFINE,
> >>> +};
> >>> +
> >>> +static void
> >>> +maybe_warp_confined_pointer(struct weston_pointer_lock *lock);
> >>> +
> >>>  static void
> >>>  empty_region(pixman_region32_t *region)
> >>>  {
> >>> @@ -43,6 +53,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 +260,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 +289,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) {
> >>> @@ -598,6 +631,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;
> >>>  
> >>> @@ -616,6 +650,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);
> >>>  
> >>> @@ -1163,6 +1199,7 @@ weston_surface_activate(struct weston_surface *surface,
> >>>  			struct weston_seat *seat)
> >>>  {
> >>>  	struct weston_compositor *compositor = seat->compositor;
> >>> +	struct weston_surface_activation_data activation_data;
> >>>  
> >>>  	inc_activate_serial(compositor);
> >>>  
> >>> @@ -1171,7 +1208,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
> >>> @@ -2571,6 +2612,677 @@ 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 enum pointer_lock_type
> >>> +pointer_lock_get_type(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	if (wl_resource_instance_of(lock->resource,
> >>> +				    &_wl_locked_pointer_interface,
> >>> +				    &locked_pointer_interface)) {
> >>> +		return POINTER_LOCK_TYPE_LOCK;
> >>> +	} else if (wl_resource_instance_of(lock->resource,
> >>> +					   &_wl_confined_pointer_interface,
> >>> +					   &confined_pointer_interface)) {
> >>> +		return POINTER_LOCK_TYPE_CONFINE;
> >>> +	}
> >>> +
> >>> +	abort();
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static void
> >>> +pointer_lock_notify_activated(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	struct wl_resource *resource = lock->resource;
> >>> +
> >>> +	switch (pointer_lock_get_type(lock)) {
> >>> +	case POINTER_LOCK_TYPE_LOCK:
> >>> +		_wl_locked_pointer_send_locked(resource);
> >>> +		break;
> >>> +	case POINTER_LOCK_TYPE_CONFINE:
> >>> +		_wl_confined_pointer_send_confined(resource);
> >>> +		break;
> >>> +	}
> >>> +}
> >>> +
> >>> +static void
> >>> +pointer_lock_notify_deactivated(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	struct wl_resource *resource = lock->resource;
> >>> +
> >>> +	switch (pointer_lock_get_type(lock)) {
> >>> +	case POINTER_LOCK_TYPE_LOCK:
> >>> +		_wl_locked_pointer_send_unlocked(resource);
> >>> +		break;
> >>> +	case POINTER_LOCK_TYPE_CONFINE:
> >>> +		_wl_confined_pointer_send_unconfined(resource);
> >>> +		break;
> >>> +	}
> >>> +}
> >>> +
> >>> +static struct weston_pointer_lock *
> >>> +get_pointer_lock_for_pointer(struct weston_surface *surface,
> >>> +			     struct weston_pointer *pointer)
> >>> +{
> >>> +	struct weston_pointer_lock *lock;
> >>> +
> >>> +	wl_list_for_each(lock, &surface->pointer_locks, link) {
> >>> +		if (lock->pointer == pointer)
> >>> +			return lock;
> >>> +	}
> >>> +
> >>> +	return NULL;
> >>> +}
> >>> +
> >>> +static void
> >>> +enable_pointer_lock(struct weston_pointer_lock *lock,
> >>> +		    struct weston_view *view)
> >>> +{
> >>> +	assert(lock->view == NULL);
> >>> +	lock->view = view;
> >>> +	pointer_lock_notify_activated(lock);
> >>> +	weston_pointer_start_grab(lock->pointer, &lock->grab);
> >>> +}
> >>> +
> >>> +static bool
> >>> +is_pointer_lock_enabled(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	return lock->view != NULL;
> >>> +}
> >>> +
> >>> +void
> >>> +weston_pointer_lock_destroy(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	if (is_pointer_lock_enabled(lock)) {
> >>> +		pointer_lock_notify_deactivated(lock);
> >>> +		weston_pointer_end_grab(lock->grab.pointer);
> >>> +	}
> >>> +
> >>> +	wl_list_remove(&lock->pointer_destroy_listener.link);
> >>> +	wl_list_remove(&lock->surface_destroy_listener.link);
> >>> +	wl_list_remove(&lock->surface_commit_listener.link);
> >>> +	wl_list_remove(&lock->surface_activate_listener.link);
> >>> +
> >>> +	wl_resource_set_user_data(lock->resource, NULL);
> >>> +	pixman_region32_fini(&lock->region);
> >>> +	wl_list_remove(&lock->link);
> >>> +	free(lock);
> >>> +}
> >>> +
> >>> +static void
> >>> +disable_pointer_lock(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	weston_pointer_lock_destroy(lock);
> >>> +}
> >>> +
> >>> +static bool
> >>> +is_within_lock_region(struct weston_pointer_lock *lock,
> >>> +		      wl_fixed_t sx, wl_fixed_t sy)
> >>> +{
> >>> +	struct weston_surface *surface = lock->surface;
> >>> +	pixman_region32_t lock_region;
> >>> +	bool result;
> >>> +
> >>> +	pixman_region32_init(&lock_region);
> >>> +	pixman_region32_intersect(&lock_region, &surface->input, &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_pointer_lock *lock)
> >>> +{
> >>> +	struct weston_surface *surface = lock->surface;
> >>> +	struct weston_view *vit;
> >>> +	struct weston_view *view = NULL;
> >>> +	struct weston_pointer *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(lock,
> >>> +				   wl_fixed_from_int(x),
> >>> +				   wl_fixed_from_int(y)))
> >>> +		return;
> >>> +
> >>> +	enable_pointer_lock(lock, 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_lock *lock =
> >>> +		container_of(grab, struct weston_pointer_lock, grab);
> >>> +
> >>> +	disable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +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_pointer_lock *lock = wl_resource_get_user_data(resource);
> >>> +
> >>> +	if (!lock)
> >>> +		return;
> >>> +
> >>> +	disable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +static void
> >>> +pointer_lock_surface_activate(struct wl_listener *listener, void *data)
> >>> +{
> >>> +	struct weston_surface_activation_data *activation = data;
> >>> +	struct weston_pointer *pointer = activation->seat->pointer;
> >>> +	struct weston_surface *focus = activation->surface;
> >>> +	struct weston_pointer_lock *lock =
> >>> +		container_of(listener, struct weston_pointer_lock,
> >>> +			     surface_activate_listener);
> >>> +	bool is_lock_surface;
> >>> +
> >>> +	is_lock_surface = get_pointer_lock_for_pointer(focus, pointer) == lock;
> >>> +
> >>> +	if (is_lock_surface &&  !is_pointer_lock_enabled(lock))
> >>> +		maybe_enable_pointer_lock(lock);
> >>> +	else if (!is_lock_surface && is_pointer_lock_enabled(lock))
> >>> +		disable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +static void
> >>> +pointer_lock_pointer_destroyed(struct wl_listener *listener, void *data)
> >>> +{
> >>> +	struct weston_pointer_lock *lock =
> >>> +		container_of(listener, struct weston_pointer_lock,
> >>> +			     pointer_destroy_listener);
> >>> +
> >>> +	disable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +static void
> >>> +pointer_lock_surface_destroyed(struct wl_listener *listener, void *data)
> >>> +{
> >>> +	struct weston_pointer_lock *lock =
> >>> +		container_of(listener, struct weston_pointer_lock,
> >>> +			     surface_destroy_listener);
> >>> +
> >>> +	disable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +static void
> >>> +pointer_lock_surface_committed(struct wl_listener *listener, void *data)
> >>> +{
> >>> +	struct weston_pointer_lock *lock =
> >>> +		container_of(listener, struct weston_pointer_lock,
> >>> +			     surface_commit_listener);
> >>> +
> >>> +	if (lock->pending_region_set) {
> >>> +		lock->pending_region_set = false;
> >>> +		pixman_region32_copy(&lock->region, &lock->pending_region);
> >>> +		pixman_region32_fini(&lock->pending_region);
> >>> +		pixman_region32_init(&lock->pending_region);
> >>> +	}
> >>> +
> >>> +	if (pointer_lock_get_type(lock) == POINTER_LOCK_TYPE_CONFINE &&
> >>> +	    is_pointer_lock_enabled(lock))
> >>> +		maybe_warp_confined_pointer(lock);
> >>> +}
> >>> +
> >>> +static struct weston_pointer_lock *
> >>> +weston_pointer_lock_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_lock *lock;
> >>> +
> >>> +	lock = zalloc(sizeof *lock);
> >>> +	if (!lock)
> >>> +		return NULL;
> >>> +
> >>> +	pixman_region32_init(&lock->region);
> >>> +	pixman_region32_init(&lock->pending_region);
> >>> +	wl_list_insert(&surface->pointer_locks, &lock->link);
> >>> +	lock->surface = surface;
> >>> +	lock->pointer = pointer;
> >>> +	lock->resource = cr;
> >>> +	lock->grab.interface = grab_interface;
> >>> +	if (region) {
> >>> +		pixman_region32_copy(&lock->region,
> >>> +				     &region->region);
> >>> +	} else {
> >>> +		pixman_region32_fini(&lock->region);
> >>> +		region_init_infinite(&lock->region);
> >>> +	}
> >>> +
> >>> +	lock->surface_activate_listener.notify = pointer_lock_surface_activate;
> >>> +	lock->surface_destroy_listener.notify = pointer_lock_surface_destroyed;
> >>> +	lock->surface_commit_listener.notify = pointer_lock_surface_committed;
> >>> +	lock->pointer_destroy_listener.notify = pointer_lock_pointer_destroyed;
> >>> +
> >>> +	wl_signal_add(&surface->compositor->activate_signal,
> >>> +		      &lock->surface_activate_listener);
> >>> +	wl_signal_add(&pointer->destroy_signal,
> >>> +		      &lock->pointer_destroy_listener);
> >>> +	wl_signal_add(&surface->destroy_signal,
> >>> +		      &lock->surface_destroy_listener);
> >>> +	wl_signal_add(&surface->commit_signal,
> >>> +		      &lock->surface_commit_listener);
> >>> +
> >>> +	return lock;
> >>> +}
> >>> +
> >>> +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;
> >>> +	struct weston_pointer_lock *lock;
> >>> +
> >>> +	if (get_pointer_lock_for_pointer(surface, pointer)) {
> >>> +		wl_resource_post_error(pointer_lock_resource,
> >>> +				       WL_DISPLAY_ERROR_INVALID_OBJECT,
> >>> +				       "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_lock_resource),
> >>> +				id);
> >>> +	if (cr == NULL) {
> >>> +		wl_client_post_no_memory(client);
> >>> +		return;
> >>> +	}
> >>> +
> >>> +	lock = weston_pointer_lock_create(surface, pointer, region,
> >>> +					  cr, grab_interface);
> >>> +	if (lock == NULL) {
> >>> +		wl_client_post_no_memory(client);
> >>> +		return;
> >>> +	}
> >>> +
> >>> +	wl_resource_set_implementation(cr, implementation, lock,
> >>> +				       pointer_lock_lock_resource_destroyed);
> >>> +
> >>> +	maybe_enable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +static void
> >>> +locked_pointer_destroy(struct wl_client *client,
> >>> +		       struct wl_resource *resource)
> >>> +{
> >>> +	struct weston_pointer_lock *lock = wl_resource_get_user_data(resource);
> >>> +	wl_fixed_t x_hint = lock->x_hint;
> >>> +	wl_fixed_t y_hint = lock->y_hint;
> >>> +	wl_fixed_t x, y;
> >>> +
> >>> +	if (lock->view && lock->hint_set &&
> >>> +	    is_within_lock_region(lock, x_hint, y_hint)) {
> >>> +		weston_view_to_global_fixed(lock->view,
> >>> +					    x_hint, y_hint,
> >>> +					    &x, &y);
> >>> +		weston_pointer_move_to(lock->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_lock *lock = wl_resource_get_user_data(resource);
> >>> +
> >>> +	/* Ignore a set cursor hint that was already sent after the lock
> >>> +	 * was cancelled. */
> >>> +	if (!lock->resource ||
> >>> +	    lock->resource != resource)
> >>> +		return;
> >>> +
> >>> +	lock->hint_set = true;
> >>> +	lock->x_hint = surface_x;
> >>> +	lock->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_lock *lock = 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(&lock->pending_region, &region->region);
> >>> +	} else {
> >>> +		pixman_region32_fini(&lock->pending_region);
> >>> +		region_init_infinite(&lock->pending_region);
> >>> +	}
> >>> +	lock->pending_region_set = true;
> >>> +}
> >>> +
> >>> +
> >>> +static const struct _wl_locked_pointer_interface locked_pointer_interface = {
> >>> +	locked_pointer_destroy,
> >>> +	locked_pointer_set_cursor_position_hint,
> >>> +	locked_pointer_set_region,
> >>> +};
> >>> +
> >>> +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
> >>> +maybe_warp_confined_pointer(struct weston_pointer_lock *lock)
> >>> +{
> >>> +	wl_fixed_t x;
> >>> +	wl_fixed_t y;
> >>> +	wl_fixed_t sx;
> >>> +	wl_fixed_t sy;
> >>> +
> >>> +	weston_view_from_global_fixed(lock->view,
> >>> +				      lock->pointer->x,
> >>> +				      lock->pointer->y,
> >>> +				      &sx,
> >>> +				      &sy);
> >>> +
> >>> +	if (!is_within_lock_region(lock, sx, sy)) {
> >>> +		pixman_region32_t *region = &lock->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(lock->view, sx, sy, &x, &y);
> >>> +		weston_pointer_move_to(lock->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_lock *lock =
> >>> +		container_of(grab, struct weston_pointer_lock, 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 == lock->surface);
> >>> +
> >>> +	surface = pointer->focus->surface;
> >>> +
> >>> +	pixman_region32_init(&confine_region);
> >>> +	pixman_region32_intersect(&confine_region,
> >>> +				  &surface->input,
> >>> +				  &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_pointer_lock *lock =
> >>> +		container_of(grab, struct weston_pointer_lock, grab);
> >>> +
> >>> +	disable_pointer_lock(lock);
> >>> +}
> >>> +
> >>> +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_lock *lock = 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(&lock->pending_region, &region->region);
> >>> +	} else {
> >>> +		pixman_region32_fini(&lock->pending_region);
> >>> +		region_init_infinite(&lock->pending_region);
> >>> +	}
> >>> +	lock->pending_region_set = true;
> >>> +}
> >>> +
> >>> +static const struct _wl_confined_pointer_interface confined_pointer_interface = {
> >>> +	confined_pointer_destroy,
> >>> +	confined_pointer_set_region,
> >>> +};
> >>> +
> >>> +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)
> >>>  {
> >>> @@ -2579,5 +3291,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;
> >>>  }
> >>>
> >>
> > _______________________________________________
> > wayland-devel mailing list
> > wayland-devel@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/wayland-devel
> > 
>