[PATCH (v5) xserver 7/8] Input: Add initial multitouch support from Xi 2.1

Daniel Stone daniel at fooishbar.org
Wed Jan 19 15:11:49 PST 2011


Xi 2.1 adds TouchClasses to devices, as well as TouchBegin, TouchMotion
and TouchEnd events, to allow support for multiple touchpoints on a
single device.

Signed-off-by: Daniel Stone <daniel at fooishbar.org>
Co-authored-by: Chase Douglas <chase.douglas at canonical.com>
---

v5: Updated for new version of the spec including separate TouchOwnership and
    TouchMotionUnowned events.
    Handle assigning monotonic touch IDs in the server, rather than relying
    on the DDX to do it.
    Split FindTouchPoint out into FindTouchPointByClientID for client-visible
    IDs, and FindTouchPointByDDXID for DDX-visible IDs.
    Make GetTouchEvents take Begin/Motion/End types explicitly, rather than
    heuristically detecting Begin - also the same for xf86PostTouchEvent.
    Accepting and rejecting grabs and removing listeners now handled in
    ProcessTouchOwnershipEvent, rather than each caller doing it on their
    own.
    DeliverTouchGrabEvents and DeliverTouchSelectionEvents merged into
    DeliverTouchEvents, and given the ability to send to a specific resource
    only.
    num_grabs changed to reflect only grabs which have not yet accepted or
    rejected a touch, rather than including accepted grabs.
    TouchPointInfoRec enlarged to contain num_valuators.
    Use new DeepestSpriteWin macro.
    Call TouchListenerGone for removed/disappeared selections, as well as
    grabs.
    Support max_touches == 0 by enlarging the number of touches as required.

 Xi/exevents.c                  |  582 ++++++++++++++++++++++++++++++++++++++++
 Xi/extinit.c                   |   29 ++-
 Xi/xiallowev.c                 |   92 +++++++
 Xi/xiallowev.h                 |    2 +
 Xi/xipassivegrab.c             |   18 ++-
 Xi/xiquerydevice.c             |   95 +++++++
 Xi/xiquerydevice.h             |    3 +
 Xi/xiselectev.c                |   44 +++
 configure.ac                   |    2 +-
 dix/devices.c                  |  137 ++++++++++
 dix/eventconvert.c             |   44 +++
 dix/events.c                   |    3 +-
 dix/getevents.c                |  137 ++++++++++
 dix/grabs.c                    |   74 +++++-
 dix/inpututils.c               |  129 +++++++++
 dix/window.c                   |    9 +-
 hw/xfree86/common/xf86Xinput.c |   45 +++
 hw/xfree86/common/xf86Xinput.h |    5 +
 include/events.h               |    1 +
 include/eventstr.h             |   25 ++
 include/exevents.h             |   20 ++
 include/input.h                |   37 +++
 include/inputstr.h             |   42 +++-
 include/protocol-versions.h    |    2 +-
 mi/mieq.c                      |    7 +
 25 files changed, 1572 insertions(+), 12 deletions(-)

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 327873e..b3b0782 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -44,6 +44,31 @@ SOFTWARE.
 
 ********************************************************/
 
+/*
+ * Copyright © 2010 Collabora 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.
+ *
+ * Author: Daniel Stone <daniel at fooishbar.org>
+ */
+
 /********************************************************************
  *
  *  Routines to register and initialize extension input devices.
@@ -77,6 +102,7 @@ SOFTWARE.
 #include "xiquerydevice.h" /* For List*Info */
 #include "eventconvert.h"
 #include "eventstr.h"
+#include "xserver-properties.h"
 
 #include <X11/extensions/XKBproto.h>
 #include "xkbsrv.h"
