← Back to team overview

canonical-x team mailing list archive

Re: Unity pointer barriers update for xserver 1.14?

 

On Wed, Feb 06, 2013 at 05:52:14PM +0100, Didier Roche wrote:
> Just a quick followup after discussing with Thomas:
> it would be great to have the diff in term of protocol between our
> patch and the new version of it so that we can see if it's cheaper
> (due to the restrained resource) to either revert the upstream fix
> and reapply our patch or just go the new road.
> 
> Do you mind providing us that so that we can estimate the needed work?
> Thanks,
> Didier

Alright.  I'm attaching what I think you need.  Due to the nature of the
changes I think a straight up diff wouldn't be usable.  The protocol is
relatively concise but a lot of things have changed names, and the
protocol bits are moved out of xserver to libxi and inputproto.  I think
it's going to be simpler to just review the original patch and the API
docs for the new code.

was:
  500_pointer_barrier_thresholds.diff is the original code, that Unity
  currently uses.

is:
  pb-events.txt is Peter's intro about the new code changes.
  inputproto.patch and libxi.patch are the protocol changes that have been
  implemented upstream.

The full changeset with all the server changes was presented upstream in
December, and essentially went in as-is (maybe modulo a few bug fixes):

  http://lists.x.org/archives/xorg-devel/2012-December/034690.html


So, you'll need three X packages for your work - x11proto-input, libxi,
and xorg-server.  The first is already in raring, the other two we'll be
putting in this PPA, probably within the next day or so:

  https://launchpad.net/~ubuntu-nexus7/+archive/staging/+packages?field.series_filter=raring

Bryce
>From fetchmail  Thu Dec  6 23:47:57 2012
Return-Path: <xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx>
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on
	humber.bryceharrington.org
X-Spam-Level: 
X-Spam-Status: No, score=0.1 required=5.0 tests=MISSING_FROM autolearn=no
	version=3.3.2
Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145])
	by vinyl.outflux.net (8.14.4/8.14.4/Debian-2ubuntu2) with ESMTP id qB77h0uJ020144
	for <bryce@xxxxxxxxxxxx>; Thu, 6 Dec 2012 23:43:00 -0800
Received: from cluster-j.mailcontrol.com (cluster-j.mailcontrol.com [85.115.54.190])
	by fiordland.canonical.com (Postfix) with ESMTP id 214D5A183ED
	for <bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx>; Fri,  7 Dec 2012 07:42:55 +0000 (UTC)
Received: from arctowski.canonical.com (arctowski.canonical.com [91.189.94.158])
	by rly54j.srv.mailcontrol.com (MailControl) with ESMTP id qB77gpCB022416
	for <bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx>; Fri, 7 Dec 2012 07:42:51 GMT
Received: from fiordland.canonical.com ([91.189.94.145])
	by arctowski.canonical.com with esmtp (Exim 4.71)
	(envelope-from <xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx>)
	id 1TgsaB-0004qL-Io
	for bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx; Fri, 07 Dec 2012 07:42:51 +0000
Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177])
	by fiordland.canonical.com (Postfix) with ESMTP id 5B010A192A1
	for <bryce@xxxxxxxxxxxxx>; Fri,  7 Dec 2012 07:42:51 +0000 (UTC)
Received: from gabe.freedesktop.org (localhost [127.0.0.1])
	by gabe.freedesktop.org (Postfix) with ESMTP id ACFAAE6046
	for <bryce@xxxxxxxxxxxxx>; Thu,  6 Dec 2012 23:42:50 -0800 (PST)
X-Original-To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Delivered-To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Received: from mail.clearchain.com (leo.clearchain.com [199.73.29.74])
	by gabe.freedesktop.org (Postfix) with ESMTP id 63D8FE5C37
	for <xorg-devel@xxxxxxxxxxxxxxxxxxxxx>;
	Thu,  6 Dec 2012 22:25:04 -0800 (PST)
Received: from yabbi.bne.redhat.com (localhost [127.0.0.1])
	by mail.clearchain.com (8.14.5/8.14.5) with ESMTP id qB76P1I5094217
	for <xorg-devel@xxxxxxxxxxxxxxxxxxxxx>;
	Fri, 7 Dec 2012 16:55:02 +1030 (CST)
	(envelope-from peter.hutterer@xxxxxxxxx)
From: Peter Hutterer <peter.hutterer@xxxxxxxxx>
To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Subject: [RFC/PATCH] Pointer barrier events (XI 2.3)
Date: Fri,  7 Dec 2012 16:25:04 +1000
Message-Id: <1354861506-1121-1-git-send-email-peter.hutterer@xxxxxxxxx>
X-Mailer: git-send-email 1.7.11.7
X-Greylist: Sender is SPF-compliant, not delayed by milter-greylist-4.0.1
	(mail.clearchain.com [127.0.0.1]);
	Fri, 07 Dec 2012 16:55:03 +1030 (CST)
X-BeenThere: xorg-devel@xxxxxxxxxxx
X-Mailman-Version: 2.1.13
Precedence: list
List-Id: "X.Org development list" <xorg-devel.lists.x.org>
List-Unsubscribe: <http://lists.x.org/mailman/options/xorg-devel>,
	<mailto:xorg-devel-request@xxxxxxxxxxx?subject=unsubscribe>
List-Archive: <http://lists.x.org/archives/xorg-devel>
List-Post: <mailto:xorg-devel@xxxxxxxxxxx>
List-Help: <mailto:xorg-devel-request@xxxxxxxxxxx?subject=help>
List-Subscribe: <http://lists.x.org/mailman/listinfo/xorg-devel>,
	<mailto:xorg-devel-request@xxxxxxxxxxx?subject=subscribe>
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Sender: xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx
Errors-To: xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx
X-Mailcontrol-Inbound: WBq!ucXEz80wFmxD6S7f3nyjenN!phNbIQWiyBG44+lo3FL0yiLKb3yjenN!phNbh+9WRKrdgbE=
X-Scanned-By: MIMEDefang 2.71 on 198.145.64.163
Envelope-To: bryce@xxxxxxxxxxxx
X-MIMEDefang-Filter: outflux$Revision: 1.316 $
X-HELO: fiordland.canonical.com
Status: RO
Content-Length: 1914
Lines: 45


This is the protocol and client-side for the proposed XI 2.3 extension for
pointer barrier events and barrier release requests. This feature adds two
things:
- if a pointer hits a barrier, events are sent to selected clients
- a client may "release" the pointer, allowing it to pass through the
  barrier with the next movement.

This enables two distinct user-visible features:
- hot corners/edges that respond to pushing against them (GNOME3 wants this)
- ad-hoc transparent barriers, so that depending on the speed of the
  pointer the user can move through the barrier (Unity wants this)

Potential/current issues:
- grab behaviour is different to other events
- loss of predictable pointer acceleration across a barrier. In my tests, I
  have not found this to be noticable
- device-specific release has not yet been implemented
- one of the passive grab tests currently fails

For a full view of the
development history plus server and tests see the following repos:

git://people.freedesktop.org/~whot/inputproto.git :barriers
git://people.freedesktop.org/~whot/libXi.git :barriers
git://people.freedesktop.org/~whot/xserver.git :barriers
git://people.freedesktop.org/~whot/xorg-integration-tests.git :barriers

Many thanks to Jasper and Chris, who did most of the work here.

I think this is a valuable addition to 1.14, unless someone can find
significant issues with the protocol as it is proposed here. Should we merge
this for 1.14, I do reserve the right to disable this feature in the server
before the release, unless we have a credible client-side implementation.
We've learned the lesson with smooth scrolling that a server-implementation
only is not good enough.

Comments appreciated.

Cheers,
  Peter
_______________________________________________
xorg-devel@xxxxxxxxxxx: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

>From fetchmail  Thu Dec  6 23:48:01 2012
Return-Path: <xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx>
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on
	humber.bryceharrington.org
X-Spam-Level: 
X-Spam-Status: No, score=-6.6 required=5.0 tests=BWH_DEBIAN,BWH_DEBIAN_2,
	BWH_FREE,BWH_FREE_2,BWH_PATCH_1,BWH_PATCH_2,BWH_PATCH_3,BWH_RANDOM_GRAMMAR,
	BWH_RANDOM_GRAMMAR_7,MISSING_FROM autolearn=ham version=3.3.2
Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145])
	by vinyl.outflux.net (8.14.4/8.14.4/Debian-2ubuntu2) with ESMTP id qB77hkE6020333
	for <bryce@xxxxxxxxxxxx>; Thu, 6 Dec 2012 23:43:46 -0800
Received: from cluster-e.mailcontrol.com (cluster-e.mailcontrol.com [85.115.58.190])
	by fiordland.canonical.com (Postfix) with ESMTP id 44434A183ED
	for <bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx>; Fri,  7 Dec 2012 07:43:40 +0000 (UTC)
Received: from arctowski.canonical.com (arctowski.canonical.com [91.189.94.158])
	by rly16e.srv.mailcontrol.com (MailControl) with ESMTP id qB77hcC2023366
	for <bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx>; Fri, 7 Dec 2012 07:43:38 GMT
Received: from fiordland.canonical.com ([91.189.94.145])
	by arctowski.canonical.com with esmtp (Exim 4.71)
	(envelope-from <xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx>)
	id 1Tgsaw-0004rE-1L
	for bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx; Fri, 07 Dec 2012 07:43:38 +0000
Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177])
	by fiordland.canonical.com (Postfix) with ESMTP id 197CCA183ED
	for <bryce@xxxxxxxxxxxxx>; Fri,  7 Dec 2012 07:43:37 +0000 (UTC)
Received: from gabe.freedesktop.org (localhost [127.0.0.1])
	by gabe.freedesktop.org (Postfix) with ESMTP id 2C8EAE5DB9
	for <bryce@xxxxxxxxxxxxx>; Thu,  6 Dec 2012 23:43:36 -0800 (PST)
X-Original-To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Delivered-To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Received: from mail.clearchain.com (leo.clearchain.com [199.73.29.74])
	by gabe.freedesktop.org (Postfix) with ESMTP id 643A4E5C7B
	for <xorg-devel@xxxxxxxxxxxxxxxxxxxxx>;
	Thu,  6 Dec 2012 22:25:07 -0800 (PST)
Received: from yabbi.bne.redhat.com (localhost [127.0.0.1])
	by mail.clearchain.com (8.14.5/8.14.5) with ESMTP id qB76P1I7094217;
	Fri, 7 Dec 2012 16:55:05 +1030 (CST)
	(envelope-from peter.hutterer@xxxxxxxxx)
From: Peter Hutterer <peter.hutterer@xxxxxxxxx>
To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Subject: =?UTF-8?q?=5BPATCH=20libXi=5D=20Add=20support=20for=20pointer=20barrier=20events?=
Date: Fri,  7 Dec 2012 16:25:06 +1000
Message-Id: <1354861506-1121-3-git-send-email-peter.hutterer@xxxxxxxxx>
X-Mailer: git-send-email 1.7.11.7
In-Reply-To: <1354861506-1121-1-git-send-email-peter.hutterer@xxxxxxxxx>
References: <1354861506-1121-1-git-send-email-peter.hutterer@xxxxxxxxx>
MIME-Version: 1.0
X-Greylist: Sender is SPF-compliant, not delayed by milter-greylist-4.0.1
	(mail.clearchain.com [127.0.0.1]);
	Fri, 07 Dec 2012 16:55:06 +1030 (CST)
Cc: "Jasper St. Pierre" <jstpierre@xxxxxxxxxxx>
X-BeenThere: xorg-devel@xxxxxxxxxxx
X-Mailman-Version: 2.1.13
Precedence: list
List-Id: "X.Org development list" <xorg-devel.lists.x.org>
List-Unsubscribe: <http://lists.x.org/mailman/options/xorg-devel>,
	<mailto:xorg-devel-request@xxxxxxxxxxx?subject=unsubscribe>
List-Archive: <http://lists.x.org/archives/xorg-devel>
List-Post: <mailto:xorg-devel@xxxxxxxxxxx>
List-Help: <mailto:xorg-devel-request@xxxxxxxxxxx?subject=help>
List-Subscribe: <http://lists.x.org/mailman/listinfo/xorg-devel>,
	<mailto:xorg-devel-request@xxxxxxxxxxx?subject=subscribe>
