[PATCH xserver 16/17] Input: Add initial multitouch support from Xi 2.1

Daniel Stone daniel at fooishbar.org
Tue Dec 28 09:58:07 PST 2010


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>
---
 Xi/exevents.c                  |  439 ++++++++++++++++++++++++++++++++++++++++
 Xi/extinit.c                   |    9 +-
 Xi/xiallowev.c                 |  109 ++++++++++
 Xi/xiallowev.h                 |    2 +
 Xi/xipassivegrab.c             |   18 ++-
 Xi/xiquerydevice.c             |   95 +++++++++
 Xi/xiquerydevice.h             |    3 +
 Xi/xiselectev.c                |   41 ++++
 configure.ac                   |    2 +-
 dix/devices.c                  |   93 +++++++++
 dix/eventconvert.c             |   12 +
 dix/getevents.c                |  114 +++++++++++
 dix/grabs.c                    |   78 +++++++-
 dix/inpututils.c               |   58 ++++++
 dix/window.c                   |    9 +-
 hw/xfree86/common/xf86Xinput.c |   21 ++
 hw/xfree86/common/xf86Xinput.h |    5 +
 include/eventstr.h             |    4 +
 include/exevents.h             |   20 ++
 include/input.h                |   17 ++
 include/inputstr.h             |   38 ++++-
 include/protocol-versions.h    |    2 +-
 mi/mieq.c                      |    3 +
 23 files changed, 1181 insertions(+), 11 deletions(-)

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 327873e..9b19124 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,331 @@ 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, DeviceEvent *ev)
+{
+    TouchClassPtr t = sourcedev->touch;
+    SpritePtr sprite = &ti->sprite;
+    int i;
+
+    if (ev->type != ET_TouchBegin)
+    {
+        if (ev->type == ET_TouchMotion && sprite->spriteTraceGood <= 0)
+            return FALSE;
+
+        return TRUE;
+    }
+
+    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->root_x, ev->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;
+}
+
+/**
+ * Delivers a touch event to all interested grabbing clients.  Will update
+ * ti->num_grabs for all events, and also ti->listeners for TouchBegin
+ * events.  May also update ev->flags.
+ */
+static void
+DeliverTouchGrabEvents(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
+                       DeviceEvent *ev, Window child)
+{
+    SpritePtr sprite = &ti->sprite;
+    DeviceIntPtr masterdev = sourcedev->u.master;
+    DeviceIntPtr deliverdev;
+    GrabPtr grab;
+    xEvent *xi2 = NULL;
+    Mask filter;
+    WindowPtr win;
+    int i, j, err, deliveries;
+
+    ti->num_grabs = 0;
+    for (i = 0; i < sprite->spriteTraceGood; i++)
+    {
+        win = sprite->spriteTrace[i];
+
+        deliverdev = sourcedev;
+        grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
+        if (!grab && masterdev)
+        {
+            deliverdev = masterdev;
+            grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
+        }
+        if (!grab)
+            continue;
+
+        /* Change the device ID in the event to reflect who we're delivering
+         * to. */
+        free(xi2);
+        xi2 = NULL;
+        ev->deviceid = deliverdev->id;
+        err = EventToXI2((InternalEvent *) ev, &xi2);
+        if (err != Success)
+        {
+            ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEvent (%d)\n",
+                   sourcedev->name, err);
+            return;
+        }
+        FixUpEventFromWindow(sprite, xi2, win, child, FALSE);
+        filter = GetEventFilter(deliverdev, xi2);
+
+        /* Stash the list of currently-active listeners away at TouchBegin time,
+         * then subsequently check against that list to make sure we only
+         * deliver to that same list later on, so we don't deliver TouchMotion
+         * events to clients which have never seen the corresponding
+         * TouchBegin. */
+        if (ev->type != ET_TouchBegin)
+        {
+            for (j = 0; j < ti->num_listeners; j++)
+            {
+                if (ti->listeners[j] == grab->resource)
+                    break;
+            }
+            if (j == ti->num_listeners)
+                continue;
+        }
+
+        if (XaceHook(XACE_RECEIVE_ACCESS, rClient(grab), win,
+                     (xEvent *) xi2, 1) != Success)
+            continue;
+        deliveries = TryClientEvents(rClient(grab), deliverdev, xi2, 1,
+                                     filter, filter, NullGrab);
+        if (deliveries > 0)
+        {
+            ti->num_grabs++;
+            /* If we've made a delivery, the next one definitely won't be
+             * to the owner. */
+            ev->flags &= ~XITouchOwner;
+            if (ev->type == ET_TouchBegin)
+                ti->listeners[ti->num_listeners++] = grab->resource;
+        }
+    }
+
+    free(xi2);
+}
+
+/**
+ * Delivers a touch event to all interested selecting clients.  Will update
+ * ti->listeners for TouchBegin events.
+ */
+static void
+DeliverTouchSelectionEvents(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
+                            DeviceEvent *ev, Window child)
+{
+    SpritePtr sprite = &ti->sprite;
+    DeviceIntPtr deliverdev;
+    DeviceIntPtr masterdev = sourcedev->u.master;
+    xEvent *xi2 = NULL;
+    Mask filter = GetEventFilter(sourcedev, xi2);
+    WindowPtr win;
+    OtherInputMasks *inputMasks;
+    InputClients *iclients;
+    int evbyte = GetXI2Type((InternalEvent *) ev) / 8;
+    int i, j, err, deliveries;
+
+    for (i = sprite->spriteTraceGood; 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;
+#define test_xi2_mask(x, d, b, f) ((x)->xi2mask[d][b] & f)
+        if (!test_xi2_mask(inputMasks, sourcedev->id, evbyte, filter) &&
+            !test_xi2_mask(inputMasks, XIAllDevices, evbyte, filter) &&
+            !(masterdev &&
+              (test_xi2_mask(inputMasks, masterdev->id, evbyte, filter) ||
+               test_xi2_mask(inputMasks, XIAllMasterDevices, evbyte, filter))))
+            continue;
+
+        for (; iclients; iclients = iclients->next)
+        {
+            /* Now find the particular client who selected for touch events. */
+            if (test_xi2_mask(iclients, sourcedev->id, evbyte, filter) ||
+                test_xi2_mask(iclients, XIAllDevices, evbyte, filter))
+                deliverdev = sourcedev;
+            else if (masterdev &&
+                     (test_xi2_mask(iclients, masterdev->id, evbyte, filter) ||
+                      test_xi2_mask(iclients, XIAllMasterDevices, evbyte,
+                                    filter)))
+                deliverdev = masterdev;
+            else
+                continue;
+#undef test_xi2_mask
+
+            ev->deviceid = deliverdev->id;
+            err = EventToXI2((InternalEvent *) ev, &xi2);
+            if (err != Success)
+            {
+                ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEvent "
+                       "(%d)\n", sourcedev->name, err);
+                return;
+            }
+            FixUpEventFromWindow(sprite, xi2, win, child, FALSE);
+
+            /* See comment in grab-delivery TouchBegin branch. */
+            if (ev->type != ET_TouchBegin) {
+                for (j = 0; j < ti->num_listeners; j++)
+                {
+                    if (ti->listeners[j] == iclients->resource)
+                        break;
+                }
+                if (j == ti->num_listeners)
+                {
+                    free(xi2);
+                    continue;
+                }
+            }
+
+            if (XaceHook(XACE_RECEIVE_ACCESS, rClient(iclients), win,
+                         (xEvent *) xi2, 1) != Success)
+                continue;
+            deliveries = TryClientEvents(rClient(iclients), deliverdev, xi2, 1,
+                                         filter, filter, NullGrab);
+            /* As only one event selection can receive events, bail as soon as
+             * we find one. */
+            if (ev->type == ET_TouchBegin && deliveries > 0)
+            {
+                ti->listeners[ti->num_listeners++] = iclients->resource;
+                free(xi2);
+                return;
+            }
+            else if (ev->type != ET_TouchBegin)
+            {
+                free(xi2);
+                return;
+            }
+        }
+    }
+}
+
+/**
+ * Processes and delivers a TouchBegin, TouchMotion 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(DeviceEvent *ev, DeviceIntPtr sourcedev)
+{
+    TouchClassPtr t;
+    TouchPointInfoPtr ti;
+    DeviceIntPtr masterdev = NULL;
+    Window child;
+    SpritePtr sprite;
+    int touch;
+
+    /* 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 (!sourcedev->touch)
+        return;
+    t = sourcedev->touch;
+
+    touch = FindTouchPoint(sourcedev, ev->detail.touch);
+    if (touch < 0)
+    {
+        DebugF("[Xi] %s: Received event for inactive touchpoint %d\n",
+               sourcedev->name, ev->detail.touch);
+        return;
+    }
+    ti = &t->touches[touch];
+    sprite = &ti->sprite;
+
+    /* If we get a TouchEnd event but someone still has an open grab, just
+     * flip pending_finish and wait for ownership to settle. */
+    if (ev->type == ET_TouchEnd && ti->num_grabs)
+    {
+        ev->type = ET_TouchMotion;
+        ev->flags |= XITouchPendingFinish;
+        ti->pending_finish = TRUE;
+    }
+
+    /* The first delivery we make will be to the owner, so set the owner flag
+     * here, and clear it once we've made a delivery. */
+    ev->flags |= XITouchOwner;
+
+    /* Make sure we have a valid window trace for event delivery. */
+    if (!EnsureTouchSprite(sourcedev, ti, ev))
+        return;
+
+    /* Now deliver first to grabbing clients, then to clients with an
+     * applicable event selection. */
+    child = sprite->spriteTrace[sprite->spriteTraceGood - 1]->drawable.id;
+    DeliverTouchGrabEvents(sourcedev, ti, ev, child);
+    DeliverTouchSelectionEvents(sourcedev, ti, ev, child);
+
+    if (ev->type == ET_TouchEnd)
+        FinishTouchPoint(sourcedev, ev->detail.touch);
+}
+
+/**
  * Main device event processing function.
  * Called from when processing the events from the event queue.
  *
@@ -954,6 +1305,12 @@ 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_TouchEnd)
+    {
+        ProcessTouchEvent(&ev->device_event, device);
+        return;
     }
 
     if (IsPointerDevice(device))
@@ -1152,6 +1509,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 +1943,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)
@@ -1734,6 +2143,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..8adec8f 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 */
 };
 
 /*****************************************************************
@@ -881,6 +883,9 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
         case XI_KeyRelease:
         case XI_ButtonPress:
         case XI_ButtonRelease:
+        case XI_TouchBegin:
+        case XI_TouchMotion:
+        case XI_TouchEnd:
             SDeviceEvent((xXIDeviceEvent*)from, (xXIDeviceEvent*)to);
             break;
         case XI_RawMotion:
diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c
index 3077e1a..93905fd 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,110 @@ 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, touch, i, nev;
+    uint32_t flags = 0;
+    ValuatorMask *mask = valuator_mask_new(0);
+    EventList *events = InitEventList(GetMaximumEventsNum());
+    DeviceEvent *ev;
+
+    REQUEST(xXIAllowTouchEventsReq);
+    REQUEST_SIZE_MATCH(xXIAllowTouchEventsReq);
+
+    if (!mask || !events)
+        return BadAlloc;
+
+    ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
+    if (ret != Success)
+        return ret;
+    if (!dev->touch)
+    {
+        client->errorValue = stuff->deviceid;
+        return BadDevice;
+    }
+
+    touch = FindTouchPoint(dev, stuff->touchid);
+    if (touch < 0)
+    {
+        client->errorValue = touch;
+        return BadValue;
+    }
+    ti = &dev->touch->touches[touch];
+
+    if (ti->num_listeners == 0 ||
+        CLIENT_ID(ti->listeners[0]) != client->index)
+        return BadAccess;
+
+    if (stuff->mode & XITouchOwnerAccept)
+    {
+        ProcessInputEvents();
+
+        if (stuff->mode & ~(XITouchOwnerAccept | XITouchNoPointerEmulation))
+        {
+            client->errorValue = stuff->mode;
+            return BadValue;
+        }
+
+        if (stuff->mode & XITouchNoPointerEmulation)
+            ti->emulate_pointer = FALSE;
+
+        flags |= XITouchOwnerAccepted;
+        nev = GetTouchEvents(events, dev, stuff->touchid, mask, 0, flags);
+        if (nev == 0)
+            return BadAlloc;
+        for (i = 0; i < nev; i++)
+        {
+            ev = (DeviceEvent *)((events + i)->event);
+            mieqProcessDeviceEvent(dev, (InternalEvent *) ev, NULL);
+        }
+
+        ti->num_listeners = 1;
+    }
+    else if (stuff->mode & XITouchOwnerReject)
+    {
+        if (stuff->mode & ~XITouchOwnerReject)
+        {
+            client->errorValue = stuff->mode;
+            return BadValue;
+        }
+
+        for (i = 0; i < ti->num_listeners - 1; i++)
+            ti->listeners[i] = ti->listeners[i + 1];
+        ti->num_listeners--;
+
+        ProcessInputEvents();
+        nev = GetTouchEvents(events, dev, stuff->touchid, mask, 0, 0);
+        if (nev == 0)
+            return BadAlloc;
+        for (i = 0; i < nev; i++)
+        {
+            ev = (DeviceEvent *)((events + i)->event);
+            mieqProcessDeviceEvent(dev, (InternalEvent *) ev, NULL);
+        }
+    }
+    else
+    {
+        client->errorValue = stuff->mode;
+        return BadValue;
+    }
+
+    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..72ca794 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -152,6 +152,47 @@ 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_TouchEnd)) &&
+                (!BitIsOn(bits, XI_TouchBegin) ||
+                 !BitIsOn(bits, XI_TouchMotion) ||
+                 !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..c532db8 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,84 @@ InitPointerDeviceStruct(DevicePtr device, CARD8 *map, int numButtons, Atom* btn_
 	   InitPtrFeedbackClassDeviceStruct(dev, controlProc));
 }
 
+/**
+ * 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 *valuators;
+    int i;
+
+    if (device->touch)
+        return FALSE;
+
+    if ((mode != XIDirectTouch && mode != XIDependentTouch) ||
+        max_touches == 0 || num_axes < 2)
+        return FALSE;
+
+    if (num_axes > MAX_VALUATORS)
+    {
+        LogMessage(X_WARNING,
+                   "Device '%s' has %d axes, only using first %d.\n",
+                   device->name, num_axes, MAX_VALUATORS);
+        num_axes = MAX_VALUATORS;
+    }
+
+    touch = calloc(1,
+                   sizeof(TouchClassRec) +
+                   num_axes * sizeof(TouchAxisInfoRec) +
+                   max_touches * sizeof(TouchPointInfoRec) +
+                   max_touches * num_axes * sizeof(int));
+    if (!touch)
+        return FALSE;
+
+    touch->axes = (TouchAxisInfoPtr)(touch + 1);
+    touch->touches = (TouchPointInfoPtr)(touch->axes + num_axes);
+
+    valuators = (int *)(touch->touches + max_touches);
+    for (i = 0; i < max_touches; i++)
+    {
+        touch->touches[i].valuators = valuators;
+        valuators += num_axes;
+    }
+
+    for (i = 0; i < max_touches; i++)
+    {
+        SpritePtr sprite = &touch->touches[i].sprite;
+
+        sprite->spriteTrace = calloc(32, sizeof(*sprite->spriteTrace));
+        if (!sprite->spriteTrace)
+        {
+            free(touch);
+            return FALSE;
+        }
+        sprite->spriteTraceSize = 32;
+        sprite->spriteTrace[0] = screenInfo.screens[0]->root;
+        sprite->hot.pScreen = screenInfo.screens[0];
+        sprite->hotPhys.pScreen = screenInfo.screens[0];
+
+        touch->touches[i].id = -1;
+    }
+
+    touch->num_axes = num_axes;
+    touch->num_touches = max_touches;
+    touch->mode = mode;
+    touch->last_touchid = (unsigned int) -1;
+    touch->x_axis = -1;
+    touch->y_axis = -1;
+
+    device->touch = touch;
+
+    return TRUE;
+}
+
 /*
  * 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..63957b5 100644
--- a/dix/eventconvert.c
+++ b/dix/eventconvert.c
@@ -139,6 +139,9 @@ EventToCore(InternalEvent *event, xEvent *core)
         case ET_RawButtonPress:
         case ET_RawButtonRelease:
         case ET_RawMotion:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
             return BadMatch;
         default:
             /* XXX: */