@@ -926,6 +952,470 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
 }
 
 /**
+ * Ensure a window trace is present in ti->sprite, constructing one for
+ * TouchBegin events.
+ */
+static Bool
+EnsureTouchSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
+                  InternalEvent *ev)
+{
+    TouchClassPtr t = sourcedev->touch;
+    SpritePtr sprite = &ti->sprite;
+    int i;
+
+    /* We may not have a sprite if there are no applicable grabs or
+     * event selections, or if they've disappeared, or if all the grab
+     * owners have rejected the touch.  Don't bother delivering motion
+     * events if not, but TouchEnd events still need to be processed so
+     * we can call FinishTouchPoint and release it for later use. */
+    if (ev->any.type == ET_TouchEnd)
+        return TRUE;
+    else if (ev->any.type != ET_TouchBegin)
+        return (sprite->spriteTraceGood > 0);
+
+    if (t->mode == XIDirectTouch)
+    {
+        /* Focus immediately under the touchpoint in direct touch mode.
+         * XXX: Do we need to handle crossing screens here? */
+        sprite->spriteTrace[0] =
+            sourcedev->spriteInfo->sprite->hotPhys.pScreen->root;
+        XYToWindow(sprite, ev->device_event.root_x, ev->device_event.root_y);
+    }
+    else
+    {
+        WindowPtr *trace;
+        SpritePtr srcsprite;
+
+        /* Find and reuse an existing touch's sprite if possible, else use the
+         * device's sprite. */
+        for (i = 0; i < t->num_touches; i++)
+            if (t->touches[i].sprite.spriteTraceGood > 0)
+                break;
+        if (i < t->num_touches)
+            srcsprite = &t->touches[i].sprite;
+        else if (sourcedev->spriteInfo->sprite)
+            srcsprite = sourcedev->spriteInfo->sprite;
+        else
+            return FALSE;
+
+        if (srcsprite->spriteTraceGood > sprite->spriteTraceSize)
+        {
+            trace = realloc(sprite->spriteTrace,
+                            srcsprite->spriteTraceSize * sizeof(*trace));
+            if (!trace)
+            {
+                sprite->spriteTraceGood = 0;
+                return FALSE;
+            }
+            sprite->spriteTrace = trace;
+            sprite->spriteTraceSize = srcsprite->spriteTraceGood;
+        }
+        memcpy(sprite->spriteTrace, srcsprite->spriteTrace,
+               srcsprite->spriteTraceGood * sizeof(*trace));
+        sprite->spriteTraceGood = srcsprite->spriteTraceGood;
+    }
+
+    if (sprite->spriteTraceGood <= 0)
+        return FALSE;
+
+    /* Mark which grabs/event selections we're delivering to: max one grab per
+     * window plus the bottom-most event selection. */
+    ti->listeners = calloc(sprite->spriteTraceGood + 1, sizeof(*ti->listeners));
+    if (!ti->listeners)
+    {
+        sprite->spriteTraceGood = 0;
+        return FALSE;
+    }
+    ti->num_listeners = 0;
+
+    return TRUE;
+}
+
+/**
+ * @returns TRUE if the specified grab or selection is currently listening to
+ * events from ti's touch sequence.
+ */
+static Bool
+TouchResourceIsListening(TouchPointInfoPtr ti, XID resource)
+{
+    int i;
+
+    for (i = 0; i < ti->num_listeners; i++)
+    {
+        if (ti->listeners[i] == resource)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+/**
+ * @returns TRUE if the specified grab or selection is the current owner of
+ * the touch sequence.
+ */
+static Bool
+TouchResourceIsOwner(TouchPointInfoPtr ti, XID resource)
+{
+    return (ti->listeners[0] == resource);
+}
+
+/**
+ * Attempts to deliver a touch event to the given client.
+ */
+static Bool
+DeliverOneTouchEvent(ClientPtr client, DeviceIntPtr dev, TouchPointInfoPtr ti,
+                     WindowPtr win, InternalEvent *ev)
+{
+    int err;
+    xEvent *xi2;
+    Mask filter;
+    Window child = DeepestSpriteWin(&ti->sprite)->drawable.id;
+
+    /* If we fail here, we're going to leave a client hanging. */
+    err = EventToXI2(ev, &xi2);
+    if (err != Success)
+        FatalError("[Xi] %s: XI2 conversion failed in DeliverOneTouchEvent"
+                   " (%d)\n", dev->name, err);
+
+    FixUpEventFromWindow(&ti->sprite, xi2, win, child, FALSE);
+    filter = GetEventFilter(dev, xi2);
+    if (XaceHook(XACE_RECEIVE_ACCESS, client, win, xi2, 1) != Success)
+        return FALSE;
+    err = TryClientEvents(client, dev, xi2, 1, filter, filter, NullGrab);
+    free(xi2);
+
+    /* Returning the value from TryClientEvents isn't useful, since all our
+     * resource-gone cleanups will update the delivery list anyway. */
+    return TRUE;
+}
+
+/**
+ * Delivers a touch event to all interested clients.  For TouchBegin events,
+ * will update ti->listeners, ti->num_listeners, and ti->num_grabs.
+ * May also mutate ev (type and flags) upon successful delivery.  If
+ * @resource is non-zero, will only attempt delivery to the owner of that
+ * resource.
+ */
+static void
+DeliverTouchEvents(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
+                   InternalEvent *ev, XID resource)
+{
+    SpritePtr sprite = &ti->sprite;
+    DeviceIntPtr masterdev = sourcedev->u.master;
+    DeviceIntPtr deliverdev;
+    GrabPtr grab;
+    WindowPtr win;
+    OtherInputMasks *inputMasks;
+    InputClients *iclients;
+    uint16_t evtype = GetXI2Type(ev);
+    int i;
+    Bool ret;
+
+    /* First, deliver to all grabbing clients, going from the root window down
+     * to the deepest child window. */
+    for (i = 0; i < sprite->spriteTraceGood; i++)
+    {
+        win = sprite->spriteTrace[i];
+
+        /* Find a device to deliver to: first check for grabs on the specific
+         * SD or XIAllDevices, then try for the MD/XIAllMasterDevices if we're
+         * not floating. */
+        deliverdev = sourcedev;
+        grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
+        if (!grab && masterdev)
+        {
+            deliverdev = masterdev;
+            grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
+        }
+        if (!grab)
+            continue;
+
+        /* ti->listeners is populated at TouchBegin time with the list of
+         * clients who received the TouchBegin event, and never added to.
+         * For non-TouchBegin events, check against that list, and bin the
+         * delivery we can't find it.  Also skip delivery if a particular
+         * resource was specified, and this isn't it. */
+        if (ev->any.type != ET_TouchBegin &&
+            !TouchResourceIsListening(ti, grab->resource))
+            continue;
+        if (resource != 0 && grab->resource != resource)
+            continue;
+
+        /* Don't tell the owner that the touch is ending because the owner has
+         * accepted it. */
+        if (TouchResourceIsOwner(ti, grab->resource) &&
+            ev->any.type == ET_TouchEnd &&
+            ev->device_event.flags == XITouchOwnerAccepted)
+            continue;
+
+        if (ev->any.type == ET_TouchOwnership)
+            ev->touch_ownership_event.deviceid = deliverdev->id;
+        else
+            ev->device_event.deviceid = deliverdev->id;
+
+        ret = DeliverOneTouchEvent(rClient(grab), deliverdev, ti, win, ev);
+
+        /* If we've made a TouchBegin delivery, update ti->listeners to
+         * add our resource.  If we've made an owned TouchMotion delivery,
+         * change it to TouchMotionUnowned, because there can only be one
+         * owner.  If we were asked to deliver to a specific resource and did,
+         * get out. */
+        if (ret && ev->any.type == ET_TouchBegin)
+        {
+            ti->listeners[ti->num_listeners++] = grab->resource;
+            ti->num_grabs++;
+        }
+        else if (ev->any.type == ET_TouchMotion)
+        {
+            ev->any.type = ET_TouchMotionUnowned;
+        }
+        else if (resource != 0)
+        {
+            return;
+        }
+    }
+
+    /* Now deliver to the first client with an applicable event selection,
+     * going from deepest child window back up to the root window. */
+    for (i = sprite->spriteTraceGood - 1; i >= 0; i--)
+    {
+        /* First check whether anyone has selected for touch events on this
+         * window at all. */
+        win = sprite->spriteTrace[i];
+        inputMasks = wOtherInputMasks(win);
+        if (!inputMasks)
+            continue;
+        iclients = inputMasks->inputClients;
+        if (!iclients)
+            continue;
+        if (!BitIsOn(inputMasks->xi2mask[sourcedev->id], evtype) &&
+            !BitIsOn(inputMasks->xi2mask[XIAllDevices], evtype) &&
+            !(masterdev &&
+              (BitIsOn(inputMasks->xi2mask[masterdev->id], evtype) ||
+               BitIsOn(inputMasks->xi2mask[XIAllMasterDevices], evtype))))
+            continue;
+
+        for (; iclients; iclients = iclients->next)
+        {
+            /* Now find the particular client who selected for touch events. */
+            if (BitIsOn(iclients->xi2mask[sourcedev->id], evtype) ||
+                BitIsOn(iclients->xi2mask[XIAllDevices], evtype))
+                deliverdev = sourcedev;
+            else if (masterdev &&
+                     (BitIsOn(iclients->xi2mask[masterdev->id], evtype) ||
+                      BitIsOn(iclients->xi2mask[XIAllMasterDevices], evtype)))
+                deliverdev = masterdev;
+            else
+                continue;
+
+            /* See comment in the matching branch above. */
+            if (ev->any.type != ET_TouchBegin &&
+                !TouchResourceIsListening(ti, iclients->resource))
+                continue;
+            if (resource && iclients->resource != resource)
+                continue;
+
+            if (ev->any.type == ET_TouchOwnership)
+                ev->touch_ownership_event.deviceid = deliverdev->id;
+            else
+                ev->device_event.deviceid = deliverdev->id;
+            ret = DeliverOneTouchEvent(rClient(iclients), deliverdev, ti, win,
+                                       ev);
+
+            /* As only one event selection can receive events, bail as soon as
+             * we find one.  If we couldn't deliver a TouchBegin to a client,
+             * assume we'll never be able to and punt the event up the tree. */
+            if (ev->any.type == ET_TouchBegin)
+            {
+                if (ret)
+                    ti->listeners[ti->num_listeners++] = iclients->resource;
+                else
+                    continue;
+            }
+
+            return;
+        }
+    }
+}
+
+/**
+ * Processes a TouchOwnership event, indicating a grab has accepted the touch
+ * it currently owns, or a grab or selection has been removed.  Will generate
+ * and send TouchEnd events to all clients removed from the delivery list, as
+ * well as possibly sending the new TouchOwnership event.  May end the
+ * touchpoint if it is pending finish.
+ */
+static void
+ProcessTouchOwnershipEvent(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
+                           TouchOwnershipEvent *ev)
+{
+    EventList *tel = InitEventList(GetMaximumEventsNum());
+    InternalEvent *tee;
+    ValuatorMask *mask = valuator_mask_new(0);
+    void *grab;
+    int nev, i;
+
+    if (ev->reason == XITouchOwnerReject)
+    {
+        Bool was_owner = (ev->resource == ti->listeners[0]);
+
+        /* Send a TouchEnd event to the resource being removed. */
+        nev = GetTouchEvents(tel, sourcedev, ti, XI_TouchEnd, 0, mask);
+        for (i = 0; i < nev; i++)
+        {
+            tee = (InternalEvent *) ((tel + i)->event);
+            DeliverTouchEvents(sourcedev, ti, tee, ev->resource);
+        }
+
+        /* If there are no other listeners left, then don't bother sending an
+         * ownership change event to no-one; if the touchpoint is pending
+         * finish, then we can just kill it now. */
+        if (ti->num_listeners == 1)
+        {
+            if (ti->pending_finish)
+                EndTouchPoint(sourcedev, ti);
+            return;
+        }
+
+        /* Remove the resource from the listener list, updating
+         * ti->num_listeners, as well as ti->num_grabs if it was a grab. */
+        for (i = 0; i < ti->num_listeners; i++)
+        {
+            if (ti->listeners[i] == ev->resource)
+            {
+                for (; i < ti->num_listeners - 1; i++)
+                    ti->listeners[i] = ti->listeners[i + 1];
+                ti->num_listeners--;
+                if (dixLookupResourceByType(&grab, ev->resource, RT_PASSIVEGRAB,
+                                            serverClient,
+                                            DixGetAttrAccess) == Success)
+                    ti->num_grabs--;
+                break;
+            }
+        }
+
+        /* If the current owner was removed, deliver the TouchOwnership event
+         * to the new owner. */
+        if (was_owner)
+        {
+            DeliverTouchEvents(sourcedev, ti, (InternalEvent *) ev,
+                               ti->listeners[0]);
+
+            /* If we've just removed the last grab and the touch has physically
+             * ended, send a TouchEnd event too and finalise the touch. */
+            if (ti->num_listeners == 1 && ti->num_grabs == 0 &&
+                ti->pending_finish)
+            {
+                nev = GetTouchEvents(tel, sourcedev, ti, XI_TouchEnd, 0, mask);
+                for (i = 0; i < nev; i++)
+                {
+                    tee = (InternalEvent *) ((tel + i)->event);
+                    DeliverTouchEvents(sourcedev, ti, tee, 0);
+                }
+                EndTouchPoint(sourcedev, ti);
+                return;
+            }
+        }
+    }
+    else {
+        /* The touch owner has accepted the touch.  Send TouchEnd events to
+         * everyone else, and truncate the list of listeners. */
+        nev = GetTouchEvents(tel, sourcedev, ti, XI_TouchEnd,
+                            XITouchOwnerAccepted, mask);
+        for (i = 0; i < nev; i++)
+        {
+            tee = (InternalEvent *) ((tel + i)->event);
+            DeliverTouchEvents(sourcedev, ti, tee, 0);
+        }
+
+        ti->num_listeners = 1;
+        ti->num_grabs = 0;
+
+        /* If we were pending finish, we can now end the touch. */
+        if (ti->pending_finish)
+        {
+            nev = GetTouchEvents(tel, sourcedev, ti, XI_TouchEnd, 0, mask);
+            for (i = 0; i < nev; i++)
+            {
+                tee = (InternalEvent *) ((tel + i)->event);
+                DeliverTouchEvents(sourcedev, ti, tee, 0);
+            }
+            EndTouchPoint(sourcedev, ti);
+        }
+    }
+}
+
+/**
+ * Processes and delivers a TouchBegin, TouchMotion, TouchMotionOwned, or a
+ * TouchEnd event.
+ *
+ * Due to having rather different delivery semantics (see the Xi 2.1 protocol
+ * spec for more information), this implements its own grab and event-selection
+ * delivery logic.
+ */
+static void
+ProcessTouchEvent(InternalEvent *ev, DeviceIntPtr sourcedev)
+{
+    DeviceIntPtr masterdev = NULL;
+    TouchClassPtr t = sourcedev->touch;
+    TouchPointInfoPtr ti;
+    SpritePtr sprite;
+    uint32_t touchid;
+
+    /* We handle deliveries to MDs through the SD, rather than copying
+     * the event and processing it twice. */
+    if (IsMaster(sourcedev))
+        return;
+    if (sourcedev->u.master)
+        masterdev = sourcedev->u.master;
+
+    if (!t)
+        return;
+
+    if (ev->any.type == ET_TouchOwnership)
+        touchid = ev->touch_ownership_event.touchid;
+    else
+        touchid = ev->device_event.detail.touch;
+    ti = FindTouchPointByClientID(sourcedev, touchid);
+    if (!ti)
+    {
+        DebugF("[Xi] %s: Received event for inactive touchpoint %d\n",
+               sourcedev->name, touchid);
+        return;
+    }
+    sprite = &ti->sprite;
+
+    /* We only send TouchEnd events when no client will ever receive any more
+     * events from that touch stream.  If there are still open grabs, just
+     * set the PendingFinish flag until they've all accepted or rejected the
+     * touch. */
+    if (ev->any.type == ET_TouchEnd && ti->num_grabs > 0)
+    {
+        ev->any.type = ET_TouchMotion;
+        ev->device_event.flags |= XITouchPendingFinish;
+        ti->pending_finish = TRUE;
+    }
+
+    /* Make sure we have a valid window trace for event delivery; must be
+     * called after event type mutation. */
+    if (!EnsureTouchSprite(sourcedev, ti, ev))
+        return;
+
+    /* TouchOwnership events are handled separately from the rest, as they
+     * have more complex semantics. */
+    if (ev->any.type == ET_TouchOwnership)
+    {
+        ProcessTouchOwnershipEvent(sourcedev, ti, &ev->touch_ownership_event);
+    }
+    else
+    {
+        DeliverTouchEvents(sourcedev, ti, (InternalEvent *) ev, 0);
+        if (ev->any.type == ET_TouchEnd)
+            EndTouchPoint(sourcedev, ti);
+    }
+}
+
+/**
  * Main device event processing function.
  * Called from when processing the events from the event queue.
  *
@@ -954,6 +1444,14 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
     {
         ProcessRawEvent(&ev->raw_event, device);
         return;
+    } else if (ev->any.type == ET_TouchBegin ||
+               ev->any.type == ET_TouchMotion ||
+               ev->any.type == ET_TouchMotionUnowned ||
+               ev->any.type == ET_TouchOwnership ||
+               ev->any.type == ET_TouchEnd)
+    {
+        ProcessTouchEvent(ev, device);
+        return;
     }
 
     if (IsPointerDevice(device))
@@ -1152,6 +1650,30 @@ InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int
         dev->proximity->in_proximity = FALSE;
 }
 
+void
+InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
+                            int maxval, int resolution)
+{
+    TouchAxisInfoPtr ax;
+
+    if (!dev || !dev->touch || minval > maxval)
+        return;
+    if (axnum >= dev->touch->num_axes)
+        return;
+
+    ax = dev->touch->axes + axnum;
+
+    ax->min_value = minval;
+    ax->max_value = maxval;
+    ax->resolution = resolution;
+    ax->label = label;
+
+    if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X))
+        dev->touch->x_axis = axnum;
+    else if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y))
+        dev->touch->y_axis = axnum;
+}
+
 static void
 FixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
 		     ButtonClassPtr b, ValuatorClassPtr v, int first)
@@ -1562,6 +2084,34 @@ GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
     return AddPassiveGrabToList(client, grab);
 }
 
+/* Touch grab */
+int
+GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
+          GrabParameters *param, GrabMask *mask)
+{
+    WindowPtr pWin;
+    GrabPtr grab;
+    int rc;
+
+    rc = CheckGrabValues(client, param);
+    if (rc != Success)
+        return rc;
+
+    rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
+    if (rc != Success)
+	return rc;
+    rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixGrabAccess);
+    if (rc != Success)
+	return rc;
+
+    grab = CreateGrab(client->index, dev, mod_dev, pWin, GRABTYPE_XI2,
+                      mask, param, XI_TouchBegin, 0, NullWindow, NullCursor);
+    if (!grab)
+        return BadAlloc;
+
+    return AddPassiveGrabToList(client, grab);
+}
+
 int
 SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
 		Mask mask, Mask exclusivemasks)