Content-Type: text/plain; charset="utf-8"
Sender: xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx
Errors-To: xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx
X-Mailcontrol-Inbound: WBq!ucXEz80wFmxD6S7f3nyjenN!phNbIQWiyBG44+lo3FL0yiLKb3yjenN!phNbh+9WRKrdgbE=
X-Scanned-By: MIMEDefang 2.71 on 198.145.64.163
Envelope-To: bryce@xxxxxxxxxxxx
X-MIMEDefang-Filter: outflux$Revision: 1.316 $
X-HELO: fiordland.canonical.com
Content-Transfer-Encoding: 8bit
X-MIME-Autoconverted: from base64 to 8bit by vinyl.outflux.net id qB77hkE6020333
Status: RO
Content-Length: 13117
Lines: 417

From: "Jasper St. Pierre" <jstpierre@xxxxxxxxxxx>

Co-authored-by: Peter Hutterer <peter.hutterer@xxxxxxxxx>
Signed-off-by: Peter Hutterer <peter.hutterer@xxxxxxxxx>
---
 include/X11/extensions/XInput2.h | 47 +++++++++++++++++++++++
 man/Makefile.am                  |  3 ++
 man/XIBarrierReleasePointer.txt  | 76 +++++++++++++++++++++++++++++++++++++
 src/Makefile.am                  |  3 +-
 src/XExtInt.c                    | 62 ++++++++++++++++++++++++++++++
 src/XIBarrier.c                  | 81 ++++++++++++++++++++++++++++++++++++++++
 src/XIint.h                      |  1 +
 7 files changed, 272 insertions(+), 1 deletion(-)
 create mode 100644 man/XIBarrierReleasePointer.txt
 create mode 100644 src/XIBarrier.c

diff --git a/include/X11/extensions/XInput2.h b/include/X11/extensions/XInput2.h
index 26de695..a746012 100644
--- a/include/X11/extensions/XInput2.h
+++ b/include/X11/extensions/XInput2.h
@@ -169,6 +169,16 @@ typedef struct
     int                 status;
 } XIGrabModifiers;
 
+typedef XID PointerBarrier;
+typedef unsigned int BarrierEventID;
+
+typedef struct
+{
+    int                 deviceid;
+    PointerBarrier      barrier;
+    BarrierEventID      eventid;
+} XIBarrierReleasePointerInfo;
+
 /**
  * Generic XI2 event. All XI2 events have the same header.
  */
@@ -328,6 +338,28 @@ typedef struct {
     int           flags;
 } XITouchOwnershipEvent;
 
+typedef struct {
+    int           type;         /* GenericEvent */
+    unsigned long serial;       /* # of last request processed by server */
+    Bool          send_event;   /* true if this came from a SendEvent request */
+    Display       *display;     /* Display the event was read from */
+    int           extension;    /* XI extension offset */
+    int           evtype;
+    Time          time;
+    int           deviceid;
+    int           sourceid;
+    Window        event;
+    Window        root;
+    double        root_x;
+    double        root_y;
+    double        dx;
+    double        dy;
+    int           dtime;
+    int           flags;
+    PointerBarrier barrier;
+    BarrierEventID eventid;
+} XIBarrierEvent;
+
 _XFUNCPROTOBEGIN
 
 extern Bool     XIQueryPointer(
@@ -603,6 +635,21 @@ XIGetProperty(
     unsigned char       **data
 );
 
+extern void
+XIBarrierReleasePointers(
+    Display*                    display,
+    XIBarrierReleasePointerInfo *barriers,
+    int                         num_barriers
+);
+
+extern void
+XIBarrierReleasePointer(
+    Display*                    display,
+    int                         deviceid,
+    PointerBarrier              barrier,
+    BarrierEventID              eventid
+);
+
 extern void XIFreeDeviceInfo(XIDeviceInfo       *info);
 
 _XFUNCPROTOEND
diff --git a/man/Makefile.am b/man/Makefile.am
index af63952..b92d486 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -25,6 +25,7 @@ libmandir = $(LIB_MAN_DIR)
 
 # Source files for X Input v2 man pages
 XI2_txt = 					\
+	XIBarrierReleasePointer.txt 		\
 	XIChangeHierarchy.txt			\
 	XIDefineCursor.txt			\
 	XIGrabButton.txt			\
@@ -76,6 +77,7 @@ libman_xml = $(libman_txt:.txt=.xml)
 # They are created by the xmlto command when generating man pages from DocBook
 # The shadow man page contains a gtroff .so request to include the main man page
 XI2_shadows = 					\
+	XIBarrierReleasePointers.man 		\
 	XIUndefineCursor.man			\
 	XIUngrabButton.man			\
 	XIGrabKeycode.man			\
@@ -152,6 +154,7 @@ XIDeleteProperty.man: XIChangeProperty.man
 XIUngrabEnter.man XIGrabFocusIn.man XIUngrabFocusIn.man: XIGrabEnter.man
 XIGetSelectedEvents.man: XISelectEvents.man
 XIFreeDeviceInfo.man: XIQueryDevice.man
+XIBarrierReleasePointers.man: XIBarrierReleasePointer.man
 
 # String replacements in MAN_SUBSTS now come from xorg-macros.m4 via configure
 # Unable to use __libmansuffix__ as underscores are lost in txt --> xml conversion
diff --git a/man/XIBarrierReleasePointer.txt b/man/XIBarrierReleasePointer.txt
new file mode 100644
index 0000000..4fa48fe
--- /dev/null
+++ b/man/XIBarrierReleasePointer.txt
@@ -0,0 +1,76 @@
+XIBARRIERRELEASEPOINTER(libmansuffix)
+=====================================
+
+NAME
+----
+
+   XIBarrierReleasePointer, XIBarrierReleasePointers - allow pointer movement across a pointer barrier
+
+SYNOPSIS
+--------
+
+   #include <X11/extensions/XInput2.h>
+
+   int XIBarrierReleasePointer( Display *display,
+                                int deviceid,
+                                PointerBarrier barrier,
+                                BarrierEventID eventid);
+
+   int XIBarrierReleasePointers( Display *display,
+                                 XIBarrierReleasePointerInfo *barriers,
+                                 int num_barriers);
+
+   display
+          Specifies the connection to the X server.
+
+   deviceid
+          Specifies the device allowed to move across the barrier.
+
+   barrier
+          Specifies the barrier.
+
+   eventid
+          Specifies the unique event ID for this barrier event sequence.
+
+   barriers
+          List of deviceid, barrier, eventid triplets.
+
+   num_barriers
+          Number of elements in barriers.
+
+DESCRIPTION
+-----------
+
+   If a pointer is constrained by a pointer barrier, release the pointer
+   from the barrier and allow it to cross the barrier with the next motion.
+   If the pointer moves away from the barrier, this request does nothing.
+
+   This request only releases the pointer for the given barrier event
+   sequence. If the pointer moves away from or through the barrier, a
+   XI_BarrierLeave event is generated. Future pointer movement constrained
+   by this barrier will assign a new eventid and require a new
+   XIBarrierReleasePointer request.
+
+   If the pointer is not currently blocked by the barrier or the barrier
+   event sequence has ended, this request does nothing.
+
+   XIBarrierReleasePointer and XIBarrierReleasePointers can generate a
+   BadDevice or BadValue error.
+
+DIAGNOSTICS
+-----------
+
+   BadDevice
+          An invalid device was specified. The device does not
+          exist or is not a master pointer device.
+
+   BadValue
+          A value for a PointerBarrier argument does not name a defined
+          pointer barrier.
+
+See also
+--------
+
+   XFixesCreatePointerBarrier(libmansuffix)
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 806265c..ee40753 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,7 +13,8 @@ XI2_sources = XIAllowEvents.c \
 		  XIWarpPointer.c \
 		  XIHierarchy.c \
 		  XIDefineCursor.c \
-		  XIQueryPointer.c
+		  XIQueryPointer.c \
+		  XIBarrier.c
 
 libXi_la_SOURCES = \
 		  XAllowDv.c \
diff --git a/src/XExtInt.c b/src/XExtInt.c
index 1c668c7..57d071d 100644
--- a/src/XExtInt.c
+++ b/src/XExtInt.c
@@ -124,6 +124,9 @@ wireToPropertyEvent(xXIPropertyEvent *in, XGenericEventCookie *cookie);
 static int
 wireToTouchOwnershipEvent(xXITouchOwnershipEvent *in,
                           XGenericEventCookie *cookie);
+static int
+wireToBarrierEvent(xXIBarrierEvent *in,
+                   XGenericEventCookie *cookie);
 
 static /* const */ XEvent emptyevent;
 
@@ -1022,6 +1025,16 @@ XInputWireToCookie(
                 break;
             }
             return ENQUEUE_EVENT;
+        case XI_BarrierHit:
+        case XI_BarrierLeave:
+            *cookie = *(XGenericEventCookie*)save;
+            if (!wireToBarrierEvent((xXIBarrierEvent*)event, cookie))
+            {
+                printf("XInputWireToCookie: CONVERSION FAILURE!  evtype=%d\n",
+                        ge->evtype);
+                break;
+            }
+            return ENQUEUE_EVENT;
         default:
             printf("XInputWireToCookie: Unknown generic event. type %d\n", ge->evtype);
 
@@ -1403,7 +1416,21 @@ copyRawEvent(XGenericEventCookie *cookie_in,
     return True;
 }
 
+static Bool
+copyBarrierEvent(XGenericEventCookie *in_cookie,
+                 XGenericEventCookie *out_cookie)
+{
+    XIBarrierEvent *in, *out;
+
+    in = in_cookie->data;
+
+    out = out_cookie->data = calloc(1, sizeof(XIBarrierEvent));
+    if (!out)
+        return False;
+    *out = *in;
 
+    return True;
+}
 
 static Bool
 XInputCopyCookie(Display *dpy, XGenericEventCookie *in, XGenericEventCookie *out)
@@ -1459,6 +1486,10 @@ XInputCopyCookie(Display *dpy, XGenericEventCookie *in, XGenericEventCookie *out
         case XI_RawMotion:
             ret = copyRawEvent(in, out);
             break;
+        case XI_BarrierHit:
+        case XI_BarrierLeave:
+            ret = copyBarrierEvent(in, out);
+            break;
         default:
             printf("XInputCopyCookie: unknown evtype %d\n", in->evtype);
             ret = False;
@@ -1958,3 +1989,34 @@ wireToTouchOwnershipEvent(xXITouchOwnershipEvent *in,
 
     return 1;
 }
+
+#define FP3232_TO_DOUBLE(x) ((double) (x).integral + (x).frac / (1ULL << 32))
+
+static int
+wireToBarrierEvent(xXIBarrierEvent *in, XGenericEventCookie *cookie)
+{
+    XIBarrierEvent *out = malloc(sizeof(XIBarrierEvent));
+
+    cookie->data = out;
+
+    out->display    = cookie->display;
+    out->type       = in->type;
+    out->extension  = in->extension;
+    out->evtype     = in->evtype;
+    out->send_event = ((in->type & 0x80) != 0);
+    out->time       = in->time;
+    out->deviceid   = in->deviceid;
+    out->sourceid   = in->sourceid;
+    out->event      = in->event;
+    out->root       = in->root;
+    out->root_x     = FP1616toDBL(in->root_x);
+    out->root_y     = FP1616toDBL(in->root_y);
+    out->dx         = FP3232_TO_DOUBLE (in->dx);
+    out->dy         = FP3232_TO_DOUBLE (in->dy);
+    out->dtime      = in->dtime;
+    out->flags      = in->flags;
+    out->barrier    = in->barrier;
+    out->eventid    = in->eventid;
+
+    return 1;
+}
diff --git a/src/XIBarrier.c b/src/XIBarrier.c
new file mode 100644
index 0000000..155b98b
--- /dev/null
+++ b/src/XIBarrier.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2009 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.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <X11/Xlibint.h>
+#include <X11/extensions/XI2proto.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/extutil.h>
+#include "XIint.h"
+
+void
+XIBarrierReleasePointers(Display *dpy,
+                         XIBarrierReleasePointerInfo *barriers,
+                         int num_barriers)
+{
+    XExtDisplayInfo	        *info = XInput_find_display(dpy);
+    xXIBarrierReleasePointerReq *req;
+    int extra = 0;
+    int i;
+    xXIBarrierReleasePointerInfo *b;
+
+    if (!num_barriers)
+        return;
+
+    extra = (num_barriers * sizeof(xXIBarrierReleasePointerInfo));
+
+    LockDisplay (dpy);
+    GetReqExtra (XIBarrierReleasePointer, extra, req);
+    req->reqType = info->codes->major_opcode;
+    req->ReqType = X_XIBarrierReleasePointer;
+    req->num_barriers = num_barriers;
+
+    b = (xXIBarrierReleasePointerInfo *) &req[1];
+    for (i = 0; i < num_barriers; i++, b++) {
+        b->deviceid = barriers[i].deviceid;
+        b->eventid = barriers[i].eventid;
+        b->barrier = barriers[i].barrier;
+    }
+
+    UnlockDisplay (dpy);
+    SyncHandle ();
+}
+
+void
+XIBarrierReleasePointer(Display *dpy,
+                        int            deviceid,
+                        PointerBarrier barrier,
+                        BarrierEventID eventid)
+{
+    XIBarrierReleasePointerInfo info;
+    info.deviceid = deviceid;
+    info.barrier = barrier;
+    info.eventid = eventid;
+
+    XIBarrierReleasePointers(dpy, &info, 1);
+}
diff --git a/src/XIint.h b/src/XIint.h
index be4eafb..571bb23 100644
--- a/src/XIint.h
+++ b/src/XIint.h
@@ -22,6 +22,7 @@
 #endif
 #define XInput_2_1			8
 #define XInput_2_2			9
+#define XInput_2_3			10
 
 extern XExtDisplayInfo *XInput_find_display(Display *);
 
-- 
1.7.11.7

_______________________________________________
xorg-devel@xxxxxxxxxxx: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

>From fetchmail  Thu Dec  6 23:42:37 2012
Return-Path: <xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx>
X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on
	humber.bryceharrington.org
X-Spam-Level: 
X-Spam-Status: No, score=-7.8 required=5.0 tests=BWH_DEBIAN,BWH_DEBIAN_2,
	BWH_PATCH_1,BWH_PATCH_2,BWH_PATCH_3,MISSING_FROM autolearn=ham version=3.3.2
Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145])
	by vinyl.outflux.net (8.14.4/8.14.4/Debian-2ubuntu2) with ESMTP id qB77gErS020125
	for <bryce@xxxxxxxxxxxx>; Thu, 6 Dec 2012 23:42:15 -0800