@@ -184,6 +187,9 @@ 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:
             *count = 0;
             *xi = NULL;
             return BadMatch;
@@ -225,6 +231,9 @@ EventToXI2(InternalEvent *ev, xEvent **xi)
         case ET_ButtonRelease:
         case ET_KeyPress:
         case ET_KeyRelease:
+        case ET_TouchBegin:
+        case ET_TouchMotion:
+        case ET_TouchEnd:
             return eventToDeviceEvent(&ev->device_event, xi);
         case ET_ProximityIn:
         case ET_ProximityOut:
@@ -728,6 +737,9 @@ 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;
         default:
             break;
     }
diff --git a/dix/getevents.c b/dix/getevents.c
index 794df42..0da3536 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"
@@ -1287,6 +1288,119 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type, const Valuato
 }
 
 /**
+ * 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, unsigned int touchid,
+               const ValuatorMask *mask_in, Bool end, uint32_t flags)
+{
+    int x, y; /* in screen co-ord space */
+    float x_frac = 0.0, y_frac = 0.0; /* as above */
+    DeviceEvent *event;
+    ValuatorMask mask;
+    int touch, i;
+    TouchClassPtr t = pDev->touch;
+    ScreenPtr scr = pDev->spriteInfo->sprite->hotPhys.pScreen;
+    CARD32 ms = GetTimeInMillis();
+
+    if (!pDev->enabled)
+        return 0;
+
+    if (!t)
+        return 0;
+
+    if (t->x_axis < 0 || t->y_axis < 0)
+        return 0;
+
+    event = (DeviceEvent *)events->event;
+    init_event(pDev, event, ms);
+
+    touch = FindTouchPoint(pDev, touchid);
+    if (touch < 0) {
+        /* We obviously can't finish a non-existent touch. */
+        if (end)
+            return 0;
+
+        /* 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))
+            return 0;
+
+        /* Touch IDs must increase monotonically.
+         * XXX: Deal with this so the drivers don't have to. */
+        if (touchid - t->last_touchid > INT_MAX) {
+            LogMessage(X_WARNING, "[dix] %s: New touch ID %d going backwards, "
+                       "dropping touch.\n", pDev->name, touchid);
+            return 0;
+        }
+
+        touch = CreateTouchPoint(pDev, touchid);
+        if (touch < 0)
+            return 0;
+
+        event->type = ET_TouchBegin;
+        t->last_touchid = touchid;
+    }
+    else if (end) {
+        event->type = ET_TouchEnd;
+    }
+    else {
+        event->type = ET_TouchMotion;
+    }
+
+    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 = t->touches[touch].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 = t->touches[touch].valuators[t->y_axis];
+        y = rescaleValuatorAxis(y, 0.0, &y_frac,
+                                (AxisInfoPtr)(t->axes + t->y_axis),
+                                NULL, scr->height);
+    }
+    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 = touchid;
+    event->flags = flags;
+
+    set_valuators(pDev, event, &mask);
+    for (i = 0; i < t->num_axes; i++)
+    {
+        if (valuator_mask_isset(&mask, i))
+            t->touches[touch].valuators[i] = valuator_mask_get(&mask, i);
+    }
+
+    return 1;
+}
+
+/**
  * Synthesize a single motion event for the core pointer.
  *
  * Used in cursor functions, e.g. when cursor confinement changes, and we need
diff --git a/dix/grabs.c b/dix/grabs.c
index 69c58df..9c9f14b 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,63 @@ 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. */
+static void
+FreeTouchGrab(GrabPtr pGrab)
+{
+    TouchPointInfoPtr ti;
+    DeviceIntPtr dev;
+    InternalEvent *ev;
+    ValuatorMask *mask = valuator_mask_new(0); /* XXX use a static */
+    EventList *events = InitEventList(GetMaximumEventsNum());
+    int i, j, nev;
+
+    if (!mask || !events)
+        FatalError("FreeTouchGrab: couldn't allocate valuator_mask/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];
+            for (j = 0; j < ti->num_listeners; j++)
+            {
+                if (ti->listeners[j] != pGrab->resource)
+                    continue;
+
+                while (j < ti->num_listeners - 1)
+                {
+                    ti->listeners[j] = ti->listeners[j + 1];
+                    break;
+                }
+                ti->num_listeners--;
+                ti->num_grabs--;
+
+                ProcessInputEvents();
+                nev = GetTouchEvents(events, dev, ti->id, mask, 0, 0);
+                for (j = 0; j < nev; j++)
+                {
+                    ev = (InternalEvent *)((events + j)->event);
+                    mieqProcessDeviceEvent(dev, ev, NULL);
+                }
+
+                break;
+            }
+        }
+    }
+}
+
 static void
 FreeGrab(GrabPtr pGrab)
 {
+    if (pGrab->grabtype == GRABTYPE_XI2 && pGrab->type == XI_TouchBegin)
+        FreeTouchGrab(pGrab);
+
     free(pGrab->modifiersDetail.pMask);
     free(pGrab->detail.pMask);
 
@@ -243,6 +298,23 @@ 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_TouchEnd)
+        return XI_TouchBegin;
+
+    return pGrab->type;
+}
+
+/**
  * Compares two grabs and returns TRUE if the first grab matches the second
  * grab. 
  * 
@@ -261,6 +333,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 +362,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 2877804..13e8131 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -548,3 +548,61 @@ CountBits(const uint8_t *mask, int len)
 
     return ret;
 }
+
+int
+FindTouchPoint(DeviceIntPtr dev, unsigned int touchid)
+{
+    int i;
+    TouchClassPtr t = dev->touch;
+
+    if (!t)
+        return -1;
+
+    for (i = 0; i < t->num_touches; i++)
+        if (t->touches[i].active && t->touches[i].id == touchid)
+            return i;
+
+    return -1;
+}
+
+int
+CreateTouchPoint(DeviceIntPtr dev, unsigned int touchid)
+{
+    int i;
+    TouchClassPtr t = dev->touch;
+
+    if (!t)
+        return -1;
+
+    if (FindTouchPoint(dev, touchid) >= 0)
+        return -1;
+
+    for (i = 0; i < t->num_touches; i++)
+        if (!t->touches[i].active) {
+            t->touches[i].active = TRUE;
+            t->touches[i].id = touchid;
+            return i;
+        }
+
+    return -1;
+}
+
+void
+FinishTouchPoint(DeviceIntPtr dev, unsigned int touchid)
+{
+    int touch = FindTouchPoint(dev, touchid);
+    TouchPointInfoPtr ti;
+
+    if (touch < 0)
+        return;
+
+    ti = &dev->touch->touches[touch];
+
+    ti->active = FALSE;
+    ti->pending_finish = FALSE;
+    ti->num_grabs = 0;
+    ti->sprite.spriteTraceGood = 0;
+    free(ti->listeners);
+    ti->listeners = NULL;
+    ti->num_listeners = 0;
+}
diff --git a/dix/window.c b/dix/window.c
index d140dda..50d12a5 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"
@@ -2869,8 +2870,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;
 }
 
@@ -2953,8 +2956,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..ce84079 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,15 @@ xf86EnableDevice(DeviceIntPtr dev)
     EnableDevice(dev, TRUE);
 }
 
+void
+xf86PostTouchEvent(DeviceIntPtr dev, unsigned int touchid,
+                   const ValuatorMask *mask, Bool end, uint32_t flags)
+{
+    int i, nevents;
+
+    nevents = GetTouchEvents(xf86Events, dev, touchid, mask, end, flags);
+    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..cf2a4af 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, unsigned int touchid,
+                                         const ValuatorMask *mask, Bool end,
+                                         uint32_t flags);
 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/eventstr.h b/include/eventstr.h
index e1f5003..4d2ae68 100644
--- a/include/eventstr.h
+++ b/include/eventstr.h
@@ -65,6 +65,9 @@ enum EventType {
     ET_RawButtonRelease,
     ET_RawMotion,
     ET_XQuartz,
+    ET_TouchBegin,
+    ET_TouchEnd,
+    ET_TouchMotion,
     ET_Internal = 0xFF /* First byte */
 };
 