@@ -1706,6 +2256,8 @@ InputClientGone(WindowPtr pWin, XID id)
     for (other = wOtherInputMasks(pWin)->inputClients; other;
 	 other = other->next) {
 	if (other->resource == id) {
+            if (BitIsOn(other->xi2mask, XI_TouchBegin))
+                TouchListenerGone(other->resource);
 	    if (prev) {
 		prev->next = other->next;
 		free(other);
@@ -1734,6 +2286,36 @@ InputClientGone(WindowPtr pWin, XID id)
     FatalError("client not on device event list");
 }
 
+/**
+ * Search for window in each touch trace for each device. Remove the window
+ * and all its subwindows from the trace when found. The initial window
+ * order is preserved.
+ */
+void WindowGone(WindowPtr win)
+{
+    DeviceIntPtr dev;
+
+    for (dev = inputInfo.devices; dev; dev = dev->next) {
+        TouchClassPtr t = dev->touch;
+        int i;
+
+        if (!t)
+            continue;
+
+        for (i = 0; i < t->num_touches; i++) {
+            SpritePtr sprite = &t->touches[i].sprite;
+            int j;
+
+            for (j = 0; j < sprite->spriteTraceGood; j++) {
+                if (sprite->spriteTrace[j] == win) {
+                    sprite->spriteTraceGood = j;
+                    break;
+                }
+            }
+        }
+    }
+}
+
 int
 SendEvent(ClientPtr client, DeviceIntPtr d, Window dest, Bool propagate,
 	  xEvent * ev, Mask mask, int count)
diff --git a/Xi/extinit.c b/Xi/extinit.c
index 82df7eb..4560a76 100644
--- a/Xi/extinit.c
+++ b/Xi/extinit.c
@@ -258,7 +258,8 @@ static int (*ProcIVector[])(ClientPtr) = {
         ProcXIChangeProperty,                   /* 57 */
         ProcXIDeleteProperty,                   /* 58 */
         ProcXIGetProperty,                      /* 59 */
-        ProcXIGetSelectedEvents                 /* 60 */
+        ProcXIGetSelectedEvents,                /* 60 */
+        ProcXIAllowTouchEvents,                 /* 61 */
 };
 
 /* For swapped clients */
@@ -323,7 +324,8 @@ static int (*SProcIVector[])(ClientPtr) = {
         SProcXIChangeProperty,                   /* 57 */
         SProcXIDeleteProperty,                   /* 58 */
         SProcXIGetProperty,                      /* 59 */
-        SProcXIGetSelectedEvents                 /* 60 */
+        SProcXIGetSelectedEvents,                /* 60 */
+        SProcXIAllowTouchEvents,                 /* 61 */
 };
 
 /*****************************************************************
@@ -854,6 +856,21 @@ static void SRawEvent(xXIRawEvent *from, xXIRawEvent *to)
     swaps(&to->valuators_len, n);
 }
 
+static void STouchOwnershipEvent(xXITouchOwnershipEvent *from,
+                                 xXITouchOwnershipEvent *to)
+{
+    char n;
+
+    *to = *from;
+    swaps(&to->sequenceNumber, n);
+    swapl(&to->length, n);
+    swaps(&to->evtype, n);
+    swaps(&to->deviceid, n);
+    swapl(&to->time, n);
+    swaps(&to->sourceid, n);
+    swapl(&to->touchid, n);
+    swapl(&to->flags, n);
+}
 
 /** Event swapping function for XI2 events. */
 void
@@ -881,8 +898,16 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
         case XI_KeyRelease:
         case XI_ButtonPress:
         case XI_ButtonRelease:
+        case XI_TouchBegin:
+        case XI_TouchMotion:
+        case XI_TouchMotionUnowned:
+        case XI_TouchEnd:
             SDeviceEvent((xXIDeviceEvent*)from, (xXIDeviceEvent*)to);
             break;
+        case XI_TouchOwnership:
+            STouchOwnershipEvent((xXITouchOwnershipEvent*)from,
+                                 (xXITouchOwnershipEvent*)to);
+            break;
         case XI_RawMotion:
         case XI_RawKeyPress:
         case XI_RawKeyRelease:
diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c
index 3077e1a..c7056ef 100644
--- a/Xi/xiallowev.c
+++ b/Xi/xiallowev.c
@@ -35,6 +35,8 @@
 
 #include "inputstr.h"	/* DeviceIntPtr      */
 #include "windowstr.h"	/* window structure  */
+#include "eventstr.h"
+#include "mi.h"
 #include <X11/extensions/XI2.h>
 #include <X11/extensions/XI2proto.h>
 
@@ -101,3 +103,93 @@ ProcXIAllowEvents(ClientPtr client)
     return ret;
 }
 
+int
+SProcXIAllowTouchEvents(ClientPtr client)
+{
+    char n;
+
+    REQUEST(xXIAllowTouchEventsReq);
+
+    swaps(&stuff->length, n);
+    swaps(&stuff->deviceid, n);
+    swapl(&stuff->touchid, n);
+
+    return ProcXIAllowTouchEvents(client);
+}
+
+int
+ProcXIAllowTouchEvents(ClientPtr client)
+{
+    DeviceIntPtr dev;
+    TouchPointInfoPtr ti;
+    int ret, nev, i;
+    uint8_t reason;
+    EventList *events = InitEventList(GetMaximumEventsNum());
+    InternalEvent *ev;
+
+    REQUEST(xXIAllowTouchEventsReq);
+    REQUEST_SIZE_MATCH(xXIAllowTouchEventsReq);
+
+    if (!events)
+        return BadAlloc;
+
+    ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
+    if (ret != Success)
+        return ret;
+    if (!dev->touch)
+    {
+        client->errorValue = stuff->deviceid;
+        return BadDevice;
+    }
+
+    ti = FindTouchPointByClientID(dev, stuff->touchid);
+    if (!ti)
+    {
+        client->errorValue = stuff->touchid;
+        return BadValue;
+    }
+
+    if (ti->num_listeners == 0 || CLIENT_ID(ti->listeners[0]) != client->index)
+        return BadAccess;
+
+    if (stuff->mode & XITouchOwnerAccept)
+    {
+        if (stuff->mode & ~(XITouchOwnerAccept | XITouchNoPointerEmulation))
+        {
+            client->errorValue = stuff->mode;
+            return BadValue;
+        }
+
+        if (stuff->mode & XITouchNoPointerEmulation)
+            ti->emulate_pointer = FALSE;
+
+        reason = XITouchOwnerAccept;
+    }
+    else if (stuff->mode & XITouchOwnerReject)
+    {
+        if (stuff->mode & ~XITouchOwnerReject)
+        {
+            client->errorValue = stuff->mode;
+            return BadValue;
+        }
+
+        reason = XITouchOwnerReject;
+    }
+    else
+    {
+        client->errorValue = stuff->mode;
+        return BadValue;
+    }
+
+    nev = GetTouchOwnershipEvents(events, dev, ti, reason, ti->listeners[0], 0);
+    if (nev == 0)
+        return BadAlloc;
+    for (i = 0; i < nev; i++)
+    {
+        ev = (InternalEvent *)((events + i)->event);
+        mieqProcessDeviceEvent(dev, ev, NULL);
+    }
+    ProcessInputEvents();
+
+    return Success;
+}
diff --git a/Xi/xiallowev.h b/Xi/xiallowev.h
index 3a417b9..ca45ee3 100644
--- a/Xi/xiallowev.h
+++ b/Xi/xiallowev.h
@@ -32,5 +32,7 @@
 
 int ProcXIAllowEvents(ClientPtr client);
 int SProcXIAllowEvents(ClientPtr client);
+int ProcXIAllowTouchEvents(ClientPtr client);
+int SProcXIAllowTouchEvents(ClientPtr client);
 
 #endif /* XIALLOWEV_H */
diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
index e99b6e5..972988e 100644
--- a/Xi/xipassivegrab.c
+++ b/Xi/xipassivegrab.c
@@ -105,19 +105,30 @@ ProcXIPassiveGrabDevice(ClientPtr client)
     if (stuff->grab_type != XIGrabtypeButton &&
         stuff->grab_type != XIGrabtypeKeycode &&
         stuff->grab_type != XIGrabtypeEnter &&
-        stuff->grab_type != XIGrabtypeFocusIn)
+        stuff->grab_type != XIGrabtypeFocusIn &&
+        stuff->grab_type != XIGrabtypeTouchBegin)
     {
         client->errorValue = stuff->grab_type;
         return BadValue;
     }
 
     if ((stuff->grab_type == XIGrabtypeEnter ||
-         stuff->grab_type == XIGrabtypeFocusIn) && stuff->detail != 0)
+         stuff->grab_type == XIGrabtypeFocusIn ||
+         stuff->grab_type == XIGrabtypeTouchBegin) &&
+        stuff->detail != 0)
     {
         client->errorValue = stuff->detail;
         return BadValue;
     }
 