Received: from cluster-j.mailcontrol.com (cluster-j.mailcontrol.com [85.115.54.190])
	by fiordland.canonical.com (Postfix) with ESMTP id 7CEE7A183ED
	for <bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx>; Fri,  7 Dec 2012 07:42:09 +0000 (UTC)
Received: from arctowski.canonical.com (arctowski.canonical.com [91.189.94.158])
	by rly61j.srv.mailcontrol.com (MailControl) with ESMTP id qB77g6oX028753
	for <bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx>; Fri, 7 Dec 2012 07:42:06 GMT
Received: from fiordland.canonical.com ([91.189.94.145])
	by arctowski.canonical.com with esmtp (Exim 4.71)
	(envelope-from <xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx>)
	id 1TgsZS-0004of-Fr
	for bryce.harrington@xxxxxxxxxxxxxxxxxxxxxxx; Fri, 07 Dec 2012 07:42:06 +0000
Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177])
	by fiordland.canonical.com (Postfix) with ESMTP id 7B6C6A189C2
	for <bryce@xxxxxxxxxxxxx>; Fri,  7 Dec 2012 07:42:05 +0000 (UTC)
Received: from gabe.freedesktop.org (localhost [127.0.0.1])
	by gabe.freedesktop.org (Postfix) with ESMTP id 547D8E5F8A
	for <bryce@xxxxxxxxxxxxx>; Thu,  6 Dec 2012 23:42:04 -0800 (PST)
X-Original-To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Delivered-To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Received: from mail.clearchain.com (leo.clearchain.com [199.73.29.74])
	by gabe.freedesktop.org (Postfix) with ESMTP id 63E33E5C3B
	for <xorg-devel@xxxxxxxxxxxxxxxxxxxxx>;
	Thu,  6 Dec 2012 22:25:05 -0800 (PST)
Received: from yabbi.bne.redhat.com (localhost [127.0.0.1])
	by mail.clearchain.com (8.14.5/8.14.5) with ESMTP id qB76P1I6094217;
	Fri, 7 Dec 2012 16:55:03 +1030 (CST)
	(envelope-from peter.hutterer@xxxxxxxxx)
From: Peter Hutterer <peter.hutterer@xxxxxxxxx>
To: xorg-devel@xxxxxxxxxxxxxxxxxxxxx
Subject: =?UTF-8?q?=5BPATCH=20inputproto=5D=20Add=20pointer=20barrier=20events?=
Date: Fri,  7 Dec 2012 16:25:05 +1000
Message-Id: <1354861506-1121-2-git-send-email-peter.hutterer@xxxxxxxxx>
X-Mailer: git-send-email 1.7.11.7
In-Reply-To: <1354861506-1121-1-git-send-email-peter.hutterer@xxxxxxxxx>
References: <1354861506-1121-1-git-send-email-peter.hutterer@xxxxxxxxx>
MIME-Version: 1.0
X-Greylist: Sender is SPF-compliant, not delayed by milter-greylist-4.0.1
	(mail.clearchain.com [127.0.0.1]);
	Fri, 07 Dec 2012 16:55:04 +1030 (CST)
X-BeenThere: xorg-devel@xxxxxxxxxxx
X-Mailman-Version: 2.1.13
Precedence: list
List-Id: "X.Org development list" <xorg-devel.lists.x.org>
List-Unsubscribe: <http://lists.x.org/mailman/options/xorg-devel>,
	<mailto:xorg-devel-request@xxxxxxxxxxx?subject=unsubscribe>
List-Archive: <http://lists.x.org/archives/xorg-devel>
List-Post: <mailto:xorg-devel@xxxxxxxxxxx>
List-Help: <mailto:xorg-devel-request@xxxxxxxxxxx?subject=help>
List-Subscribe: <http://lists.x.org/mailman/listinfo/xorg-devel>,
	<mailto:xorg-devel-request@xxxxxxxxxxx?subject=subscribe>
Content-Type: text/plain; charset="utf-8"
Sender: xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx
Errors-To: xorg-devel-bounces+bryce=canonical.com@xxxxxxxxxxx
X-Mailcontrol-Inbound: WBq!ucXEz80wFmxD6S7f3nyjenN!phNbIQWiyBG44+lo3FL0yiLKb3yjenN!phNbh+9WRKrdgbE=
X-Scanned-By: MIMEDefang 2.71 on 198.145.64.163
Envelope-To: bryce@xxxxxxxxxxxx
X-MIMEDefang-Filter: outflux$Revision: 1.316 $
X-HELO: fiordland.canonical.com
Content-Transfer-Encoding: 8bit
X-MIME-Autoconverted: from base64 to 8bit by vinyl.outflux.net id qB77gErS020125
Status: RO
Content-Length: 14590
Lines: 378

New events:
    XI_BarrierHit ... when a pointer moves against or along a barrier
    XI_BarrierLeave . when a pointer moves away from or through a barrier
New requests:
    XIBarrierReleasePointer ... allow movement through the barrier for the
                                next event

Co-authored-by: Jasper St. Pierre <jstpierre@xxxxxxxxxxx>
Co-authored-by: Christopher James Halse Rogers <christopher.halse.rogers@xxxxxxxxxxxxx>
Signed-off-by: Peter Hutterer <peter.hutterer@xxxxxxxxx>
---
 XI2.h              |  11 +++-
 XI2proto.h         |  49 +++++++++++++-
 specs/XI2proto.txt | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 245 insertions(+), 2 deletions(-)

diff --git a/XI2.h b/XI2.h
index e864b06..5a245e8 100644
--- a/XI2.h
+++ b/XI2.h
@@ -160,6 +160,11 @@
 #define XITouchPendingEnd                       (1 << 16)
 #define XITouchEmulatingPointer                 (1 << 17)
 
+/* Barrier event flags */
+#define XIBarrierPointerReleased                (1 << 0)
+#define XIBarrierDeviceIsGrabbed                (1 << 1)
+
+
 /* Touch modes */
 #define XIDirectTouch                           1
 #define XIDependentTouch                        2
@@ -199,7 +204,9 @@
 #define XI_RawTouchBegin                 22
 #define XI_RawTouchUpdate                23
 #define XI_RawTouchEnd                   24
-#define XI_LASTEVENT                     XI_RawTouchEnd
+#define XI_BarrierHit                    25 /* XI 2.3 */
+#define XI_BarrierLeave                  26
+#define XI_LASTEVENT                     XI_BarrierLeave
 /* NOTE: XI2LASTEVENT in xserver/include/inputstr.h must be the same value
  * as XI_LASTEVENT if the server is supposed to handle masks etc. for this
  * type of event. */
@@ -232,5 +239,7 @@
 #define XI_RawTouchBeginMask             (1 << XI_RawTouchBegin)
 #define XI_RawTouchEndMask               (1 << XI_RawTouchEnd)
 #define XI_RawTouchUpdateMask            (1 << XI_RawTouchUpdate)
+#define XI_BarrierHitMask                (1 << XI_BarrierHit)
+#define XI_BarrierLeaveMask              (1 << XI_BarrierLeave)
 
 #endif /* _XI2_H_ */
diff --git a/XI2proto.h b/XI2proto.h
index 9418357..4cdaa0d 100644
--- a/XI2proto.h
+++ b/XI2proto.h
@@ -67,6 +67,7 @@
 #define Time    uint32_t
 #define Atom    uint32_t
 #define Cursor  uint32_t
+#define Barrier uint32_t
 
 /**
  * XI2 Request opcodes
@@ -92,9 +93,10 @@
 #define X_XIDeleteProperty              58
 #define X_XIGetProperty                 59
 #define X_XIGetSelectedEvents           60
+#define X_XIBarrierReleasePointer       61
 
 /** Number of XI requests */
-#define XI2REQUESTS (X_XIGetSelectedEvents - X_XIQueryPointer + 1)
+#define XI2REQUESTS (X_XIBarrierReleasePointer - X_XIQueryPointer + 1)
 /** Number of XI2 events */
 #define XI2EVENTS   (XI_LASTEVENT + 1)
 
@@ -815,6 +817,22 @@ typedef struct {
 } xXIGetPropertyReply;
 #define sz_xXIGetPropertyReply               32
 
+typedef struct {
+    uint16_t    deviceid;
+    uint16_t    pad;
+    Barrier     barrier;
+    uint32_t    eventid;
+} xXIBarrierReleasePointerInfo;
+
+typedef struct {
+    uint8_t     reqType;                /**< Input extension major opcode */
+    uint8_t     ReqType;                /**< Always X_XIBarrierReleasePointer */
+    uint16_t    length;
+    uint32_t    num_barriers;
+    /* array of xXIBarrierReleasePointerInfo */
+} xXIBarrierReleasePointerReq;
+#define sz_xXIBarrierReleasePointerReq       8
+
 /*************************************************************************************
  *                                                                                   *
  *                                      EVENTS                                       *
@@ -1035,10 +1053,39 @@ typedef struct
     uint32_t    pad3;
 } xXIPropertyEvent;
 
+typedef struct
+{
+    uint8_t     type;                   /**< Always GenericEvent */
+    uint8_t     extension;              /**< XI extension offset */
+    uint16_t    sequenceNumber;
+    uint32_t    length;                 /**< Length in 4 byte units */
+    uint16_t    evtype;                 /**< ::XI_BarrierHit or ::XI_BarrierLeave */
+    uint16_t    deviceid;
+    Time        time;
+    uint32_t    eventid;
+    Window      root;
+    Window      event;
+    Barrier     barrier;
+/* └──────── 32 byte boundary ────────┘ */
+    uint32_t    dtime;
+    uint32_t    flags;                  /**< ::XIBarrierPointerReleased
+                                             ::XIBarrierDeviceIsGrabbed */
+    uint16_t    sourceid;
+    int16_t     pad;
+    FP1616      root_x;
+    FP1616      root_y;
+    FP3232      dx;
+    FP3232      dy;
+} xXIBarrierEvent;
+
+typedef xXIBarrierEvent xXIBarrierHitEvent;
+typedef xXIBarrierEvent xXIBarrierPointerReleasedEvent;
+typedef xXIBarrierEvent xXIBarrierLeaveEvent;
 
 #undef Window
 #undef Time
 #undef Atom
 #undef Cursor