@@ -90,6 +93,7 @@ struct _DeviceEvent
     union {
         uint32_t button;  /**< Button number */
         uint32_t key;     /**< Key code */
+        uint32_t touch;   /**< Touch 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 */
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 56992d1..8728ae9 100644
--- a/include/input.h
+++ b/include/input.h
@@ -314,6 +314,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 +469,14 @@ extern int GetKeyboardValuatorEvents(
     int key_code,
     const ValuatorMask *mask);
 
+extern int GetTouchEvents(
+    EventListPtr events,
+    DeviceIntPtr pDev,
+    unsigned int touchid,
+    const ValuatorMask *mask,
+    Bool end,
+    uint32_t flags);
+
 extern int GetProximityEvents(
     EventListPtr events,
     DeviceIntPtr pDev,
@@ -524,6 +538,9 @@ extern DeviceIntPtr GetXTestDevice(DeviceIntPtr master);
 extern void SendDevicePresenceEvent(int deviceid, int type);
 extern _X_EXPORT InputAttributes *DuplicateInputAttributes(InputAttributes *attrs);
 extern _X_EXPORT void FreeInputAttributes(InputAttributes *attrs);
+extern int CreateTouchPoint(DeviceIntPtr dev, unsigned int touchid);
+extern int FindTouchPoint(DeviceIntPtr dev, unsigned int touchid);
+extern void FinishTouchPoint(DeviceIntPtr dev, unsigned int touchid);
 
 /* misc event helpers */
 extern Mask GetEventMask(DeviceIntPtr dev, xEvent* ev, InputClientsPtr clients);
diff --git a/include/inputstr.h b/include/inputstr.h
index 7de7405..0385828 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_TouchMotion
 #define XI2MASKSIZE     ((XI2LASTEVENT + 7)/8) /* no of bits for masks */
 
 /**
@@ -284,6 +286,38 @@ 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    id;
+    SpriteRec   sprite;             /* window trace for delivery */
+    int         *valuators;         /* last-recorded valuators */
+    XID         *listeners;         /* grabs/event selection IDs receiving
+                                     * events for this touch */
+    int         num_listeners;
+    int         num_grabs;
+    Bool        emulate_pointer;
+} TouchPointInfoRec, *TouchPointInfoPtr;
+
+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;
+    CARD8                 mode;         /* ::XIDirectTouch, XIDependentTouch */
+    unsigned int          last_touchid;
+    int                   x_axis;       /* axis number */
+    int                   y_axis;       /* axis number */
+} TouchClassRec, *TouchClassPtr;
+
 typedef struct _ButtonClassRec {
     int			sourceid;
     CARD8		numButtons;
@@ -388,6 +422,7 @@ typedef struct _LedFeedbackClassRec {
 typedef struct _ClassesRec {
     KeyClassPtr		key;
     ValuatorClassPtr	valuator;
+    TouchClassPtr	touch;
     ButtonClassPtr	button;
     FocusClassPtr	focus;
     ProximityClassPtr	proximity;
@@ -512,6 +547,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..42aec2c 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -269,6 +269,9 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
         case ET_ProximityOut:
         case ET_Hierarchy:
         case ET_DeviceChanged:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
             event->device_event.deviceid = dev->id;
             break;
 #if XFreeXDGA
-- 
1.7.2.3



More information about the xorg-devel mailing list