+    if (stuff->grab_type == XIGrabtypeTouchBegin &&
+        (stuff->grab_mode != GrabModeAsync ||
+         stuff->paired_device_mode != GrabModeAsync))
+    {
+        client->errorValue = GrabModeSync;
+        return BadValue;
+    }
+
     if (XICheckInvalidMaskBits(client, (unsigned char*)&stuff[1],
                                stuff->mask_len * 4) != Success)
         return BadValue;
@@ -185,6 +196,9 @@ ProcXIPassiveGrabDevice(ClientPtr client)
                 status = GrabWindow(client, dev, stuff->grab_type,
                                     &param, &mask);
                 break;
+            case XIGrabtypeTouchBegin:
+                status = GrabTouch(client, dev, mod_dev, &param, &mask);
+                break;
         }
 
         if (status != GrabSuccess)
diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
index fdd2c05..947cd8d 100644
--- a/Xi/xiquerydevice.c
+++ b/Xi/xiquerydevice.c
@@ -232,6 +232,12 @@ SizeDeviceClasses(DeviceIntPtr dev)
     if (dev->valuator)
         len += sizeof(xXIValuatorInfo) * dev->valuator->numAxes;
 
+    if (dev->touch)
+    {
+        len += sizeof(xXITouchInfo);
+        len += sizeof(xXITouchValuatorInfo) * dev->touch->num_axes;
+    }
+
     return len;
 }
 
@@ -373,6 +379,73 @@ SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info)
     swaps(&info->sourceid, n);
 }
 
+/**
+ * List multitouch information
+ *
+ * @return The number of bytes written into info.
+ */
+int
+ListTouchInfo(DeviceIntPtr dev, xXITouchInfo *touch)
+{
+    touch->type = XITouchClass;
+    touch->length = sizeof(xXITouchInfo) >> 2;
+    touch->sourceid = dev->id;
+    touch->mode = dev->touch->mode;
+    touch->num_touches = dev->touch->num_touches;
+
+    return touch->length << 2;
+}
+
+static void
+SwapTouchInfo(DeviceIntPtr dev, xXITouchInfo* touch)
+{
+    char n;
+    swaps(&touch->type, n);
+    swaps(&touch->length, n);
+    swaps(&touch->sourceid, n);
+}
+
+/**
+ * List multitouch axis information
+ *
+ * @return The number of bytes written into info.
+ */
+int
+ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
+                      int axisnumber)
+{
+    TouchClassPtr t = dev->touch;
+
+    val->type = XITouchValuatorClass;
+    val->length = sizeof(xXITouchValuatorInfo) >> 2;
+    val->sourceid = dev->id;
+    val->number = axisnumber;
+    val->label = t->axes[axisnumber].label;
+    val->min.integral = t->axes[axisnumber].min_value;
+    val->min.frac = 0;
+    val->max.integral = t->axes[axisnumber].max_value;
+    val->max.frac = 0;
+    val->resolution = t->axes[axisnumber].resolution;
+
+    return val->length << 2;
+}
+
+static void
+SwapTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val)
+{
+    char n;
+    swaps(&val->type, n);
+    swaps(&val->length, n);
+    swaps(&val->sourceid, n);
+    swaps(&val->number, n);
+    swapl(&val->label, n);
+    swapl(&val->min.integral, n);
+    swapl(&val->min.frac, n);
+    swapl(&val->max.integral, n);
+    swapl(&val->max.frac, n);
+    swapl(&val->resolution, n);
+}
+
 int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
 {
     DeviceIntPtr master = dev->u.master;
@@ -462,6 +535,22 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
         total_len += len;
     }
 
+    if (dev->touch)
+    {
+        (*nclasses)++;
+        len = ListTouchInfo(dev, (xXITouchInfo*)any);
+        any += len;
+        total_len += len;
+
+        for (i = 0; i < dev->touch->num_axes; i++)
+        {
+            (*nclasses)++;
+            len = ListTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any, i);
+            any += len;
+            total_len += len;
+        }
+    }
+
     return total_len;
 }
 
@@ -489,6 +578,12 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
             case XIValuatorClass:
                 SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
                 break;
+            case XITouchClass:
+                SwapTouchInfo(dev, (xXITouchInfo*)any);
+                break;
+            case XITouchValuatorClass:
+                SwapTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any);
+                break;
         }
 
         any += len * 4;
diff --git a/Xi/xiquerydevice.h b/Xi/xiquerydevice.h
index 02f0659..59326f7 100644
--- a/Xi/xiquerydevice.h
+++ b/Xi/xiquerydevice.h
@@ -44,4 +44,7 @@ int ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info, Bool reportState);
 int ListKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info);
 int ListValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info,
 		     int axisnumber, Bool reportState);
+int ListTouchInfo(DeviceIntPtr dev, xXITouchInfo* info);
+int ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
+                          int axisnumber);
 #endif /* QUERYDEV_H */
diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
index 22fbaf5..effce2c 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -152,6 +152,50 @@ ProcXISelectEvents(ClientPtr client)
             }
         }
 
+        if (evmask->mask_len >= 1)
+        {
+            unsigned char *bits = (unsigned char*)&evmask[1];
+
+            /* All three touch events must be selected at once */
+            if ((BitIsOn(bits, XI_TouchBegin) ||
+                 BitIsOn(bits, XI_TouchMotion) ||
+                 BitIsOn(bits, XI_TouchMotionUnowned) ||
+                 BitIsOn(bits, XI_TouchOwnership) ||
+                 BitIsOn(bits, XI_TouchEnd)) &&
+                (!BitIsOn(bits, XI_TouchBegin) ||
+                 !BitIsOn(bits, XI_TouchMotion) ||
+                 !BitIsOn(bits, XI_TouchOwnership) ||
+                 !BitIsOn(bits, XI_TouchEnd)))
+            {
+                client->errorValue = XI_TouchBegin;
+                return BadValue;
+            }
+
+            /* Only one client per window may select for touch events on the
+             * same devices, including master devices.
+             * XXX: This breaks if a device goes from floating to attached. */
+            if (BitIsOn(bits, XI_TouchBegin))
+            {
+                OtherInputMasks *inputMasks = wOtherInputMasks(win);
+                InputClients *iclient = NULL;
+                if (inputMasks)
+                    iclient = inputMasks->inputClients;
+                for (; iclient; iclient = iclient->next)
+                {
+                    if (CLIENT_ID(iclient->resource) == client->index)
+                        continue;
+                    if (BitIsOn(iclient->xi2mask[evmask->deviceid],
+                                XI_TouchBegin) ||
+                        BitIsOn(iclient->xi2mask[XIAllDevices],
+                                XI_TouchBegin) ||
+                        (dev && (IsMaster(dev) || dev->u.master) &&
+                         BitIsOn(iclient->xi2mask[XIAllMasterDevices],
+                                 XI_TouchBegin)))
+                        return BadAccess;
+                }
+            }
+        }
+
         if (XICheckInvalidMaskBits(client, (unsigned char*)&evmask[1],
                                    evmask->mask_len * 4) != Success)
             return BadValue;
diff --git a/configure.ac b/configure.ac
index 1ceffe7..55e00ed 100644
--- a/configure.ac
+++ b/configure.ac
@@ -783,7 +783,7 @@ WINDOWSWMPROTO="windowswmproto"
 APPLEWMPROTO="applewmproto >= 1.4"
 
 dnl Core modules for most extensions, et al.
-SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.4] [renderproto >= 0.11] [xextproto >= 7.1.99] [inputproto >= 1.9.99.902] [kbproto >= 1.0.3] fontsproto"
+SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.4] [renderproto >= 0.11] [xextproto >= 7.1.99] [inputproto >= 2.0.99.1] [kbproto >= 1.0.3] fontsproto"
 # Make SDK_REQUIRED_MODULES available for inclusion in xorg-server.pc
 AC_SUBST(SDK_REQUIRED_MODULES)
 
diff --git a/dix/devices.c b/dix/devices.c
index 6c0dc42..2e75b49 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -754,6 +754,20 @@ FreeDeviceClass(int type, pointer *class)
                 free((*v));
                 break;
             }