+#undef Barrier
 
 #endif /* _XI2PROTO_H_ */
diff --git a/specs/XI2proto.txt b/specs/XI2proto.txt
index c018026..402210f 100644
--- a/specs/XI2proto.txt
+++ b/specs/XI2proto.txt
@@ -14,6 +14,7 @@ Authors:
 History 
 -------
 
+- v2.3, December 2012: Pointer barrier events added
 - v2.2, March 2012: Multitouch support added
 - v2.1, December 2011: new raw event behaviour, smooth scrolling support
   added
@@ -57,6 +58,10 @@ Changes in version 2.2
 
 - Multitouch support added
 
+Changes in version 2.3
+----------------------
+
+- Pointer barrier events added
 
 //                            ❧❧❧❧❧❧❧❧❧❧❧
 
@@ -551,6 +556,54 @@ window set has been reached, the event is delivered:
 Emulated pointer events will have the PointerEmulated flag set. A touch
 event that emulates pointer events has the TouchEmulatingPointer flag set.
 
+
+[[barrier-events]]
+Pointer barrier events
+^^^^^^^^^^^^^^^^^^^^^^
+If a master pointer moves against a pointer barrier blocking movement in
+that pointer's direction, the movement of the pointer is clamped to the x or
+y coordinate of the barrier, whichever applies. For a description of pointer
+barriers and barrier creation and destruction see the XFixes protocol
+specification v 5.0 or later.
+http://cgit.freedesktop.org/xorg/proto/fixesproto/plain/fixesproto.txt
+
+A pointer hitting a blocking barrier creates a new barrier event sequence,
+identified by a unique event ID. A new event ID is assigned when the pointer
+first hits a barrier. Subsequent movements against or along the pointer
+barrier are assigned the same event ID. The event generated by the pointer
+leaving the barrier, or being released by a client request, is the last
+event with this event ID. Any future movements of this device blocked by
+this barrier will be assigned a new event ID.
+
+Pointer barrier events are delivered exclusively to the client that created
+the barrier, and to the window specified in the CreatePointerBarrier
+request (the "barrier window"). A pointer barrier blocks pointer movement
+regardless of whether its window is mapped and/or viewable. If the pointer
+barrier window is destroyed, the pointer barrier remains blocking but a
+client will not receive further events.
+
+If a device is actively grabbed by a client or a passive grab activated
+for this client, and the pointer moves against a pointer barrier created by
+this client and the grab-window is the barrier window, that client will
+receive pointer barrier events if:
+- owner-events is true or false and the grab's event mask includes
+  pointer barrier events, or
+- owner-events is true and the client has selected for barrier events on the
+  barrier window.
+
+If the grab-window is not the barrier window, the client will receive events
+if:
+- the client has selected for barrier events on the barrier window.
+
+If the barrier is not owned by this client, no barrier events are sent to
+this client. The client owning the barrier will receive events if:
+- the client has pointer barrier events selected on the window associated
+  with the pointer barrier
+
+The BarrierDeviceIsGrabbed flag is set whenever a pointer barrier event is
+generated while the device is actively grabbed by any client or a passive
+grab has activated for this device prior to the event.
+
 [[glossary-notations]]
 Notations used in this document
 -------------------------------
@@ -1940,6 +1993,48 @@ giving the number of trailing unread bytes in the stored property. If
 delete is True and the bytes_after is zero, the property is also
 deleted from the device, and a XIPropertyNotify event is generated on
 the device.  
+
+[[requests-xi23]]
+Requests introduced in version 2.3
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[[requests-barrierreleasepointer]]
+XIBarrierReleasePointer
+^^^^^^^^^^^^^^^^^^^^^^^
+    ┌───
+        XIBarrierReleasePointer
+            num_items:       CARD32
+            ▶
+            data:            LISTofBARRIERRELEASEINFO
+    └───
+
+    BARRIERRELEASEINFO { deviceid: DEVICEID,
+                         barrier:  Barrier,
+                         eventid: CARD32 }
+
+Release a pointer currently blocked by a barrier. In the future, movement of
+this pointer against the barrier will not be blocked.
+
+        deviceid
+            The device currently being blocked by a barrier
+        barrier
+            The barrier currently blocking the device
+        eventid
+            The unique event ID assigned to this barrier event sequence
+
+If the barrier given does not currently block this device, or the eventid
+is invalid, this request does nothing.
+
+Releasing a pointer barrier is only valid during one barrier event sequence,
+and only applies to the next movement of this device against this barrier.
+If the pointer moves away from the barrier following a
+XIBarrierReleasePointer request, the release request is discarded. In the
+future, if the pointer moves against the barrier again, a new eventid is
+assigned and the client must re-issue the XIBarrierReleasePointer request.
+
+If the device is not a master pointer device, a BadDevice error results.
+If the barrier does not name a valid barrier, a BadValue error results.
+
      
 [[events]]
 Events
@@ -1984,6 +2079,11 @@ Version 2.2:
         - RawTouchUpdate
         - RawTouchEnd
 
+Version 2.3:
+
+        - BarrierHit
+        - BarrierLeave
+
 All events have a set of common fields specified as EVENTHEADER.
 
 
@@ -2434,6 +2534,93 @@ is now the owner of the touch sequence specified by touchid.
     flags
         A bitmask of flags for this event.
 
+[[events-xi23]]
+Events introduced in version 2.3
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[[events-barrierevent]]
+BarrierEvent
+^^^^^^^^^^^^
+    ┌───
+        BarrierEvent
+            EVENTHEADER
+            eventid:                    CARD32
+            root:                       Window
+            event:                      Window
+            barrier:                    Barrier
+            dtime:                      CARD32
+            flags:                      SETofBARRIERFLAGS
+            sourceid:                   DEVICEID
+            root_x:                     FP1616
+            root_y:                     FP1616
+            dx:                         FP3232
+            dy:                         FP3232
+    └───
+
+    BARRIERFLAGS { PointerReleased, DeviceIsGrabbed }
+
+A BarrierEvent indicates interaction between a barrier and a pointer device.
+If the event type is BarrierHit, pointer movement has been blocked by a
+barrier. If the event type is BarrierLeave, a pointer previously blocked
+by a barrier has moved away from that barrier, or has moved
+through the blocking barrier following an earlier XIBarrierReleasePointer
+request.
+
+    eventid
+       The unique event ID for this barrier event sequence.
+    root
+    event
+        The root window or barrier window, respectively. The barrier window
+        is always the drawable specified in in the CreatePointerBarrier request.
+    barrier
+        The barrier blocking pointer movement.
+    dtime
+        The relative time in milliseconds between the last event and this
+        event.
+    flags
+        A set of flags that apply to this barrier event
+        PointerReleased:
+           The pointer has moved through the barrier following a
+           XIBarrierReleasePointer request (BarrierLeave only).
+        DeviceIsGrabbed:
+           The pointer device that generated this event is currently
+           grabbed.
+    sourceid
+        The source device that originally generated the event.
+    root_x
+    root_y
+        The position of the pointer in screen coordinates (16.16 fixed
+        point), after being constrained by barrier and/or screen extents.
+    dx
+    dy
+        The relative movement of the pointer from its previous position to
+        the new position if pointer movement were not constrained by this
+        barrier.
+
+Root coordinates in barrier events represent the position of the cursor
+after confinement by barriers, screens and RandR output extents.
+
+Barrier event IDs are provided in the eventid field of barrier events. Its
+value is always provided in every barrier event. Event IDs are
+represented as unsigned 32-bit values and increase strictly monotonically in
+value for each new barrier event sequence, wrapping back to 0 upon reaching
+the numerical limit of IDs. The increment between two event IDs is
+indeterminate. Clients may not assume that any future barrier constraints
+will have specific event IDs. IDs are unique per device per barrier.
+
+If a pointer is actively grabbed after a barrier event sequence has
+initiated, future barrier events of this sequence continue to use the same
+eventid, but all barrier events have the DeviceIsGrabbed flag set. If the
+pointer is ungrabbed, future events of this sequence have the same eventid
+and the DeviceIsGrabbed flag is unset.
+
+The PointerReleased flag may only be set on a BarrierLeave event.
+A BarrierLeave(PointerReleased) event is generated when the pointer moves
+through the barrier following a XIBarrierReleasePointer request. The time
+between the XIBarrierReleasePointer and the BarrierLeave event thus depends
+on user input.
+A BarrierLeave(PointerReleased) event is also generated if the barrier is
+destroyed while pointer movement is constrained by the barrier.
 
 :numbered!:
 [[xi22-usecases]]
-- 
1.7.11.7

_______________________________________________
xorg-devel@xxxxxxxxxxx: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

diff --git a/configure.ac b/configure.ac
index 2693ce7..b5c3880 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2130,6 +2130,25 @@ AC_SUBST([prefix])
 
 AC_CONFIG_COMMANDS([sdksyms], [touch hw/xfree86/sdksyms.dep])
 
+AC_PROG_CXX
+
+PKG_CHECK_MODULES(XORG_GTEST, xorg-gtest,
+        [have_xorg_gtest="yes"],
+        [AC_MSG_WARN([xorg-gtest not installed, tests will not be built])])
+AM_CONDITIONAL([HAVE_XORG_GTEST], [test "x$have_xorg_gtest" = xyes])
+AC_SUBST([XORG_GTEST_CFLAGS])
+AC_SUBST([XORG_GTEST_LIBS])
+
+PKG_CHECK_MODULES([XFIXES], xfixes, [have_xfixes="yes"], [have_xfixes="no"])
+AM_CONDITIONAL([HAVE_XFIXES], [test "x$have_xfixes" = xyes])
+AC_SUBST([XFIXES_CFLAGS])
+AC_SUBST([XFIXES_LIBS])
+
+PKG_CHECK_MODULES([XTEST], xtst, [have_xtest="yes"], [have_xtest="no"])
+AM_CONDITIONAL([HAVE_XTEST], [test "x$have_xtest" = xyes])
+AC_SUBST([XTEST_CFLAGS])
+AC_SUBST([XTEST_LIBS])
+
 AC_OUTPUT([
 Makefile
 glx/Makefile
@@ -2230,6 +2249,7 @@ hw/kdrive/linux/Makefile
 hw/kdrive/src/Makefile
 test/Makefile
 test/xi2/Makefile
+test/gtest/Makefile
 xserver.ent
 xorg-server.pc
 ])
diff --git a/dix/dixutils.c b/dix/dixutils.c
index 9eb5489..bae5fbc 100644
--- a/dix/dixutils.c
+++ b/dix/dixutils.c
@@ -522,7 +522,7 @@ ProcessWorkQueue(void)
         if ((*q->function) (q->client, q->closure)) {
             /* remove q from the list */
             *p = q->next;       /* don't fetch until after func called */
-            free(q);
+            (*q->destroyProc) (q);
         }
         else {
             p = &q->next;       /* don't fetch until after func called */
@@ -542,7 +542,7 @@ ProcessWorkQueueZombies(void)
             (void) (*q->function) (q->client, q->closure);
             /* remove q from the list */
             *p = q->next;       /* don't fetch until after func called */
-            free(q);
+            (*q->destroyProc) (q);
         }
         else {
             p = &q->next;       /* don't fetch until after func called */
@@ -551,6 +551,12 @@ ProcessWorkQueueZombies(void)
     workQueueLast = p;
 }
 
+static void
+FreeWorkQueueItem (WorkQueuePtr this)
+{
+    free(this);
+}
+
 Bool
 QueueWorkProc(Bool (*function)
               (ClientPtr /* pClient */ , pointer /* closure */ ),
@@ -564,12 +570,22 @@ QueueWorkProc(Bool (*function)
     q->function = function;
     q->client = client;
     q->closure = closure;
+    q->destroyProc = FreeWorkQueueItem;
     q->next = NULL;
     *workQueueLast = q;
     workQueueLast = &q->next;
     return TRUE;
 }
 
+Bool
+QueueWorkItem (WorkQueuePtr item)
+{
+    item->next = NULL;
+    *workQueueLast = item;
+    workQueueLast = &item->next;
+    return TRUE;
+}
+
 /*
  * Manage a queue of sleeping clients, awakening them
  * when requested, by using the OS functions IgnoreClient
diff --git a/dix/getevents.c b/dix/getevents.c
index 3093786..c608382 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -85,6 +85,12 @@ typedef const char *string;
 InternalEvent *InputEventList = NULL;
 
 /**
+ * xfixes/cursor.c wants the unclamped (x,y) values for velocity
+ * calculation.  Export them here.
+ */
+int unclamped_prex = -1, unclamped_prey = -1;
+
+/**
  * Pick some arbitrary size for Xi motion history.
  */
 int
@@ -916,7 +922,15 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask,
     /* miPointerSetPosition takes care of crossing screens for us, as well as
      * clipping to the current screen. Coordinates returned are in desktop
      * coord system */
+    /*
+     * Hack to pass the unclipped values through to the pointer barrier code.
+     * Required (for now) to calculate the velocity.
+     */
+    unclamped_prex = (int)floor(*screenx) - scr->x;
+    unclamped_prey = (int)floor(*screeny) - scr->y;
     scr = miPointerSetPosition(dev, mode, screenx, screeny);
+    unclamped_prex = -1;
+    unclamped_prey = -1;
 
     /* If we were constrained, rescale x/y from the screen coordinates so
      * the device valuators reflect the correct position. For screen
diff --git a/include/dix.h b/include/dix.h
index 5dc2ac5..78b8a73 100644
--- a/include/dix.h
+++ b/include/dix.h
@@ -245,6 +245,8 @@ extern _X_EXPORT Bool QueueWorkProc(Bool (* /*function */ )(
                                     pointer     /*closure */
     );
 
+extern _X_EXPORT Bool QueueWorkItem(WorkQueuePtr item);
+
 typedef Bool (*ClientSleepProcPtr) (ClientPtr /*client */ ,
                                     pointer /*closure */ );
 
diff --git a/include/dixstruct.h b/include/dixstruct.h
index b2a168a..ba660a3 100644
--- a/include/dixstruct.h
+++ b/include/dixstruct.h
@@ -153,6 +153,9 @@ typedef struct _WorkQueue {
         );
     ClientPtr client;
     pointer closure;
+    void       (*destroyProc) (
+                struct _WorkQueue * /* this */
+);
 } WorkQueueRec;
 
 extern _X_EXPORT TimeStamp currentTime;
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index 479ac2f..f965d6f 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -122,7 +122,7 @@
 #define SERVER_XF86VIDMODE_MINOR_VERSION	2
 
 /* Fixes */
-#define SERVER_XFIXES_MAJOR_VERSION		5
+#define SERVER_XFIXES_MAJOR_VERSION		6
 #define SERVER_XFIXES_MINOR_VERSION		0
 
 /* X Input */
diff --git a/test/Makefile.am b/test/Makefile.am
index eb61470..29e3fc2 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,6 +7,9 @@ if XORG
 SUBDIRS += xi2
 noinst_PROGRAMS += xkb input xtest misc fixes xfree86
 endif
+if HAVE_XORG_GTEST
+SUBDIRS+= gtest
+endif
 check_LTLIBRARIES = libxservertest.la
 
 TESTS=$(noinst_PROGRAMS)
diff --git a/xfixes/cursor.c b/xfixes/cursor.c
index 602b906..cab5b31 100644
--- a/xfixes/cursor.c
+++ b/xfixes/cursor.c
@@ -61,6 +61,7 @@ static RESTYPE CursorClientType;
 static RESTYPE CursorHideCountType;
 static RESTYPE CursorWindowType;
 RESTYPE PointerBarrierType;
+static RESTYPE PointerBarrierClientType;
 static CursorPtr CursorCurrent[MAXDEVICES];
 
 static DevPrivateKeyRec CursorScreenPrivateKeyRec;
@@ -121,6 +122,11 @@ struct PointerBarrierClient {
 };
 
 /*
+ * Pick up unclamped (x,y) coordinates from dix/getevents
+ */
+extern int unclamped_prex, unclamped_prey;
+
+/*
  * Wrap DisplayCursor to catch cursor change events
  */
 
@@ -130,6 +136,7 @@ typedef struct _CursorScreen {
     ConstrainCursorHarderProcPtr ConstrainCursorHarder;
     CursorHideCountPtr pCursorHideCounts;
     struct xorg_list barriers;
+    struct xorg_list barrierClients;
 } CursorScreenRec, *CursorScreenPtr;
 
 #define GetCursorScreen(s) ((CursorScreenPtr)dixLookupPrivate(&(s)->devPrivates, CursorScreenPrivateKey))