+        case XITouchClass:
+            {
+                TouchClassPtr *t = (TouchClassPtr*)class;
+                int i;
+
+                for (i = 0; i < (*t)->num_touches; i++)
+                {
+                    free((*t)->touches[i].sprite.spriteTrace);
+                    free((*t)->touches[i].listeners);
+                }
+
+                free((*t));
+                break;
+            }
         case FocusClass:
             {
                 FocusClassPtr *f = (FocusClassPtr*)class;
@@ -862,6 +876,7 @@ FreeAllDeviceClasses(ClassesPtr classes)
 
     FreeDeviceClass(KeyClass, (pointer)&classes->key);
     FreeDeviceClass(ValuatorClass, (pointer)&classes->valuator);
+    FreeDeviceClass(XITouchClass, (pointer)&classes->touch);
     FreeDeviceClass(ButtonClass, (pointer)&classes->button);
     FreeDeviceClass(FocusClass, (pointer)&classes->focus);
     FreeDeviceClass(ProximityClass, (pointer)&classes->proximity);
@@ -1543,6 +1558,128 @@ InitPointerDeviceStruct(DevicePtr device, CARD8 *map, int numButtons, Atom* btn_
 	   InitPtrFeedbackClassDeviceStruct(dev, controlProc));
 }
 
+Bool
+InitTouchPoint(TouchClassPtr t, int index)
+{
+    TouchPointInfoPtr ti;
+
+    if (!index >= t->num_touches)
+        return FALSE;
+    ti = &t->touches[index];
+
+    ti->valuators = calloc(t->num_axes, sizeof(*ti->valuators));
+    if (!ti->valuators)
+        return FALSE;
+    ti->num_valuators = t->num_axes;
+
+    ti->sprite.spriteTrace = calloc(32, sizeof(*ti->sprite.spriteTrace));
+    if (!ti->sprite.spriteTrace)
+    {
+        free(ti->valuators);
+        ti->valuators = NULL;
+        ti->num_valuators = 0;
+        return FALSE;
+    }
+    ti->sprite.spriteTraceSize = 32;
+    ti->sprite.spriteTrace[0] = screenInfo.screens[0]->root;
+    ti->sprite.hot.pScreen = screenInfo.screens[0];
+    ti->sprite.hotPhys.pScreen = screenInfo.screens[0];
+
+    ti->ddx_id = -1;
+    ti->client_id = -1;
+
+    return TRUE;
+}
+
+void
+FreeTouchPoint(DeviceIntPtr device, int index)
+{
+    TouchPointInfoPtr ti;
+
+    if (!device->touch || index >= device->touch->num_touches)
+        return;
+    ti = &device->touch->touches[index];
+
+    if (ti->active)
+        EndTouchPoint(device, ti);
+
+    free(ti->valuators);
+    ti->valuators = NULL;
+    ti->num_valuators = 0;
+    free(ti->sprite.spriteTrace);
+    ti->sprite.spriteTrace = NULL;
+}
+
+/**
+ * Sets up multitouch capabilities on @device.
+ *
+ * @max_touches The maximum number of simultaneous touches, or 0 for unlimited.
+ * @mode The mode of the touch device (XIDirectTouch or XIDependentTouch).
+ * @num_axes The number of touch valuator axes.
+ */
+Bool
+InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches,
+                           unsigned int mode, unsigned int num_axes)
+{
+    TouchClassPtr touch;
+    int i;
+
+    if (device->touch)
+        return FALSE;
+
+    /* Check the mode is valid, and at least X and Y axes. */
+    if (mode != XIDirectTouch && mode != XIDependentTouch)
+        return FALSE;
+    if (num_axes < 2)
+        return FALSE;
+
+    if (num_axes > MAX_VALUATORS)
+    {
+        LogMessage(X_WARNING,
+                   "Device '%s' has %d touch axes, only using first %d.\n",
+                   device->name, num_axes, MAX_VALUATORS);
+        num_axes = MAX_VALUATORS;
+    }
+
+    touch = calloc(1, sizeof(*touch));
+    if (!touch)
+        return FALSE;
+
+    touch->axes = calloc(num_axes, sizeof(*touch->axes));
+    if (!touch->axes)
+        goto err;
+    touch->num_axes = num_axes;
+
+    touch->max_touches = max_touches;
+    if (max_touches == 0)
+        max_touches = 5; /* arbitrary number plucked out of the air */
+    touch->touches = calloc(max_touches, sizeof(*touch->touches));
+    if (!touch->touches)
+        goto err;
+    touch->num_touches = max_touches;
+    for (i = 0; i < max_touches; i++)
+        InitTouchPoint(touch, i);
+
+    touch->mode = mode;
+    touch->x_axis = -1;
+    touch->y_axis = -1;
+    touch->next_client_id = 1;
+
+    device->touch = touch;
+
+    return TRUE;
+
+err:
+    for (i = 0; i < touch->num_touches; i++)
+        FreeTouchPoint(device, i);
+
+    free(touch->touches);
+    free(touch->axes);
+    free(touch);
+
+    return FALSE;
+}
+
 /*
  * Check if the given buffer contains elements between low (inclusive) and
  * high (inclusive) only.
diff --git a/dix/eventconvert.c b/dix/eventconvert.c
index 76d9a3e..025e246 100644
--- a/dix/eventconvert.c
+++ b/dix/eventconvert.c
@@ -55,6 +55,7 @@ static int eventToKeyButtonPointer(DeviceEvent *ev, xEvent **xi, int *count);
 static int eventToDeviceChanged(DeviceChangedEvent *ev, xEvent **dcce);
 static int eventToDeviceEvent(DeviceEvent *ev, xEvent **xi);
 static int eventToRawEvent(RawDeviceEvent *ev, xEvent **xi);
+static int eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi);
 
 /* Do not use, read comments below */
 BOOL EventIsKeyRepeat(xEvent *event);
@@ -139,6 +140,11 @@ EventToCore(InternalEvent *event, xEvent *core)
         case ET_RawButtonPress:
         case ET_RawButtonRelease:
         case ET_RawMotion:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
+        case ET_TouchMotionUnowned:
+        case ET_TouchOwnership:
             return BadMatch;
         default:
             /* XXX: */
@@ -184,6 +190,11 @@ EventToXI(InternalEvent *ev, xEvent **xi, int *count)
         case ET_RawButtonPress:
         case ET_RawButtonRelease:
         case ET_RawMotion:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
+        case ET_TouchMotionUnowned:
+        case ET_TouchOwnership:
             *count = 0;
             *xi = NULL;
             return BadMatch;
@@ -225,7 +236,13 @@ EventToXI2(InternalEvent *ev, xEvent **xi)
         case ET_ButtonRelease:
         case ET_KeyPress:
         case ET_KeyRelease:
+        case ET_TouchBegin:
+        case ET_TouchMotion:
+        case ET_TouchMotionUnowned:
+        case ET_TouchEnd:
             return eventToDeviceEvent(&ev->device_event, xi);
+        case ET_TouchOwnership:
+            return eventToTouchOwnershipEvent(&ev->touch_ownership_event, xi);
         case ET_ProximityIn:
         case ET_ProximityOut:
             *xi = NULL;
@@ -614,6 +631,28 @@ eventToDeviceEvent(DeviceEvent *ev, xEvent **xi)
 }
 
 static int
+eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi)
+{
+    int len = sizeof(xXITouchOwnershipEvent);
+    xXITouchOwnershipEvent *xtoe;
+
+    *xi = calloc(1, len);
+    xtoe = (xXITouchOwnershipEvent*)*xi;
+    xtoe->type          = GenericEvent;
+    xtoe->extension     = IReqCode;
+    xtoe->length        = bytes_to_int32(len - sizeof(xEvent));
+    xtoe->evtype        = GetXI2Type((InternalEvent*)ev);
+    xtoe->deviceid      = ev->deviceid;
+    xtoe->time          = ev->time;
+    xtoe->sourceid      = ev->sourceid;
+    xtoe->reason        = ev->reason;
+    xtoe->touchid       = ev->touchid;
+    xtoe->flags         = ev->flags;
+
+    return Success;
+}
+
+static int
 eventToRawEvent(RawDeviceEvent *ev, xEvent **xi)
 {
     xXIRawEvent* raw;
@@ -728,6 +767,11 @@ GetXI2Type(InternalEvent *event)
         case ET_RawMotion:      xi2type = XI_RawMotion;        break;
         case ET_FocusIn:        xi2type = XI_FocusIn;          break;
         case ET_FocusOut:       xi2type = XI_FocusOut;         break;
+        case ET_TouchBegin:     xi2type = XI_TouchBegin;       break;
+        case ET_TouchEnd:       xi2type = XI_TouchEnd;         break;
+        case ET_TouchMotion:    xi2type = XI_TouchMotion;      break;
+        case ET_TouchMotionUnowned: xi2type = XI_TouchMotionUnowned; break;
+        case ET_TouchOwnership: xi2type = XI_TouchOwnership;   break;
         default:
             break;
     }
diff --git a/dix/events.c b/dix/events.c
index 38e9a08..8c590f0 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -2232,7 +2232,8 @@ FixUpEventFromWindow(
             event->evtype == XI_RawMotion ||
             event->evtype == XI_DeviceChanged ||
             event->evtype == XI_HierarchyChanged ||
-            event->evtype == XI_PropertyEvent)
+            event->evtype == XI_PropertyEvent ||
+            event->evtype == XI_TouchOwnership)
             return;
 
         event->root = RootWindow(pSprite)->drawable.id;
diff --git a/dix/getevents.c b/dix/getevents.c
index 7757512..530008f 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -47,6 +47,7 @@
 #include "eventstr.h"
 #include "eventconvert.h"
 #include "inpututils.h"
+#include "windowstr.h"
 
 #include <X11/extensions/XKBproto.h>
 #include "xkbsrv.h"
@@ -172,6 +173,18 @@ init_event(DeviceIntPtr dev, DeviceEvent* event, Time ms)
 }
 
 static void
+init_touch_ownership(DeviceIntPtr dev, TouchOwnershipEvent *event, Time ms)
+{
+    memset(event, 0, sizeof(TouchOwnershipEvent));
+    event->header = ET_Internal;
+    event->type = ET_TouchOwnership;
+    event->length = sizeof(TouchOwnershipEvent);
+    event->time = ms;
+    event->deviceid = dev->id;
+    event->sourceid = dev->id;
+}
+
+static void
 init_raw(DeviceIntPtr dev, RawDeviceEvent *event, Time ms, int type, int detail)
 {
     memset(event, 0, sizeof(RawDeviceEvent));
@@ -1286,6 +1299,130 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type, const Valuato
     return num_events;
 }
 
+int
+GetTouchOwnershipEvents(EventList *events, DeviceIntPtr pDev,
+                        TouchPointInfoPtr ti, uint8_t reason, XID resource,
+                        uint32_t flags)
+{
+    TouchClassPtr t = pDev->touch;
+    TouchOwnershipEvent *event;
+    CARD32 ms = GetTimeInMillis();
+
+    if (!pDev->enabled || !t || !ti)
+        return 0;
+
+    if (reason != XITouchOwnerAccept && reason != XITouchOwnerReject)
+        return 0;
+
+    event = (TouchOwnershipEvent *) events->event;
+    init_touch_ownership(pDev, event, ms);
+
+    event->touchid = ti->client_id;
+    event->reason = reason;
+    event->resource = resource;
+    event->flags = flags;
+
+    return 1;
+}
+
+/**
+ * Get events for a touch. Generates a TouchBegin event if end is not set and
+ * the touch id is not active. Generates a TouchMotion event if end is not set
+ * and the touch id is active. Generates a TouchEnd event if end is set and the
+ * touch id is active.
+ *
+ * events is not NULL-terminated; the return value is the number of events.
+ * The DDX is responsible for allocating the event structure in the first
+ * place via GetMaximumEventsNum(), and for freeing it.
+ */
+int
+GetTouchEvents(EventList *events, DeviceIntPtr pDev, TouchPointInfoPtr ti,
+               uint16_t type, uint32_t flags, const ValuatorMask *mask_in)
+{
+    ScreenPtr scr = pDev->spriteInfo->sprite->hotPhys.pScreen;
+    TouchClassPtr t = pDev->touch;
+    DeviceEvent *event;
+    CARD32 ms = GetTimeInMillis();
+    ValuatorMask mask;
+    int x, y; /* in screen co-ord space */
+    float x_frac = 0.0, y_frac = 0.0; /* as above */
+    int i;
+
+    if (!pDev->enabled || !t || t->x_axis == -1 || t->y_axis == -1)
+        return 0;
+
+    event = (DeviceEvent *) events->event;
+    init_event(pDev, event, ms);
+
+    switch (type) {
+    case XI_TouchBegin:
+        event->type = ET_TouchBegin;
+        /* If we're starting a touch, we must have x & y co-ordinates. */
+        if (!valuator_mask_isset(mask_in, t->x_axis) ||
+            !valuator_mask_isset(mask_in, t->y_axis))
+        {
+            DebugF("%s: Attempted to start touch without x/y (driver bug)\n",
+                   pDev->name);
+            return 0;
+        }
+        break;
+    case XI_TouchMotion:
+        event->type = ET_TouchMotion;
+        break;
+    case XI_TouchEnd:
+        event->type = ET_TouchEnd;
+        break;
+    default:
+        return 0;
+    }
+
+    valuator_mask_copy(&mask, mask_in);
+
+    /* Get our screen event co-ordinates (root_x/root_y/event_x/event_y):
+     * these come from the touchpoint in Absolute mode, or the sprite in
+     * Relative. */
+    if (t->mode == XIDirectTouch) {
+        if (valuator_mask_isset(&mask, t->x_axis))
+            x = valuator_mask_get(&mask, t->x_axis);
+        else
+            x = ti->valuators[t->x_axis];
+        x = rescaleValuatorAxis(x, 0.0, &x_frac,
+                                (AxisInfoPtr)(t->axes + t->x_axis),
+                                NULL, scr->width);
+
+        if (valuator_mask_isset(&mask, t->y_axis))
+            y = valuator_mask_get(&mask, t->y_axis);
+        else
+            y = ti->valuators[t->y_axis];
+        y = rescaleValuatorAxis(y, 0.0, &y_frac,
+                                (AxisInfoPtr)(t->axes + t->y_axis),
+                                NULL, scr->height);
+
+        transformAbsolute(pDev, &mask, &x, &y);
+    }
+    else {
+        x = pDev->spriteInfo->sprite->hotPhys.x;
+        y = pDev->spriteInfo->sprite->hotPhys.y;
+    }
+
+    event->root = scr->root->drawable.id;
+    event->root_x = x;
+    event->root_y = y;
+    event->root_x_frac = x_frac;
+    event->root_y_frac = y_frac;
+    event->detail.touch = ti->client_id;
+    event->flags = flags;
+
+    set_valuators(pDev, event, &mask);
+    for (i = 0; i < t->num_axes; i++)
+    {
+        if (valuator_mask_isset(&mask, i))
+            ti->valuators[i] = valuator_mask_get(&mask, i);
+    }
+
+    return 1;
+}
+
 /**
  * Synthesize a single motion event for the core pointer.
  *
diff --git a/dix/grabs.c b/dix/grabs.c
index 69c58df..437642e 100644
--- a/dix/grabs.c
+++ b/dix/grabs.c
@@ -60,6 +60,7 @@ SOFTWARE.
 #include "dixgrabs.h"
 #include "xace.h"
 #include "exevents.h"
+#include "mi.h"
 
 #define BITMASK(i) (((Mask)1) << ((i) & 31))
 #define MASKIDX(i) ((i) >> 5)
@@ -114,9 +115,57 @@ CreateGrab(
 
 }
 
+/* As touch grabs don't turn into active grabs with their own resources, we
+ * need to walk all the touches and remove this grab from any delivery
+ * lists. */
+void
+TouchListenerGone(XID resource)
+{
+    TouchPointInfoPtr ti;
+    DeviceIntPtr dev;
+    InternalEvent *ev;
+    EventList *events = InitEventList(GetMaximumEventsNum());
+    int i, j, k, nev;
+
+    if (!events)
+        FatalError("TouchListenerGone: couldn't allocate events\n");
+
+    for (dev = inputInfo.devices; dev; dev = dev->next)
+    {
+        if (!dev->touch)
+            continue;
+
+        for (i = 0; i < dev->touch->num_touches; i++)
+        {
+            ti = &dev->touch->touches[i];
+            if (!ti->active)
+                continue;
+
+            for (j = 0; j < ti->num_listeners; j++)
+            {
+                if (ti->listeners[j] != resource)
+                    continue;
+
+                nev = GetTouchOwnershipEvents(events, dev, ti, resource,
+                                              XITouchOwnerReject, 0);
+                for (k = 0; k < nev; k++)
+                {
+                    ev = (InternalEvent *)((events + k)->event);
+                    mieqProcessDeviceEvent(dev, ev, NULL);
+                }
+
+                break;
+            }
+        }
+    }
+}
+
 static void
 FreeGrab(GrabPtr pGrab)
 {
+    if (pGrab->grabtype == GRABTYPE_XI2 && pGrab->type == XI_TouchBegin)
+        TouchListenerGone(pGrab->resource);
+
     free(pGrab->modifiersDetail.pMask);
     free(pGrab->detail.pMask);
 
@@ -243,6 +292,25 @@ GrabSupersedesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab)
 }
 
 /**
+ * Returns the event type to match when comparing grabs.
+ */
+static uint32_t
+GetGrabEventMatch(GrabPtr pGrab)
+{
+    if (pGrab->grabtype != GRABTYPE_XI2)
+        return pGrab->type;
+
+    if (pGrab->type == XI_TouchBegin ||
+        pGrab->type == XI_TouchMotion ||
+        pGrab->type == XI_TouchMotionUnowned ||
+        pGrab->type == XI_TouchOwnership ||
+        pGrab->type == XI_TouchEnd)
+        return XI_TouchBegin;
+
+    return pGrab->type;
+}
+
+/**
  * Compares two grabs and returns TRUE if the first grab matches the second
  * grab. 
  * 
@@ -261,6 +329,8 @@ GrabMatchesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab, Bool ignoreDevice)
     unsigned int any_modifier = (pFirstGrab->grabtype == GRABTYPE_XI2) ?
                                 (unsigned int)XIAnyModifier :
                                 (unsigned int)AnyModifier;
+    uint32_t first_type = GetGrabEventMatch(pFirstGrab);
+    uint32_t second_type = GetGrabEventMatch(pSecondGrab);
 
     if (pFirstGrab->grabtype != pSecondGrab->grabtype)
         return FALSE;
@@ -288,8 +358,8 @@ GrabMatchesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab, Bool ignoreDevice)
              (pFirstGrab->modifierDevice != pSecondGrab->modifierDevice)))
             return FALSE;
 
-    if (pFirstGrab->type != pSecondGrab->type)
-	return FALSE;
+    if (first_type != second_type)
+        return FALSE;
 
     if (GrabSupersedesSecond(pFirstGrab, pSecondGrab) ||
 	GrabSupersedesSecond(pSecondGrab, pFirstGrab))
diff --git a/dix/inpututils.c b/dix/inpututils.c
index ef3142c..c2016c0 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -556,3 +556,132 @@ CountBits(const uint8_t *mask, int len)
 
     return ret;
 }
+
+/**
+ * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the
+ * associated TouchPointInfoRec.
+ */
+TouchPointInfoPtr
+FindTouchPointByDDXID(DeviceIntPtr dev, uint32_t ddx_id)
+{
+    TouchClassPtr t = dev->touch;
+    TouchPointInfoPtr ti;
+    int i;
+
+    if (!t)
+        return NULL;
+
+    for (i = 0; i < t->num_touches; i++)
+    {
+        ti = &t->touches[i];
+        if (ti->active && ti->ddx_id == ddx_id && !ti->pending_finish)
+            return ti;
+    }
+
+    return NULL;
+}
+
+/**
+ * Given a client-facing ID (e.g. DeviceEvent::detail.touch), find the
+ * associated TouchPointInfoRec.
+ */
+TouchPointInfoPtr
+FindTouchPointByClientID(DeviceIntPtr dev, uint32_t client_id)
+{
+    TouchClassPtr t = dev->touch;
+    TouchPointInfoPtr ti;
+    int i;
+
+    if (!t)
+        return NULL;
+
+    for (i = 0; i < t->num_touches; i++)
+    {
+        ti = &t->touches[i];
+        if (ti->active && ti->client_id == client_id)
+            return ti;
+    }
+
+    return NULL;
+}
+
+/**
+ * Given a unique DDX ID for a touchpoint, create a touchpoint record in the
+ * server and return the client-facing ID.
+ *
+ * Returns 0 on failure (i.e. if another touch with that ID is already active,
+ * allocation failure).
+ */
+_X_EXPORT TouchPointInfoPtr
+BeginTouchPoint(DeviceIntPtr dev, uint32_t ddx_id)
+{
+    int i;
+    TouchClassPtr t = dev->touch;
+    TouchPointInfoPtr ti;
+    void *tmp;
+
+    if (!t)
+        return NULL;
+
+    /* Look for another active touchpoint with the same DDX ID.  It's entirely
+     * legitimate for a touchpoint to still exist with the same DDX ID but
+     * be in the pending_finish state as it waits for a client to release its
+     * grab, so allow for that. */
+    if (FindTouchPointByDDXID(dev, ddx_id))
+        return NULL;
+
+try_find_touch:
+    for (i = 0; i < t->num_touches; i++)
+    {
+        ti = &t->touches[i];
+        if (!ti->active) {
+            ti->active = TRUE;
+            ti->ddx_id = ddx_id;
+            ti->client_id = t->next_client_id;
+next_touch_id:
+            t->next_client_id++;
+            if (t->next_client_id == 0)
+                t->next_client_id = 1;
+            if (FindTouchPointByClientID(dev, t->next_client_id))
+                goto next_touch_id; /* n'th time's a charm */
+            return ti;
+        }
+    }
+
+    /* If we get here, then we've run out of touches: enlarge dev->touch and
+     * try again. */
+    tmp = realloc(t, (t->num_touches + 1) * sizeof(*t));
+    if (tmp)
+    {
+        dev->touch = tmp;
+        t->num_touches++;
+        if (InitTouchPoint(t, t->num_touches - 1))
+            goto try_find_touch;
+    }
+
+    return NULL;
+}
+
+/**
+ * Releases a touchpoint for use: this must only be called after all events
+ * related to that touchpoint have been sent and finalised.  Called from
+ * ProcessTouchEvent and friends.  Not by you.
+ */
+void
+EndTouchPoint(DeviceIntPtr dev, TouchPointInfoPtr ti)
+{
+    int i;
+
+    ti->active = FALSE;
+    ti->pending_finish = FALSE;
+    ti->sprite.spriteTraceGood = 0;
+    free(ti->listeners);
+    ti->listeners = NULL;
+    ti->num_listeners = 0;
+    ti->num_grabs = 0;
+    ti->client_id = 0;
+    ti->ddx_id = 0;
+
+    for (i = 0; i < ti->num_valuators; i++)
+        ti->valuators[i] = 0;
+}
diff --git a/dix/window.c b/dix/window.c
index 9be7064..d05f76d 100644
--- a/dix/window.c
+++ b/dix/window.c
@@ -110,6 +110,7 @@ Equipment Corporation.
 #include "windowstr.h"
 #include "input.h"
 #include "inputstr.h"