@@ -1080,7 +1087,8 @@ barrier_is_blocking(const struct PointerBarrier * barrier,
 
     /* Algorithm below doesn't handle edge cases well, hence the extra
      * checks. */
-    if (barrier_is_vertical(barrier)) {
+        if (barrier_is_vertical(barrier) &&
+	    (dir & (BarrierPositiveX | BarrierNegativeX))) {
         /* handle immediate barrier adjacency, moving away */
         if (dir & BarrierPositiveX && x1 == barrier->x1)
             return FALSE;
@@ -1092,7 +1100,8 @@ barrier_is_blocking(const struct PointerBarrier * barrier,
             return TRUE;
         }
     }
-    else {
+   else if (barrier_is_horizontal(barrier) &&
+		(dir & (BarrierPositiveY | BarrierNegativeY))){
         /* handle immediate barrier adjacency, moving away */
         if (dir & BarrierPositiveY && y1 == barrier->y1)
             return FALSE;
@@ -1191,6 +1200,126 @@ barrier_clamp_to_barrier(struct PointerBarrier *barrier, int dir, int *x,
     }
 }
 
+/*
+ * ConstrainCursorHarder is called from the SIGIO context.
+ * This means we cannot safely send a client event from anything in
+ * CursorConstrainCursorHarder's callgraph.
+ *
+ * Allocate a set of WorkQueue items to use.
+ */
+
+struct BarrierEventStore {
+    WorkQueueRec wq_item;
+    xXFixesBarrierNotifyEvent ev;
+    Bool in_use;
+};
+
+/* Let's guess that 100 events is enough of a buffer. */
+#define BARRIER_EVENT_QUEUE_SIZE 100
+struct BarrierEventStore barrierEventQueue[BARRIER_EVENT_QUEUE_SIZE];
+
+static void
+CursorWorkQueueDestroyProc (WorkQueuePtr this)
+{
+    struct BarrierEventStore *store;
+    store = container_of (this, struct BarrierEventStore, wq_item);
+
+    store->in_use = FALSE;
+}
+
+static Bool
+CursorSendBarrierEvent (ClientPtr client, pointer eventStore)
+{
+    struct BarrierEventStore *store = (struct BarrierEventStore *)eventStore;
+    WriteEventsToClient (client, 1, (xEvent *)&store->ev);
+
+    return TRUE;
+}
+
+static struct BarrierEventStore *
+CursorFindFreeEventStore (void)
+{
+    for (int i = 0; i < BARRIER_EVENT_QUEUE_SIZE; ++i) {
+	if (!barrierEventQueue[i].in_use) {
+	    return &barrierEventQueue[i];
+	}
+    }
+    return NULL;
+}
+
+static void
+QueueBarrierEvent(CursorScreenPtr cs, struct PointerBarrier *barrier,
+		  int x, int y, int velocity, Bool threshold_exceeded)
+{
+    PointerBarrierEventClientPtr client;
+    struct BarrierEventStore *store;
+    xorg_list_for_each_entry(client, &cs->barrierClients, entry) {
+	store = CursorFindFreeEventStore ();
+	if (store == NULL) {
+	    ErrorF ("[xfixes] Barrier event queue full.  Dropping further events\n");
+	    return;
+	}
+
+	store->in_use = TRUE;
+
+	store->ev.type = XFixesEventBase + XFixesBarrierNotify;
+	store->ev.subtype = threshold_exceeded ? XFixesBarrierThresholdExceededNotify :
+					         XFixesBarrierHitNotify;
+	store->ev.event_id = barrier->barrierEventID;
+	store->ev.barrier = barrier->barrier;
+	store->ev.x = x;
+	store->ev.y = y;
+	store->ev.velocity = velocity;
+	store->ev.timestamp = currentTime.milliseconds;
+
+	if (client->client->swapped) {
+
+	    swapl(&store->ev.event_id);
+	    swapl(&store->ev.barrier);
+	    swaps(&store->ev.x);
+	    swaps(&store->ev.y);
+	    swapl(&store->ev.velocity);
+	    swapl(&store->ev.timestamp);
+	}
+
+	store->wq_item.function = CursorSendBarrierEvent;
+	store->wq_item.client = client->client;
+	store->wq_item.closure = store;
+	store->wq_item.destroyProc = CursorWorkQueueDestroyProc;
+
+	QueueWorkItem (&store->wq_item); 
+    }
+}
+
+static void
+barrier_calculate_velocity_components (int x1, int y1, int x2, int y2,
+				       int *vel_x, int *vel_y)
+{
+    static CARD32 last_timestamp = 0;
+    CARD32 timestamp = GetTimeInMillis();
+    int dx, dy;
+    int dt = timestamp - last_timestamp;
+
+    if (last_timestamp == 0) {
+	/* Not much we can do for the first event */
+	*vel_x = 0;
+	*vel_y = 0;
+	last_timestamp = timestamp;
+	return;
+    }
+
+    /* Lets not divide by zero if we can avoid it */
+    dt = dt > 0 ? dt : 1;
+
+    dx = x2 - x1;
+    dy = y2 - y1;
+
+    *vel_x = abs(dx) * 1000.0 / dt;
+    *vel_y = abs(dy) * 1000.0 / dt;
+
+    last_timestamp = timestamp;
+}
+
 static void
 CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode,
                             int *x, int *y)
@@ -1200,12 +1329,23 @@ CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode,
     if (!xorg_list_is_empty(&cs->barriers) && !IsFloating(dev) &&
         mode == Relative) {
         int ox, oy;
+        int vel_x, vel_y;
         int dir;
         struct PointerBarrier *nearest = NULL;
+        PointerBarrierClientPtr c;
 
         /* where are we coming from */
         miPointerGetPosition(dev, &ox, &oy);
 
+        /* Use the unclamped values, if available.  If not, *x, *y
+         * will have to do.
+         * NOTE: We should never get here with unclamped values unset.
+         */
+        if (unclamped_prex == -1 || unclamped_prey == -1) {
+            unclamped_prex = *x;
+            unclamped_prey = *y;
+        }
+
         /* How this works:
          * Given the origin and the movement vector, get the nearest barrier
          * to the origin that is blocking the movement.
@@ -1213,11 +1353,27 @@ CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode,
          * Then, check from the clamped intersection to the original
          * destination, again finding the nearest barrier and clamping.
          */
-        dir = barrier_get_direction(ox, oy, *x, *y);
+        dir = barrier_get_direction(ox, oy, unclamped_prex, unclamped_prey);
+        barrier_calculate_velocity_components(ox, oy, unclamped_prex, unclamped_prey, &vel_x, &vel_y);
 
-        nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
+        nearest = barrier_find_nearest(cs, dir, ox, oy, unclamped_prex, unclamped_prey);
         if (nearest) {
-            barrier_clamp_to_barrier(nearest, dir, x, y);
+	    int velocity = barrier_is_vertical(nearest) ? vel_x : vel_y;
+	    Bool threshold_exceeded = (nearest->velocity != 0) &&
+				      (velocity > nearest->velocity);
+
+	    if (!nearest->lastHit) {
+		/* This is the start of a new barrier event */
+		nearest->barrierEventID++;
+	    }
+
+	    if ((!threshold_exceeded || nearest->lastHit) &&
+		(nearest->barrierEventID != nearest->releaseEventID)) {
+		barrier_clamp_to_barrier(nearest, dir, x, y);
+		nearest->hit = TRUE;
+	    }
+
+	    QueueBarrierEvent(cs, nearest, *x, *y, velocity, threshold_exceeded);
 
             if (barrier_is_vertical(nearest)) {
                 dir &= ~(BarrierNegativeX | BarrierPositiveX);
@@ -1228,11 +1384,31 @@ CursorConstrainCursorHarder(DeviceIntPtr dev, ScreenPtr screen, int mode,
                 oy = *y;
             }
 
-            nearest = barrier_find_nearest(cs, dir, ox, oy, *x, *y);
+            nearest = barrier_find_nearest(cs, dir, ox, oy, unclamped_prex, unclamped_prey);
             if (nearest) {
-                barrier_clamp_to_barrier(nearest, dir, x, y);
+			velocity = barrier_is_vertical(nearest) ? vel_x : vel_y;
+			threshold_exceeded = (nearest->velocity != 0) &&
+						(velocity > nearest->velocity);
+
+			if (!nearest->lastHit) {
+				/* This is the start of a new barrier event */
+				nearest->barrierEventID++;
+			}
+
+			if ((!threshold_exceeded || nearest->lastHit) &&
+				(nearest->barrierEventID != nearest->releaseEventID)) {
+				barrier_clamp_to_barrier(nearest, dir, x, y);
+				nearest->hit = TRUE;
+			}
+
+			QueueBarrierEvent(cs, nearest, *x, *y, velocity, threshold_exceeded);
             }
         }
+
+	xorg_list_for_each_entry(c, &cs->barriers, entry) {
+	    c->barrier.lastHit = c->barrier.hit;
+	    c->barrier.hit = FALSE;
+	}
     }
 
     if (cs->ConstrainCursorHarder) {
@@ -1247,15 +1423,45 @@ CreatePointerBarrierClient(ScreenPtr screen, ClientPtr client,
                            xXFixesCreatePointerBarrierReq * stuff)
 {
     CursorScreenPtr cs = GetCursorScreen(screen);
-    struct PointerBarrierClient *ret = malloc(sizeof(*ret));
+    struct PointerBarrierClient *ret = calloc(sizeof(*ret), 1);
 
     if (ret) {
         ret->screen = screen;
+        ret->barrier.barrier = stuff->barrier;
         ret->barrier.x1 = min(stuff->x1, stuff->x2);
         ret->barrier.x2 = max(stuff->x1, stuff->x2);
         ret->barrier.y1 = min(stuff->y1, stuff->y2);
         ret->barrier.y2 = max(stuff->y1, stuff->y2);
         ret->barrier.directions = stuff->directions & 0x0f;
+	ret->barrier.velocity = 0;
+	ret->barrier.barrierEventID = 0;
+	if (barrier_is_horizontal(&ret->barrier))
+	    ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
+	if (barrier_is_vertical(&ret->barrier))
+	    ret->barrier.directions &= ~(BarrierPositiveY | BarrierNegativeY);
+	xorg_list_add(&ret->entry, &cs->barriers);
+    }
+
+    return ret;
+}
+
+static struct PointerBarrierClient *
+CreatePointerBarrierVelocityClient(ScreenPtr screen, ClientPtr client,
+			   xXFixesCreatePointerBarrierVelocityReq *stuff)
+{
+    CursorScreenPtr cs = GetCursorScreen(screen);
+    struct PointerBarrierClient *ret = calloc(sizeof(*ret), 1);
+
+    if (ret) {
+	ret->screen = screen;
+	ret->barrier.barrier = stuff->barrier;
+	ret->barrier.x1 = min(stuff->x1, stuff->x2);
+	ret->barrier.x2 = max(stuff->x1, stuff->x2);
+	ret->barrier.y1 = min(stuff->y1, stuff->y2);
+	ret->barrier.y2 = max(stuff->y1, stuff->y2);
+	ret->barrier.directions = stuff->directions & 0x0f;
+	ret->barrier.velocity = stuff->velocity;
+	ret->barrier.barrierEventID = 0;
         if (barrier_is_horizontal(&ret->barrier))
             ret->barrier.directions &= ~(BarrierPositiveX | BarrierNegativeX);
         if (barrier_is_vertical(&ret->barrier))
@@ -1328,6 +1534,68 @@ SProcXFixesCreatePointerBarrier(ClientPtr client)
     return ProcXFixesVector[stuff->xfixesReqType] (client);
 }
 
+int
+ProcXFixesCreatePointerBarrierVelocity (ClientPtr client)
+{
+    int err;
+    WindowPtr pWin;
+    struct PointerBarrierClient *barrier;
+    struct PointerBarrier b;
+    REQUEST (xXFixesCreatePointerBarrierVelocityReq);
+
+    REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierVelocityReq);
+    LEGAL_NEW_RESOURCE(stuff->barrier, client);
+
+    err = dixLookupWindow(&pWin, stuff->window, client, DixReadAccess);
+    if (err != Success) {
+	client->errorValue = stuff->window;
+	return err;
+    }
+
+    /* This sure does need fixing. */
+    if (stuff->num_devices)
+	return BadImplementation;
+
+    b.x1 = stuff->x1;
+    b.x2 = stuff->x2;
+    b.y1 = stuff->y1;
+    b.y2 = stuff->y2;
+
+    if (!barrier_is_horizontal(&b) && !barrier_is_vertical(&b))
+	return BadValue;
+
+    /* no 0-sized barriers */
+    if (barrier_is_horizontal(&b) && barrier_is_vertical(&b))
+	return BadValue;
+
+    if (!(barrier = CreatePointerBarrierVelocityClient(pWin->drawable.pScreen,
+						       client, stuff)))
+	return BadAlloc;
+
+    if (!AddResource(stuff->barrier, PointerBarrierType, &barrier->barrier))
+	return BadAlloc;
+
+    return Success;
+}
+
+int
+SProcXFixesCreatePointerBarrierVelocity (ClientPtr client)
+{
+    REQUEST(xXFixesCreatePointerBarrierVelocityReq);
+
+    swaps(&stuff->length);
+    REQUEST_SIZE_MATCH(xXFixesCreatePointerBarrierReq);
+    swapl(&stuff->barrier);
+    swapl(&stuff->window);
+    swaps(&stuff->x1);
+    swaps(&stuff->y1);
+    swaps(&stuff->x2);
+    swaps(&stuff->y2);
+    swapl(&stuff->directions);
+    swapl(&stuff->velocity);
+    return ProcXFixesVector[stuff->xfixesReqType](client);
+}
+
 static int
 CursorFreeBarrier(void *data, XID id)
 {
@@ -1384,6 +1652,116 @@ SProcXFixesDestroyPointerBarrier(ClientPtr client)
     return ProcXFixesVector[stuff->xfixesReqType] (client);
 }
 
+static int
+CursorFreeBarrierClient(void *data, XID id)
+{
+    PointerBarrierEventClientPtr client = data, c;
+    ScreenPtr screen = client->screen;
+    CursorScreenPtr cs = GetCursorScreen(screen);
+
+    /* find and unlink from the screen private */
+    xorg_list_for_each_entry(c, &cs->barrierClients, entry) {
+	if (c == client) {
+	    xorg_list_del(&c->entry);
+	    break;
+	}
+    }
+
+    free(client);
+    return Success;
+}
+
+static struct PointerBarrierEventClient *
+CreatePointerBarrierEventClient(ScreenPtr screen, ClientPtr client,
+				   xXFixesSelectBarrierInputReq *stuff)
+{
+    CursorScreenPtr cs = GetCursorScreen(screen);
+    struct PointerBarrierEventClient *ret = malloc(sizeof(*ret));
+
+    if (ret) {
+	ret->screen = screen;
+	ret->client = client;
+	ret->eventMask = stuff->eventMask;
+	ret->window = stuff->window;
+	ret->resource = FakeClientID (client->index);
+      xorg_list_add(&ret->entry, &cs->barrierClients);
+    }
+
+    return ret;
+}
+
+int
+ProcXFixesSelectBarrierInput (ClientPtr client)
+{
+    int err;
+    WindowPtr pWin;
+    struct PointerBarrierEventClient *eventClient;
+    REQUEST (xXFixesSelectBarrierInputReq);
+
+    REQUEST_SIZE_MATCH(xXFixesSelectBarrierInputReq);
+
+    err = dixLookupWindow(&pWin	, stuff->window, client, DixReadAccess);
+    if (err != Success) {
+	client->errorValue = stuff->window;
+	return err;
+    }
+
+    if (!(eventClient = CreatePointerBarrierEventClient(pWin->drawable.pScreen,
+							client,
+							stuff)))
+      return BadAlloc;
+
+    if (!AddResource (eventClient->resource, PointerBarrierClientType, eventClient))
+      return BadAlloc;
+
+    return Success;
+}
+
+int
+SProcXFixesSelectBarrierInput (ClientPtr client)
+{
+    REQUEST(xXFixesSelectBarrierInputReq);
+
+    swaps(&stuff->length);
+    REQUEST_SIZE_MATCH(xXFixesSelectBarrierInputReq);
+    swapl(&stuff->window);
+    swapl(&stuff->eventMask);
+    return ProcXFixesVector[stuff->xfixesReqType](client);
+}
+
+int
+ProcXFixesBarrierReleasePointer (ClientPtr client)
+{
+    int err;
+    struct PointerBarrier *barrier;
+    REQUEST (xXFixesBarrierReleasePointerReq);
+    REQUEST_SIZE_MATCH(xXFixesBarrierReleasePointerReq);
+
+    err = dixLookupResourceByType((void **)&barrier, stuff->barrier,
+				  PointerBarrierType, client,
+				  DixReadAccess);
+    if (err != Success) {
+	client->errorValue = stuff->barrier;
+	return err;
+    }
+
+    barrier->releaseEventID = stuff->event_id;
+
+    return Success;
+}
+
+int
+SProcXFixesBarrierReleasePointer (ClientPtr client)
+{
+    REQUEST(xXFixesBarrierReleasePointerReq);
+
+    swaps(&stuff->length);
+    REQUEST_SIZE_MATCH(xXFixesBarrierReleasePointerReq);
+    swapl(&stuff->barrier);
+    swapl(&stuff->event_id);
+    return ProcXFixesVector[stuff->xfixesReqType](client);
+}
+
 Bool
 XFixesCursorInit(void)
 {
@@ -1403,6 +1781,7 @@ XFixesCursorInit(void)
         if (!cs)
             return FALSE;
         xorg_list_init(&cs->barriers);
+        xorg_list_init(&cs->barrierClients);
         Wrap(cs, pScreen, CloseScreen, CursorCloseScreen);
         Wrap(cs, pScreen, DisplayCursor, CursorDisplayCursor);
         Wrap(cs, pScreen, ConstrainCursorHarder, CursorConstrainCursorHarder);
@@ -1417,7 +1796,8 @@ XFixesCursorInit(void)
                                              "XFixesCursorWindow");
     PointerBarrierType = CreateNewResourceType(CursorFreeBarrier,
                                                "XFixesPointerBarrier");
-
+    PointerBarrierClientType = CreateNewResourceType(CursorFreeBarrierClient,
+                                               "XFixesPointerBarrierClient");
     return CursorClientType && CursorHideCountType && CursorWindowType &&
-        PointerBarrierType;
+        PointerBarrierType && PointerBarrierClientType;
 }
diff --git a/xfixes/xfixes.c b/xfixes/xfixes.c
index f80230f..36b93b7 100644
--- a/xfixes/xfixes.c
+++ b/xfixes/xfixes.c
@@ -102,6 +102,7 @@ static const int version_requests[] = {
     X_XFixesExpandRegion,       /* Version 3 */
     X_XFixesShowCursor,         /* Version 4 */
     X_XFixesDestroyPointerBarrier,      /* Version 5 */
+    X_XFixesBarrierReleasePointer, /* Version 6 */
 };
 
 #define NUM_VERSION_REQUESTS	(sizeof (version_requests) / sizeof (version_requests[0]))
@@ -140,7 +141,11 @@ int (*ProcXFixesVector[XFixesNumberRequests]) (ClientPtr) = {
 /*************** Version 4 ****************/
         ProcXFixesHideCursor, ProcXFixesShowCursor,
 /*************** Version 5 ****************/
-ProcXFixesCreatePointerBarrier, ProcXFixesDestroyPointerBarrier,};
+ProcXFixesCreatePointerBarrier, ProcXFixesDestroyPointerBarrier,
+/*************** Version 6 ****************/
+        ProcXFixesCreatePointerBarrierVelocity,
+        ProcXFixesSelectBarrierInput,
+        ProcXFixesBarrierReleasePointer,};
 
 static int
 ProcXFixesDispatch(ClientPtr client)
@@ -200,7 +205,11 @@ static int (*SProcXFixesVector[XFixesNumberRequests]) (ClientPtr) = {
 /*************** Version 4 ****************/
         SProcXFixesHideCursor, SProcXFixesShowCursor,
 /*************** Version 5 ****************/
-SProcXFixesCreatePointerBarrier, SProcXFixesDestroyPointerBarrier,};
+SProcXFixesCreatePointerBarrier, SProcXFixesDestroyPointerBarrier,
+/*************** Version 6 ****************/
+        SProcXFixesCreatePointerBarrierVelocity,
+        SProcXFixesSelectBarrierInput,
+        SProcXFixesBarrierReleasePointer,};
 
 static int
 SProcXFixesDispatch(ClientPtr client)
diff --git a/xfixes/xfixes.h b/xfixes/xfixes.h
index 19af09f..fc7c3c4 100644
--- a/xfixes/xfixes.h
+++ b/xfixes/xfixes.h
@@ -28,6 +28,7 @@
 #define _XFIXES_H_
 
 #include "resource.h"
+#include "list.h"
 
 extern _X_EXPORT RESTYPE RegionResType;
 extern _X_EXPORT RESTYPE PointerBarrierType;
@@ -52,9 +53,25 @@ extern _X_EXPORT int XFixesErrorBase;
 extern _X_EXPORT RegionPtr
  XFixesRegionCopy(RegionPtr pRegion);
 
+typedef struct PointerBarrierEventClient *PointerBarrierEventClientPtr;
+
+struct PointerBarrierEventClient {
+    ScreenPtr screen;
+    ClientPtr client;
+    CARD32    eventMask;
+    XID window;
+    XID resource;
+    struct xorg_list entry;
+};
+
 struct PointerBarrier {
+    XID    barrier;
     CARD16 x1, x2, y1, y2;
     CARD32 directions;
+    CARD32 velocity;
+    CARD32 barrierEventID;
+    CARD32 releaseEventID;
+    Bool   hit, lastHit;
 };
 
 extern int
diff --git a/xfixes/xfixesint.h b/xfixes/xfixesint.h
index 71f88a9..8b204dc 100644
--- a/xfixes/xfixesint.h
+++ b/xfixes/xfixesint.h
@@ -59,6 +59,7 @@
 #include "windowstr.h"
 #include "selection.h"
 #include "xfixes.h"
+#include "list.h"
 
 extern int XFixesEventBase;
 
@@ -295,6 +296,26 @@ int
 int
  SProcXFixesDestroyPointerBarrier(ClientPtr client);
 
+/* Version 6 */
+
+int
+ProcXFixesSelectBarrierInput (ClientPtr client);
+
+int
+SProcXFixesSelectBarrierInput (ClientPtr client);
+
+int
+ProcXFixesCreatePointerBarrierVelocity (ClientPtr client);
+
+int
+SProcXFixesCreatePointerBarrierVelocity (ClientPtr client);
+
+int
+ProcXFixesBarrierReleasePointer (ClientPtr client);
+
+int
+SProcXFixesBarrierReleasePointer (ClientPtr client);
+
 /* Xinerama */
 extern int (*PanoramiXSaveXFixesVector[XFixesNumberRequests]) (ClientPtr);
 void PanoramiXFixesInit(void);
diff --git a/dix/dixutils.c b/dix/dixutils.c
index 9eb5489..521bdce 100644
diff -Nru /dev/null xorg-server/test/gtest/dummy.conf
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ xorg-server/test/gtest/dummy.conf	2012-05-21 18:00:45.996945542 +0200
@@ -0,0 +1,4 @@
+Section "Device"
+    Identifier "Dummy video device"
+    Driver "dummy"
+EndSection
diff -Nru /dev/null xorg-server/test/gtest/Makefile.am
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ xorg-server/test/gtest/Makefile.am	2012-05-21 18:00:45.996545542 +0200
@@ -0,0 +1,27 @@
+check_PROGRAMS = xfixes_barriers
+check_DATA = dummy.conf
+
+TESTS=xfixes_barriers
+
+GTEST_SRC_DIR = /usr/src/gtest
+GTEST_SOURCES = $(GTEST_SRC_DIR)/src/gtest-all.cc
+
+xfixes_barriers_CXXFLAGS = $(AM_CXXFLAGS) \
+	-I$(GTEST_SRC_DIR) \
+	$(XORG_GTEST_CFLAGS) \
+	$(XTEST_CFLAGS) \
+	$(XFIXES_CFLAGS) \
+	-DXORG_BINARY=\"$(top_builddir)/hw/xfree86/Xorg\" \
+	-DXORG_DUMMY_CONF=\"$(abs_srcdir)/dummy.conf\"
+
+xfixes_barriers_LDADD = \
+	$(XFIXES_LIBS) \
+	$(XTEST_LIBS) \
+	$(XORG_GTEST_LIBS) \
+	-lpthread
+
+xfixes_barriers_SOURCES = \
+	xfixes_barriers.cpp
+
+nodist_xfixes_barriers_SOURCES = \
+	$(GTEST_SOURCES)
diff -Nru /dev/null xorg-server/test/gtest/xfixes_barriers.cpp
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ xorg-server/test/gtest/xfixes_barriers.cpp	2012-05-21 18:00:45.995445542 +0200
@@ -0,0 +1,828 @@
+/*
+
+Copyright (c) 2012, Canonical Ltd
+
+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.
+*/
+
+#include <iostream>
+#include <sys/time.h>
+#include <unistd.h>
+#include <gtest/gtest.h>
+#include <xorg/gtest/test.h>
+#include <xorg/gtest/environment.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/XTest.h>
+#include <X11/extensions/Xfixes.h>
+
+
+int main (int argc, char **argv)
+{
+    ::testing::InitGoogleTest (&argc, argv);
+    xorg::testing::Environment* environment = new xorg::testing::Environment ();
+    environment->set_conf_file (XORG_DUMMY_CONF);
+    environment->set_server (XORG_BINARY);
+    testing::AddGlobalTestEnvironment (environment);
+    return RUN_ALL_TESTS ();
+}
+
+class BarrierTest : public xorg::testing::Test {
+    public:
+    ::Display *dpy;
+    static XErrorEvent *lastError;
+    int xtest_eventbase;
+    int xtest_errorbase;
+    int fixes_eventbase;
+    int fixes_errorbase;
+
+    void AssertPointerPosition (int expected_x, int expected_y)
+    {
+        int x, y, unused_int;
+        unsigned int unused_uint;
+        Window unused_win;
+
+        XQueryPointer (Display (), DefaultRootWindow (Display ()),
+                       &unused_win, &unused_win, &x, &y,
+                       &unused_int, &unused_int, &unused_uint);
+
+        ASSERT_TRUE (x == expected_x && y == expected_y) <<
+            "Incorrect pointer position: Expected ("<<
+            expected_x<< ", "<<expected_y<<"), got "<<
+            "("<<x<<", "<<y<<")\n";
+    }
+
+    bool WaitForXEvent (int msTimeout = 1000)
+    {
+        fd_set fds;
+        int xfd = ConnectionNumber (Display ());
+        struct timeval tv;
+        int retval;
+
+        FD_ZERO (&fds);
+        FD_SET (xfd, &fds);
+
+        tv.tv_sec = msTimeout / 1000;
+        tv.tv_usec = (msTimeout % 1000) * 1000;
+
+        retval = select (xfd + 1, &fds, NULL, NULL, &tv);
+
+        EXPECT_NE (-1, retval)<<"Error waiting for X event";
+
+        return retval;
+    }
+
+    protected:
+    virtual void SetUp ()
+    {
+        ASSERT_NO_FATAL_FAILURE (xorg::testing::Test::SetUp());
+
+        dpy = Display ();
+        int major = 2, minor = 2;
+        ASSERT_TRUE (XTestQueryExtension (dpy,
+                                          &xtest_eventbase, &xtest_errorbase,
+                                          &major, &minor));
+        ASSERT_EQ (2, major);
+        ASSERT_TRUE (minor >= 2);
+
+        major = 6;
+        minor = 0;
+        XFixesQueryVersion (dpy, &major, &minor);
+        ASSERT_EQ (6, major);
+        ASSERT_TRUE (minor >= 0);
+
+        ASSERT_TRUE (XFixesQueryExtension (dpy,
+                                           &fixes_eventbase, &fixes_errorbase));
+
+        lastError = new XErrorEvent;
+        XSetErrorHandler (ErrorHandler);
+    }
+
+    private:
+    static int ErrorHandler (::Display *dpy, XErrorEvent *error)
+    {
+        memcpy (lastError, error, sizeof (*lastError));
+        return 0;
+    }
+};
+
+XErrorEvent *BarrierTest::lastError = NULL;
+
+TEST_F (BarrierTest, CreateVerticalBarrierSucceeds)
+{
+    PointerBarrier barrier;
+    barrier = XFixesCreatePointerBarrier (dpy, DefaultRootWindow(dpy),
+                                          100, 0,
+                                          100, 100,
+                                          0,
+                                          0, NULL);
+    ASSERT_NE(None, barrier);
+}
+
+TEST_F (BarrierTest, CreateHorizontalBarrierSucceds)
+{
+    PointerBarrier barrier;
+    barrier = XFixesCreatePointerBarrier (dpy, DefaultRootWindow(dpy),
+                                          100, 100,
+                                          200, 100,
+                                          0,
+                                          0, NULL);
+    ASSERT_NE(None, barrier);
+}
+
+TEST_F (BarrierTest, CreateNonAxisAlignedBarrierFails)
+{
+    XFixesCreatePointerBarrier (dpy, DefaultRootWindow(dpy),
+                                0, 0,
+                                100, 100,
+                                0,
+                                0, NULL);
+    XSync (Display (), false);
+    ASSERT_EQ(BadValue, lastError->error_code);
+}
+
+TEST_F (BarrierTest, VerticalBidirectionalBarrierBlocksRelativeMotion)
+{
+    int barrier_x = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                0, 0, NULL);
+
+    int x = 200, y = 100, dx = -200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion should block on barrier
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x, y));
+}
+
+TEST_F (BarrierTest, VerticalPositiveXBarrierBlocksMotion)
+{
+    int barrier_x = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                BarrierPositiveX, 0, NULL);
+    int x = 200, y = 100, dx = -200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in -ve X direction should block on barrier
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x, y + dy));
+
+    x = 0, y = 100, dx = 200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in +ve X direction should ignore barrier
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, VerticalNegativeXBarrierBlocksMotion)
+{
+    int barrier_x = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                BarrierNegativeX,
+                                0, NULL);
+
+    int x = 200, y = 100, dx = -200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in -ve X direction should ignore barrier
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+
+    x = 0, y = 100, dx = 200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in +ve X direction should block on barrier
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x - 1, y + dy));
+}
+
+TEST_F (BarrierTest, HorizontalBidirectionalBarrierBlocksRelativeMotion)
+{
+    int barrier_y = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                0,   barrier_y,
+                                300, barrier_y,
+                                0, 0, NULL);
+
+    int x = 200, y = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in +ve Y direction should block on barrier
+    int dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y - 1));
+
+    x = 100, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in -ve Y direction should block on barrier
+    dx = 0, dy = -200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y));
+}
+
+TEST_F (BarrierTest, HorizontalPositiveYBarrierBlocksMotion)
+{
+    int barrier_y = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                0,   barrier_y,
+                                300, barrier_y,
+                                BarrierPositiveY, 0, NULL);
+
+    int x = 200, y = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in +ve Y direction should ignore barrier
+    int dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+
+    x = 100, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in -ve Y direction should block on barrier
+    dx = 0, dy = -200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y));
+}
+
+TEST_F (BarrierTest, HorizontalNegativeYBarrierBlocksMotion)
+{
+    int barrier_y = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                0,   barrier_y,
+                                300, barrier_y,
+                                BarrierNegativeY, 0, NULL);
+
+    int x = 200, y = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in +ve Y direction should block on barrier
+    int dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y - 1));
+
+    x = 100, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Relative motion in -ve Y direction should ignore barrier
+    dx = 0, dy = -200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, DestroyPointerBarrierSucceeds)
+{
+    int barrier_x = 100;
+    PointerBarrier barrier;
+    barrier = XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                          barrier_x, 0,
+                                          barrier_x, 300,
+                                          0, 0, NULL);
+
+    int x = 0, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Check that the barrier exists before we destroy it.
+    int dx = 200, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x - 1, y + dy));
+
+    // Destroy the barrier...
+    XFixesDestroyPointerBarrier (Display (), barrier);
+
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // There should be no barrier to block this.
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, BarrierIgnoresNonsensicalDirections)
+{
+    int barrier_x = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                BarrierPositiveY | BarrierNegativeY,
+                                0, NULL);
+
+    int x = 200, y = 100;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    int dx = -200, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x, y + dy));
+
+    int barrier_y = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                               0,   barrier_y,
+                               400, barrier_y,
+                               BarrierPositiveX | BarrierNegativeX,
+                               0, NULL);
+
+    x = 100, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = 0, dy = -200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y));
+}
+
+TEST_F (BarrierTest, VerticalBarrierEdges)
+{
+    int barrier_x = 300, barrier_y1 = 300 , barrier_y2 = 500;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, barrier_y1,
+                                barrier_x, barrier_y2,
+                                0, 0, NULL);
+
+    int x = barrier_x + 100, y = barrier_y1 - 1;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should take us past the top of the barrier...
+    int dx = -200, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+
+    x = barrier_x + 100, y = barrier_y1;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should hit the top of the barrier...
+    dx = -200, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x, y + dy));
+
+    x = barrier_x + 100, y = barrier_y2;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should hit the bottom of the barrier...
+    dx = -200, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x, y + dy));
+
+    x = barrier_x + 100, y = barrier_y2 + 1;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should take us past the bottom of the barrier...
+    dx = -200, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, HorizontalBarrierEdges)
+{
+    int barrier_x1 = 200, barrier_x2 = 500, barrier_y = 300;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x1, barrier_y,
+                                barrier_x2, barrier_y,
+                                0, 0, NULL);
+
+    int x = barrier_x1 - 1, y = barrier_y - 100;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should take us past the left edge of the barrier...
+    int dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+
+    x = barrier_x1, y = barrier_y - 100;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should hit the top of the barrier...
+    dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, barrier_y - 1));
+
+    x = barrier_x2, y = barrier_y - 100;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should hit the bottom of the barrier...
+    dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y - 1));
+
+    x = barrier_x2 + 1, y = barrier_y - 100;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    // Motion should take us past the bottom of the barrier...
+    dx = 0, dy = 200;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, CornerBlocksMotion)
+{
+    int corner_x, corner_y;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                corner_x, corner_y,
+                                corner_x, corner_y + 300,
+                                0, 0, NULL);
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                corner_x, corner_y,
+                                corner_x + 300, corner_y,
+                                0, 0, NULL);
+
+    int x = corner_x + 100, y = corner_y + 100;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    XTestFakeRelativeMotionEvent (Display (), -200, -200, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (corner_x, corner_y));
+}
+
+TEST_F (BarrierTest, VerticalBarrierWithAdjacentStart)
+{
+    int barrier_x = 350;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 100,
+                                barrier_x, 300,
+                                0, 0, NULL);
+
+    int x = barrier_x, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    int dx = -10, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x, y + dy));
+
+    x = barrier_x, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = 10, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+
+    x = barrier_x - 1, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = 10, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (barrier_x - 1, y + dy));
+
+    x = barrier_x - 1, y = 200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = -10, dy = 0;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, HorizontalBarrierWithAdjacentStart)
+{
+    int barrier_y = 300;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                100, barrier_y,
+                                400, barrier_y,
+                                0, 0, NULL);
+
+    int x = 240, y = barrier_y;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    int dx = 0, dy = -10;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y));
+
+    x = 240, y = barrier_y;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = 0, dy = 10;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+
+    x = 240, y = barrier_y - 1;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = 0, dy = 10;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, barrier_y - 1));
+
+    x = 240, y = barrier_y - 1;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    dx = 0, dy = -10;
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x + dx, y + dy));
+}
+
+TEST_F (BarrierTest, BarrierNotifyEventFires)
+{
+    int barrier_y = 300;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                100, barrier_y,
+                                400, barrier_y,
+                                0, 0, NULL);
+
+    XFixesSelectBarrierInput (Display (), DefaultRootWindow (Display ()),
+                              XFixesBarrierHitNotifyMask);
+
+    int x = 240, y = barrier_y + 50;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    XTestFakeRelativeMotionEvent (Display (), 0, -100, 0);
+
+    XFlush (Display ());
+    ASSERT_TRUE (WaitForXEvent ())<<"Timed out waiting to receive X event";
+    while (XPending (Display ())) {
+        XEvent e;
+        XNextEvent (Display (), &e);
+        switch (e.xany.type - fixes_eventbase) {
+            case XFixesBarrierNotify:
+                return;
+        }
+    }
+    FAIL () << "Failed to recieve BarrierNotify event";
+}
+
+TEST_F (BarrierTest, RecieveOneNotifyEventPerHit)
+{
+    int barrier_x = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                0, 0, NULL);
+
+    XFixesSelectBarrierInput (Display (), DefaultRootWindow (Display ()),
+                              XFixesBarrierHitNotifyMask);
+
+    int x = 200, y = 100, dx = -200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    /* Generate 5 barrier events */
+    for (int i = 0; i < 5; ++i) {
+        XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    }
+
+    int barrierEventCount = 0;
+    XFlush (Display ());
+    ASSERT_TRUE (WaitForXEvent ())<<"Timed out waiting to receive X event";
+    while (XPending (Display ())) {
+        XEvent e;
+        XNextEvent (Display (), &e);
+        switch (e.xany.type - fixes_eventbase) {
+            case XFixesBarrierNotify:
+                barrierEventCount++;
+                break;
+        }
+    }
+    ASSERT_EQ (5, barrierEventCount);
+}
+
+TEST_F (BarrierTest, BarrierEventHasNonZeroVelocity)
+{
+    int barrier_x = 100;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                0, 0, NULL);
+
+    XFixesSelectBarrierInput (Display (), DefaultRootWindow (Display ()),
+                              XFixesBarrierHitNotifyMask);
+
+    int x = 200, y = 100, dx = -200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    /* One relative event to ensure the server has a non-zero
+     * last-event-time */
+    XTestFakeRelativeMotionEvent (Display (), 10, 10, 0);
+    /* Run the pointer into the barrier */
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    XFlush (Display ());
+    ASSERT_TRUE (WaitForXEvent ())<<"Timed out waiting to receive X event";
+    while (XPending (Display ())) {
+        XEvent e;
+        XNextEvent (Display (), &e);
+        switch (e.xany.type - fixes_eventbase) {
+            case XFixesBarrierNotify:
+                XFixesBarrierNotifyEvent *notify = (XFixesBarrierNotifyEvent *)&e;
+                ASSERT_LT (0, notify->velocity);
+                return;
+        }
+    }
+    FAIL () << "Failed to receive barrier event";
+}
+
+TEST_F (BarrierTest, ScreenEdgeVerticalBarrierEventHasNonZeroVelocity)
+{
+    int barrier_x = 0;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                0, 0, NULL);
+
+    XFixesSelectBarrierInput (Display (), DefaultRootWindow (Display ()),
+                              XFixesBarrierHitNotifyMask);
+
+    int x = 100, y = 100, dx = -200, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    /* One relative event to ensure the server has a non-zero
+     * last-event-time */
+    XTestFakeRelativeMotionEvent (Display (), 10, 10, 0);
+    /* Run the pointer into the barrier */
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    XFlush (Display ());
+    ASSERT_TRUE (WaitForXEvent ())<<"Timed out waiting to receive X event";
+    while (XPending (Display ())) {
+        XEvent e;
+        XNextEvent (Display (), &e);
+        switch (e.xany.type - fixes_eventbase) {
+            case XFixesBarrierNotify:
+                XFixesBarrierNotifyEvent *notify = (XFixesBarrierNotifyEvent *)&e;
+                ASSERT_LT (0, notify->velocity);
+                return;
+        }
+    }
+    FAIL () << "Failed to receive barrier event";
+}
+
+TEST_F (BarrierTest, ScreenEdgeHorizontalBarrierEventHasNonZeroVelocity)
+{
+    int barrier_y = 0;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                0, barrier_y,
+                                300, barrier_y,
+                                0, 0, NULL);
+
+    XFixesSelectBarrierInput (Display (), DefaultRootWindow (Display ()),
+                              XFixesBarrierHitNotifyMask);
+
+    int x = 100, y = 100, dx = 0, dy = -200;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    /* One relative event to ensure the server has a non-zero
+     * last-event-time */
+    XTestFakeRelativeMotionEvent (Display (), 10, 10, 0);
+    /* Run the pointer into the barrier */
+    XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+
+    XFlush (Display ());
+    ASSERT_TRUE (WaitForXEvent ())<<"Timed out waiting to receive X event";
+    while (XPending (Display ())) {
+        XEvent e;
+        XNextEvent (Display (), &e);
+        switch (e.xany.type - fixes_eventbase) {
+            case XFixesBarrierNotify:
+                XFixesBarrierNotifyEvent *notify = (XFixesBarrierNotifyEvent *)&e;
+                ASSERT_LT (0, notify->velocity);
+                return;
+        }
+    }
+    FAIL () << "Failed to receive barrier event";
+}
+
+TEST_F (BarrierTest, ReceiveOneBarrierEventPerHitOnScreenEdge)
+{
+    int barrier_x = 0;
+    XFixesCreatePointerBarrier (Display (), DefaultRootWindow (Display ()),
+                                barrier_x, 0,
+                                barrier_x, 300,
+                                0, 0, NULL);
+
+    XFixesSelectBarrierInput (Display (), DefaultRootWindow (Display ()),
+                              XFixesBarrierHitNotifyMask);
+
+    int x = 20, y = 100, dx = -40, dy = 0;
+    XTestFakeMotionEvent (Display (), DefaultScreen (Display ()),
+                          x, y, 0);
+    ASSERT_NO_FATAL_FAILURE (AssertPointerPosition (x, y));
+
+    /* Generate 5 barrier events */
+    for (int i = 0; i < 5; ++i) {
+        XTestFakeRelativeMotionEvent (Display (), dx, dy, 0);
+    }
+
+    int barrierEventCount = 0;
+    XFlush (Display ());
+    ASSERT_TRUE (WaitForXEvent ())<<"Timed out waiting to receive X event";
+    while (XPending (Display ())) {
+        XEvent e;
+        XNextEvent (Display (), &e);
+        switch (e.xany.type - fixes_eventbase) {
+            case XFixesBarrierNotify:
+                barrierEventCount++;
+                break;
+        }
+    }
+    ASSERT_EQ (5, barrierEventCount);
+}

Follow ups

References