+#include "exevents.h"
 #include "resource.h"
 #include "colormapst.h"
 #include "cursorstr.h"
@@ -2873,8 +2874,10 @@ UnmapWindow(WindowPtr pWin, Bool fromConfigure)
 	if (!fromConfigure && pScreen->PostValidateTree)
 	    (*pScreen->PostValidateTree)(pLayerWin->parent, pWin, VTUnmap);
     }
-    if (wasRealized && !fromConfigure)
+    if (wasRealized && !fromConfigure) {
 	WindowsRestructured ();
+	WindowGone(pWin);
+    }
     return Success;
 }
 
@@ -2957,8 +2960,10 @@ UnmapSubwindows(WindowPtr pWin)
 	if (anyMarked && pScreen->PostValidateTree)
 	    (*pScreen->PostValidateTree)(pLayerWin->parent, pHead, VTUnmap);
     }
-    if (wasRealized)
+    if (wasRealized) {
 	WindowsRestructured ();
+	WindowGone(pWin);
+    }
 }
 
 
diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
index b9006ab..9b21c31 100644
--- a/hw/xfree86/common/xf86Xinput.c
+++ b/hw/xfree86/common/xf86Xinput.c
@@ -1345,6 +1345,16 @@ xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
 			   max_res, mode);
 }
 
+void
+xf86InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label,
+                                int minval, int maxval, int resolution)
+{
+    if (!dev || !dev->touch)
+        return;
+
+    InitTouchValuatorAxisStruct(dev, axnum, label, minval, maxval, resolution);
+}
+
 /*
  * Set the valuator values to be in synch with dix/event.c
  * DefineInitialRootWindow().
@@ -1396,4 +1406,39 @@ xf86EnableDevice(DeviceIntPtr dev)
     EnableDevice(dev, TRUE);
 }
 
+/**
+ * Post a touch event with optional valuators.  If this is the first touch in
+ * the sequence, at least x & y valuators must be provided.  If end is TRUE,
+ * then this is taken to be the last touch in the touch sequence.
+ */
+void
+xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid, uint16_t type,
+                   uint32_t flags, const ValuatorMask *mask)
+{
+    int i, nevents;
+    TouchPointInfoPtr ti = FindTouchPointByDDXID(dev, touchid);
+
+    if (!ti)
+    {
+        if (type != XI_TouchBegin)
+        {
+            xf86Msg(X_ERROR,
+                    "%s: Tried to post event for non-existent touch %lx\n",
+                    dev->name, touchid);
+            return;
+        }
+
+        ti = BeginTouchPoint(dev, touchid);
+        if (!ti)
+        {
+            xf86Msg(X_ERROR, "%s: Couldn't create touchpoint\n", dev->name);
+            return;
+        }
+    }
+
+    nevents = GetTouchEvents(xf86Events, dev, ti, type, flags, mask);
+    for (i = 0; i < nevents; i++)
+        mieqEnqueue(dev, (InternalEvent *)((xf86Events + i)->event));
+}
+
 /* end of xf86Xinput.c */
diff --git a/hw/xfree86/common/xf86Xinput.h b/hw/xfree86/common/xf86Xinput.h
index 1b0b16f..f1afb09 100644
--- a/hw/xfree86/common/xf86Xinput.h
+++ b/hw/xfree86/common/xf86Xinput.h
@@ -141,6 +141,9 @@ extern _X_EXPORT void xf86PostKeyEventP(DeviceIntPtr device, unsigned int key_co
 		       const int *valuators);
 extern _X_EXPORT void xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code,
                            int is_down);
+extern _X_EXPORT void xf86PostTouchEvent(DeviceIntPtr dev, uint32_t touchid,
+                                         uint16_t type, uint32_t flags,
+                                         const ValuatorMask *mask);
 extern _X_EXPORT InputInfoPtr xf86FirstLocalDevice(void);
 extern _X_EXPORT int xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min);
 extern _X_EXPORT void xf86XInputSetScreen(InputInfoPtr pInfo, int screen_number, int x, int y);
@@ -148,6 +151,8 @@ extern _X_EXPORT void xf86ProcessCommonOptions(InputInfoPtr pInfo, pointer optio
 extern _X_EXPORT void xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
 				int maxval, int resolution, int min_res,
 				int max_res, int mode);
+extern _X_EXPORT void xf86InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label,
+				int minval, int maxval, int resolution);
 extern _X_EXPORT void xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum);
 extern _X_EXPORT void xf86AddEnabledDevice(InputInfoPtr pInfo);
 extern _X_EXPORT void xf86RemoveEnabledDevice(InputInfoPtr pInfo);
diff --git a/include/events.h b/include/events.h
index 375173a..222cc3d 100644
--- a/include/events.h
+++ b/include/events.h
@@ -26,6 +26,7 @@
 #define EVENTS_H
 typedef struct _DeviceEvent DeviceEvent;
 typedef struct _DeviceChangedEvent DeviceChangedEvent;
+typedef struct _TouchOwnershipEvent TouchOwnershipEvent;
 #if XFreeXDGA
 typedef struct _DGAEvent DGAEvent;
 #endif
diff --git a/include/eventstr.h b/include/eventstr.h
index e1f5003..cf02efe 100644
--- a/include/eventstr.h
+++ b/include/eventstr.h
@@ -65,6 +65,11 @@ enum EventType {
     ET_RawButtonRelease,
     ET_RawMotion,
     ET_XQuartz,
+    ET_TouchBegin,
+    ET_TouchEnd,
+    ET_TouchMotion,
+    ET_TouchMotionUnowned,
+    ET_TouchOwnership,
     ET_Internal = 0xFF /* First byte */
 };
 
@@ -90,6 +95,7 @@ struct _DeviceEvent
     union {
         uint32_t button;  /**< Button number */
         uint32_t key;     /**< Key code */
+        uint32_t touch;   /**< Touch ID (client_id) */
     } detail;
     int16_t root_x;       /**< Pos relative to root window in integral data */
     float root_x_frac;    /**< Pos relative to root window in frac part */
@@ -120,6 +126,24 @@ struct _DeviceEvent
     uint32_t flags;   /**< Flags to be copied into the generated event */
 };
 
+/**
+ * Generated internally whenever a touch ownership chain changes - an owner
+ * has accepted or rejected a touch, or a grab/event selection in the delivery
+ * chain has been removed.
+ */
+struct _TouchOwnershipEvent
+{
+    unsigned char header; /**< Always ET_Internal */
+    enum EventType type;  /**< One of EventType */
+    int length;           /**< Length in bytes */
+    Time time;            /**< Time in ms */
+    int deviceid;         /**< Device to post this event for */
+    int sourceid;         /**< The physical source device */
+    uint32_t touchid;     /**< Touch ID (client_id) */
+    uint8_t reason;       /**< ::XITouchOwnerAccepted, ::XITouchOwnerRejected */
+    uint32_t resource;    /**< Provoking grab or event selection */
+    uint32_t flags;       /**< Flags to be copied into the generated event */
+};
 
 /* Flags used in DeviceChangedEvent to signal if the slave has changed */
 #define DEVCHANGE_SLAVE_SWITCH 0x2
@@ -235,6 +259,7 @@ union _InternalEvent {
         } any;
         DeviceEvent device_event;
         DeviceChangedEvent changed_event;
+        TouchOwnershipEvent touch_ownership_event;
 #if XFreeXDGA
         DGAEvent dga_event;
 #endif
diff --git a/include/exevents.h b/include/exevents.h
index dc59430..32a3962 100644
--- a/include/exevents.h
+++ b/include/exevents.h
@@ -51,6 +51,14 @@ extern _X_EXPORT void InitValuatorAxisStruct(
 	int                    /* max_res */,
 	int                    /* mode */);
 
+extern _X_EXPORT void InitTouchValuatorAxisStruct(
+	DeviceIntPtr           /* dev */,
+	int                    /* axnum */,
+	Atom                   /* label */,
+	int                    /* minval */,
+	int                    /* maxval */,
+	int                    /* resolution */);
+
 /* Input device properties */
 extern _X_EXPORT void XIDeleteAllDeviceProperties(
         DeviceIntPtr            /* device */
@@ -199,6 +207,14 @@ GrabWindow(
 	GrabMask*              /* eventMask */);
 
 extern int
+GrabTouch(
+	ClientPtr              /* client */,
+	DeviceIntPtr           /* dev */,
+	DeviceIntPtr           /* mod_dev */,
+	GrabParameters*        /* param */,
+	GrabMask*              /* eventMask */);
+
+extern int
 SelectForWindow(
 	DeviceIntPtr           /* dev */,
 	WindowPtr              /* pWin */,
@@ -222,6 +238,10 @@ InputClientGone(
 	WindowPtr              /* pWin */,
 	XID                    /* id */);
 
+extern void
+WindowGone(
+	WindowPtr              /* win */);
+
 extern int
 SendEvent (
 	ClientPtr              /* client */,
diff --git a/include/input.h b/include/input.h
index f96a0a9..60cf714 100644
--- a/include/input.h
+++ b/include/input.h
@@ -104,6 +104,8 @@ typedef struct _InputClients *InputClientsPtr;
 typedef struct _DeviceIntRec *DeviceIntPtr;
 typedef struct _ClassesRec *ClassesPtr;
 typedef struct _SpriteRec *SpritePtr;
+typedef struct _TouchClassRec *TouchClassPtr;
+typedef struct _TouchPointInfo *TouchPointInfoPtr;
 typedef union _GrabMask GrabMask;
 
 typedef struct _EventList {
@@ -314,6 +316,12 @@ extern _X_EXPORT Bool InitAbsoluteClassDeviceStruct(
 extern _X_EXPORT Bool InitFocusClassDeviceStruct(
     DeviceIntPtr /*device*/);
 
+extern _X_EXPORT Bool InitTouchClassDeviceStruct(
+    DeviceIntPtr /*device*/,
+    unsigned int /*max_touches*/,
+    unsigned int /*mode*/,
+    unsigned int /*numAxes*/);
+
 typedef void (*BellProcPtr)(
     int /*percent*/,
     DeviceIntPtr /*device*/,
@@ -463,6 +471,22 @@ extern int GetKeyboardValuatorEvents(
     int key_code,
     const ValuatorMask *mask);
 
+extern int GetTouchEvents(
+    EventListPtr events,
+    DeviceIntPtr pDev,
+    TouchPointInfoPtr ti,
+    uint16_t type,
+    uint32_t flags,
+    const ValuatorMask *mask);
+
+extern int GetTouchOwnershipEvents(
+    EventListPtr events,
+    DeviceIntPtr pDev,
+    TouchPointInfoPtr ti,
+    uint8_t mode,
+    XID resource,
+    uint32_t flags);
+
 extern int GetProximityEvents(
     EventListPtr events,
     DeviceIntPtr pDev,
@@ -525,6 +549,19 @@ extern void SendDevicePresenceEvent(int deviceid, int type);
 extern _X_EXPORT InputAttributes *DuplicateInputAttributes(InputAttributes *attrs);
 extern _X_EXPORT void FreeInputAttributes(InputAttributes *attrs);
 
+/* DDX touch API: create with CreateTouchPoint, use its returned ID to lookup
+ * with FindTouchPoint, and eventually end with FinishTouchPoint. */
+extern TouchPointInfoPtr BeginTouchPoint(DeviceIntPtr dev, uint32_t ddx_id);
+extern TouchPointInfoPtr FindTouchPointByDDXID(DeviceIntPtr dev,
+                                               uint32_t ddx_id);
+extern TouchPointInfoPtr FindTouchPointByClientID(DeviceIntPtr dev,
+                                                  uint32_t client_id);
+extern void EndTouchPoint(DeviceIntPtr dev, TouchPointInfoPtr ti);
+/* Internal use only, DDX this is not for you */
+extern Bool InitTouchPoint(TouchClassPtr touch, int index);
+extern void FreeTouchPoint(DeviceIntPtr dev, int index);
+extern void TouchListenerGone(XID resource);
+
 /* misc event helpers */
 extern Mask GetEventMask(DeviceIntPtr dev, xEvent* ev, InputClientsPtr clients);
 extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event);
diff --git a/include/inputstr.h b/include/inputstr.h
index a19b9a9..3d86b34 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -49,6 +49,8 @@ SOFTWARE.
 #ifndef INPUTSTRUCT_H
 #define INPUTSTRUCT_H
 
+#include <X11/extensions/XI2proto.h>
+
 #include <pixman.h>
 #include "input.h"
 #include "window.h"
@@ -71,7 +73,7 @@ extern _X_EXPORT int CountBits(const uint8_t *mask, int len);
  * events to the protocol, the server will not support these events until
  * this number here is bumped.
  */
-#define XI2LASTEVENT    17 /* XI_RawMotion */
+#define XI2LASTEVENT    XI_TouchMotionUnowned
 #define XI2MASKSIZE     ((XI2LASTEVENT + 7)/8) /* no of bits for masks */
 
 /**
@@ -287,6 +289,42 @@ typedef struct _ValuatorClassRec {
     ValuatorAccelerationRec	accelScheme;
 } ValuatorClassRec, *ValuatorClassPtr;
 
+typedef struct _TouchPointInfo {
+    Bool        active;             /* whether or not the touch is active */
+    Bool        pending_finish;     /* true if the touch is physically inactive
+                                     * but still owned by a grab */
+    uint32_t    client_id;          /* touch ID as seen in client events */
+    uint32_t    ddx_id;             /* touch ID given by the DDX */
+    SpriteRec   sprite;             /* window trace for delivery */
+    int         *valuators;         /* last recorded axis values */
+    int         num_valuators;      /* == TouchClassInfo::num_axes */
+    XID         *listeners;         /* grabs/event selection IDs receiving
+                                     * events for this touch */
+    int         num_listeners;
+    int         num_grabs;          /* number of open grabs on this touch
+                                     * which have not accepted or rejected */
+    Bool        emulate_pointer;
+} TouchPointInfoRec;
+
+typedef struct _TouchAxisInfo {
+    int		resolution;
+    int		min_value;
+    int		max_value;
+    Atom	label;
+} TouchAxisInfoRec, *TouchAxisInfoPtr;
+
+typedef struct _TouchClassRec {
+    TouchAxisInfoPtr   axes;
+    unsigned short     num_axes;
+    TouchPointInfoPtr  touches;
+    unsigned short     num_touches;    /* number of allocated touches */
+    unsigned short     max_touches;    /* maximum number of touches, may be 0 */
+    CARD8              mode;           /* ::XIDirectTouch, XIDependentTouch */
+    uint32_t           next_client_id; /* next client_id to give out */
+    int                x_axis;         /* axis number of x axis */
+    int                y_axis;         /* axis number of y axis */
+} TouchClassRec;
+
 typedef struct _ButtonClassRec {
     int			sourceid;
     CARD8		numButtons;
@@ -391,6 +429,7 @@ typedef struct _LedFeedbackClassRec {
 typedef struct _ClassesRec {
     KeyClassPtr		key;
     ValuatorClassPtr	valuator;
+    TouchClassPtr	touch;
     ButtonClassPtr	button;
     FocusClassPtr	focus;
     ProximityClassPtr	proximity;
@@ -515,6 +554,7 @@ typedef struct _DeviceIntRec {
     int			id;
     KeyClassPtr		key;
     ValuatorClassPtr	valuator;
+    TouchClassPtr	touch;
     ButtonClassPtr	button;
     FocusClassPtr	focus;
     ProximityClassPtr	proximity;
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index c8c7f5f..42b7d0e 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -131,7 +131,7 @@
 
 /* X Input */
 #define SERVER_XI_MAJOR_VERSION			2
-#define SERVER_XI_MINOR_VERSION			0
+#define SERVER_XI_MINOR_VERSION			1
 
 /* XKB */
 #define SERVER_XKB_MAJOR_VERSION		1
diff --git a/mi/mieq.c b/mi/mieq.c
index 01da52a..3a5d4e0 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -269,8 +269,15 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
         case ET_ProximityOut:
         case ET_Hierarchy:
         case ET_DeviceChanged:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
+        case ET_TouchMotionUnowned:
             event->device_event.deviceid = dev->id;
             break;
+        case ET_TouchOwnership:
+            event->touch_ownership_event.deviceid = dev->id;
+            break;
 #if XFreeXDGA
         case ET_DGAEvent:
             break;
-- 
1.7.2.3



More information about the xorg-devel mailing list