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

Daniel Stone daniel at fooishbar.org
Sun Sep 19 22:03:06 PDT 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.  This is a full implementation of the Xi 2.1 additions.

Signed-off-by: Daniel Stone <daniel at fooishbar.org>
---
 Xext/geext.c                   |    2 -
 Xext/security.c                |    2 -
 Xi/exevents.c                  |  423 ++++++++++++++++++++++++++++++++++++++++
 Xi/extinit.c                   |   65 ++++++
 Xi/xiallowev.c                 |   38 +++-
 Xi/xipassivegrab.c             |   11 +-
 Xi/xiquerydevice.c             |  125 ++++++++++++
 Xi/xiquerydevice.h             |    2 +
 Xi/xiselectev.c                |   21 ++
 configure.ac                   |    2 +-
 dix/devices.c                  |   56 ++++++
 dix/eventconvert.c             |   67 +++++++
 dix/events.c                   |   42 +++--
 dix/getevents.c                |  154 +++++++++++++++
 dix/inpututils.c               |   64 ++++++
 hw/xfree86/common/xf86Xinput.c |   26 +++
 hw/xfree86/common/xf86Xinput.h |   14 ++
 include/dix.h                  |    7 +
 include/events.h               |    2 +
 include/eventstr.h             |   42 ++++
 include/exevents.h             |    9 +
 include/input.h                |   40 ++++
 include/inputstr.h             |   39 ++++-
 include/protocol-versions.h    |    2 +-
 mi/mieq.c                      |   10 +
 25 files changed, 1238 insertions(+), 27 deletions(-)

diff --git a/Xext/geext.c b/Xext/geext.c
index 8319c92..b37c1a0 100644
--- a/Xext/geext.c
+++ b/Xext/geext.c
@@ -33,8 +33,6 @@
 #include "geext.h"
 #include "protocol-versions.h"
 
-#define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
-
 DevPrivateKeyRec GEClientPrivateKeyRec;
 
 int RT_GECLIENT  = 0;
diff --git a/Xext/security.c b/Xext/security.c
index 7eb95de..8673880 100644
--- a/Xext/security.c
+++ b/Xext/security.c
@@ -154,8 +154,6 @@ SecurityLookupRequestName(ClientPtr client)
 }
 
 
-#define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
-
 /* SecurityDeleteAuthorization
  *
  * Arguments:
diff --git a/Xi/exevents.c b/Xi/exevents.c
index 1f02cb0..038b7dc 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -87,6 +87,7 @@ SOFTWARE.
 	Mod3Mask | Mod4Mask | Mod5Mask )
 #define AllButtonsMask ( \
 	Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
+#define RootWindow(dev) dev->spriteInfo->sprite->spriteTrace[0]
 
 Bool ShouldFreeInputMasks(WindowPtr /* pWin */ ,
 				 Bool	/* ignoreSelectedEvents */
@@ -572,6 +573,49 @@ DeepCopyPointerClasses(DeviceIntPtr from, DeviceIntPtr to)
         to->valuator      = NULL;
     }
 
+    if (from->touch)
+    {
+        TouchClassPtr t;
+        int i;
+
+        if (!to->touch)
+        {
+            classes = to->unused_classes;
+            to->touch = classes->touch;
+            if (to->touch)
+                classes->touch = NULL;
+        }
+
+        to->touch = realloc(to->touch,
+                            sizeof(*to->touch) +
+                             (from->touch->num_touches *
+                              sizeof(*from->touch->touches)));
+        t = to->touch;
+        if (!t)
+            FatalError("[Xi] no memory for class shift.\n");
+
+        t->min_x = from->touch->min_x;
+        t->max_x = from->touch->max_x;
+        t->min_y = from->touch->min_y;
+        t->max_y = from->touch->max_y;
+        t->min_touch_width = from->touch->min_touch_width;
+        t->max_touch_width = from->touch->max_touch_width;
+        t->max_touches = from->touch->max_touches;
+        t->num_touches = from->touch->num_touches;
+        t->touches = (TouchInfoPtr) &t[1];
+        memcpy(t->touches, from->touch->touches,
+               t->num_touches * sizeof(*t->touches));
+        for (i = 0; i < t->num_touches; i++)
+            t->touches[i].sourceid = from->id;
+    }
+    else if (to->touch && !from->touch)
+    {
+        ClassesPtr classes;
+        classes = to->unused_classes;
+        classes->touch = to->touch;
+        to->touch = NULL;
+    }
+
     if (from->button)
     {
         if (!to->button)
@@ -931,6 +975,340 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
     }
 }
 
+static Bool
+CheckTouchQueueSize(TouchInfoPtr touch)
+{
+    InternalEvent *tmp;
+
+    if (touch->num_frozen_events < touch->size_frozen_events)
+        return TRUE;
+
+    touch->size_frozen_events += 10;
+    tmp = realloc(touch->frozen_events,
+                  touch->size_frozen_events * sizeof(InternalEvent));
+    if (!tmp)
+    {
+        touch->size_frozen_events -= 10;
+        return FALSE;
+    }
+    touch->frozen_events = tmp;
+
+    return TRUE;
+}
+
+static void
+ProcessTouchMotionEvent(TouchMotionEvent *ev, DeviceIntPtr device)
+{
+    TouchInfoPtr touch = FindTouchPoint(device, ev->touchid);
+    xEvent *xi2;
+    int ret;
+    WindowPtr win;
+
+    if (!touch)
+    {
+        DebugF("[Xi] %s: Received TouchMotion event for dead touchpoint %d\n",
+               device->name, ev->touchid);
+        return;
+    }
+
+    /* If the touch is currently frozen, add it to the queue if possible or
+     * drop the event if not, then get out. */
+    if (touch->frozen)
+    {
+        if (CheckTouchQueueSize(touch))
+            touch->frozen_events[touch->num_frozen_events++] =
+                *(InternalEvent*)ev;
+        return;
+    }
+
+    if (ev->mask & XITouchXMask)
+        touch->last_x = ev->x;
+    if (ev->mask & XITouchYMask)
+        touch->last_y = ev->y;
+    if (ev->mask & XITouchTouchSizeMask)
+    {
+        touch->last_touch_major = ev->touch_width_major;
+        touch->last_touch_minor = ev->touch_width_minor;
+    }
+    if (ev->mask & XITouchToolSizeMask)
+    {
+        touch->last_tool_major = ev->tool_width_major;
+        touch->last_tool_minor = ev->tool_width_minor;
+    }
+    if (ev->mask & XITouchOrientationMask)
+        touch->last_orientation = ev->orientation;
+
+    ret = EventToXI2((InternalEvent *) ev, &xi2);
+    if (ret != Success)
+    {
+        ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchMotionEvent (%d)\n",
+               device->name, ret);
+        return;
+    }
+
+    if (dixLookupWindow(&win, touch->win, clients[touch->client_id],
+                        DixReadAccess) == Success)
+        FixUpEventFromWindow(device, xi2, win, None, TRUE);
+
+    TryClientEvents(clients[touch->client_id], device, xi2, 1,
+                    0, NoEventMask, NULL);
+
+    free(xi2);
+}
+
+static void
+ProcessTouchEndEvent(TouchStateEvent *ev, DeviceIntPtr device)
+{
+    TouchInfoPtr touch = FindTouchPoint(device, ev->touchid);
+    xEvent *xi2;
+    int err;
+
+    if (!touch)
+    {
+        DebugF("[Xi] %s: Received TouchFini for dead touchpoint %d\n",
+               device->name, ev->touchid);
+        return;
+    }
+
+    /* If the touch is currently frozen, add it to the queue; if we can't
+     * enlarge the queue to fit TouchEnd, then just stomp a motion event. */
+    if (touch->frozen)
+    {
+        if (CheckTouchQueueSize(touch))
+            touch->num_frozen_events++;
+        touch->frozen_events[touch->num_frozen_events] = *(InternalEvent*)ev;
+        return;
+    }
+
+    err = EventToXI2((InternalEvent *) ev, &xi2);
+    if (err != Success)
+    {
+        ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEnd (%d)\n",
+               device->name, err);
+        return;
+    }
+
+    TryClientEvents(clients[touch->client_id], device, xi2, 1,
+                    0, NoEventMask, NULL);
+    FinishTouchPoint(device, ev->touchid);
+}
+
+static void
+ProcessTouchQueue(TouchInfoPtr touch, DeviceIntPtr device)
+{
+    int j;
+
+    /* The first queued event is this TouchBegin, so skip it. */
+    for (j = 1; j < touch->num_frozen_events; j++)
+    {
+        if (touch->frozen_events[j].any.type == ET_TouchMotion)
+            ProcessTouchMotionEvent((TouchMotionEvent*)&touch->frozen_events[j],
+                                    device);
+        else if (touch->frozen_events[j].any.type == ET_TouchEnd)
+            ProcessTouchEndEvent((TouchStateEvent*)&touch->frozen_events[j],
+                                 device);
+        else
+            FatalError("[Xi] %s: Unknown event type %d in touch queue\n",
+                       device->name, touch->frozen_events[j].any.type);
+    }
+    touch->num_frozen_events = 0;
+}
+
+static void
+ProcessTouchBeginEvent(TouchStateEvent *ev, DeviceIntPtr device)
+{
+    TouchInfoPtr touch = FindTouchPoint(device, ev->touchid);
+    WindowPtr win;
+    SpriteRec tsprite;
+    SpritePtr sprite;
+    ClientPtr client;
+    GrabRec tempGrab;
+    Window child = None;
+    xEvent *xi2;
+    int i = 0, err;
+
+    if (!touch)
+    {
+        DebugF("[Xi] %s: Received TouchBegin event for dead touchpoint %d\n",
+               device->name, ev->touchid);
+        return;
+    }
+
+    err = EventToXI2((InternalEvent *) ev, &xi2);
+    if (err != Success)
+    {
+        ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchBegin (%d)\n",
+               device->name, err);
+        return;
+    }
+
+    /* In Absolute focus_mode, we focus immediately under the touchpoint, so
+     * we need to build a window trace; in Relative, we just use the sprite. */
+    if (device->touch->focus_mode == Absolute)
+    {
+        /* Fake sprite for XYToWindow. */
+        memset(&tsprite, 0, sizeof(tsprite));
+        sprite = &tsprite;
+        tsprite.spriteTraceSize = 10;
+        tsprite.spriteTrace = calloc(tsprite.spriteTraceSize,sizeof(WindowPtr));
+        if (!tsprite.spriteTrace)
+            goto out;
+        tsprite.spriteTrace[0] = RootWindow(device);
+        XYToWindow(sprite, ev->root_x, ev->root_y);
+    } else
+    {
+        sprite = device->spriteInfo->sprite;
+    }
+
+    tempGrab.type = GetXI2Type((InternalEvent *) ev);
+    tempGrab.grabtype = GRABTYPE_XI2;
+    tempGrab.device = device;
+    tempGrab.detail.exact = ev->tool;
+    tempGrab.detail.pMask = NULL;
+    tempGrab.modifiersDetail.pMask = NULL;
+    tempGrab.next = NULL;
+
+    /* If we've already got win, then it means we're replaying the touch
+     * stream, so find it in the trace and then restart delivery from the next
+     * child. */
+    if (touch->win != None)
+    {
+        for (; i < sprite->spriteTraceGood; i++)
+        {
+            if (sprite->spriteTrace[i]->drawable.id == touch->win)
+            {
+                i++;
+                break;
+            }
+        }
+    }
+    /* Now walk the window tree downwards looking for (more) grabs. */
+    for (; i < sprite->spriteTraceGood; i++)
+    {
+        GrabPtr grab;
+
+        win = sprite->spriteTrace[i];
+        for (grab = wPassiveGrabs(win); grab; grab = grab->next)
+        {
+            DeviceIntPtr gdev;
+            XkbSrvInfoPtr xkbi = NULL;
+
+            if (grab->grabtype != GRABTYPE_XI2 || grab->type != XI_TouchBegin)
+                continue;
+
+            if (!IsMaster(grab->device) && device->u.master)
+                gdev = GetMaster(device, MASTER_KEYBOARD);
+            else
+                gdev = grab->modifierDevice;
+            if (gdev && gdev->key)
+                xkbi = gdev->key->xkbInfo;
+
+            tempGrab.window = win;
+            tempGrab.modifierDevice = grab->modifierDevice;
+            tempGrab.modifiersDetail.exact = xkbi ? xkbi->state.grab_mods : 0;
+
+            if (GrabMatchesSecond(&tempGrab, grab, FALSE))
+            {
+                client = rClient(grab);
+                FixUpEventFromWindow(device, xi2, grab->window, None, TRUE);
+                err = TryClientEvents(client, device, xi2, 1, 0, NoEventMask,
+                                      grab);
+                if (!err)
+                    continue; /* Couldn't deliver, keep trying */
+
+                /* At this point, we've delivered our grabbed event; if it's
+                 * a Sync grab, then freeze the device, else replay anything
+                 * that might be left in the queue. */
+                touch->client_id = client->index;
+                touch->win = win->drawable.id;
+
+                if (grab->keyboardMode == GrabModeSync)
+                {
+                    touch->frozen = 1;
+                    touch->num_frozen_events = 1;
+                    if (!CheckTouchQueueSize(touch))
+                        FatalError("[Xi] %s: Couldn't allocate frozen touch events\n",
+                                   device->name);
+                    touch->frozen_events[0] = *(InternalEvent*)ev;
+                } else
+                {
+                    ProcessTouchQueue(touch, device);
+                }
+                goto out;
+            }
+        }
+    }
+
+    /* If we get this far, we've gone through all the grabs and are now
+     * attempting normal window delivery. */
+    win = sprite->spriteTrace[sprite->spriteTraceGood - 1];
+    while (win)
+    {
+        OtherInputMasks *masks = wOtherInputMasks(win);
+
+        if (masks && GetWindowXI2Mask(device, win, xi2)) {
+            InputClients *iclients = masks->inputClients;
+            Mask mask = GetEventMask(device, xi2, iclients);
+            Mask filter = GetEventFilter(device, xi2);
+
+            client = rClient(iclients);
+            FixUpEventFromWindow(device, xi2, win, child, FALSE);
+            if (XaceHook(XACE_RECEIVE_ACCESS, client, win, xi2, 1) == Success)
+            {
+                err = TryClientEvents(client, device, xi2, 1, mask, filter,
+                                      NULL);
+                if (err)
+                {
+                    touch->client_id = client->index;
+                    touch->win = win->drawable.id;
+                    goto out;
+                }
+            }
+
+        }
+
+        child = win->drawable.id;
+        win = win->parent;
+    }
+
+out:
+    free(xi2);
+}
+
+void
+AllowTouches(ClientPtr client, TimeStamp time, DeviceIntPtr device,
+             int mode, uint32_t touchid)
+{
+    TouchInfoPtr touch = FindTouchPoint(device, touchid);
+
+    if (!touch)
+    {
+        DebugF("[Xi] %s: Received AllowEvents for dead touchpoint %d\n",
+               device->name, touchid);
+        return;
+    }
+
+    if (!touch->frozen || touch->num_frozen_events == 0)
+        return;
+
+    if (touch->frozen_events[0].any.type != ET_TouchBegin)
+    {
+        ErrorF("[Xi] %s: Corrupted touchstream queue, type %d at head\n",
+               device->name, touch->frozen_events[0].any.type);
+        return;
+    }
+
+    /* Either replay the TouchBegin down towards the next window, or flush the
+     * TouchMotion/TouchEnd queue out to the grabbing client, depending on the
+     * mode. */
+    touch->frozen = 0;
+    if (mode == XIReplayTouch)
+        ProcessTouchBeginEvent((TouchStateEvent *) &touch->frozen_events[0],
+                               device);
+    else
+        ProcessTouchQueue(touch, device);
+}
+
 /**
  * Main device event processing function.
  * Called from when processing the events from the event queue.
@@ -960,6 +1338,18 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
     {
         ProcessRawEvent(&ev->raw_event, device);
         return;
+    } else if (ev->any.type == ET_TouchBegin)
+    {
+        ProcessTouchBeginEvent(&ev->touch_state_event, device);
+        return;
+    } else if (ev->any.type == ET_TouchEnd)
+    {
+        ProcessTouchEndEvent(&ev->touch_state_event, device);
+        return;
+    } else if (ev->any.type == ET_TouchMotion)
+    {
+        ProcessTouchMotionEvent(&ev->touch_motion_event, device);
+        return;
     }
 
     if (IsPointerDevice(device))
@@ -1563,6 +1953,39 @@ GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
     return AddPassiveGrabToList(client, grab);
 }
 
+/* TouchBegin grab */
+int
+GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev, int tool,
+          GrabParameters *param, GrabMask *mask)
+{
+    WindowPtr pWin;
+    GrabPtr grab;
+    Mask access_mode = DixGrabAccess;
+    int rc;
+
+    rc = CheckGrabValues(client, param);
+    if (rc != Success)
+        return rc;
+
+    rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
+    if (rc != Success)
+	return rc;
+    if (param->this_device_mode == GrabModeSync)
+	access_mode |= DixFreezeAccess;
+    rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, access_mode);
+    if (rc != Success)
+	return rc;
+
+    /* FIXME: tool gets truncated to 8 bits here */
+    grab = CreateGrab(client->index, dev, mod_dev, pWin, GRABTYPE_XI2,
+                      mask, param, XI_TouchBegin, tool, NULL, NULL);
+
+    if (!grab)
+        return BadAlloc;
+
+    return AddPassiveGrabToList(client, grab);
+}
+
 int
 SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
 		Mask mask, Mask exclusivemasks)
diff --git a/Xi/extinit.c b/Xi/extinit.c
index 7edadea..556c060 100644
--- a/Xi/extinit.c
+++ b/Xi/extinit.c
@@ -812,6 +812,62 @@ static void SXIPropertyEvent(xXIPropertyEvent *from, xXIPropertyEvent *to)
     swapl(&to->property, n);
 }
 
+static void SXITouchStateEvent(xXITouchStateEvent *from,
+                               xXITouchStateEvent *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);
+    swapl(&to->touchid, n);
+    swapl(&to->tool, n);
+    swaps(&to->sourceid, n);
+    swapl(&to->root, n);
+    swapl(&to->event, n);
+    swapl(&to->child, n);
+}
+
+static void SXITouchMotionEvent(xXITouchMotionEvent *from,
+                                xXITouchMotionEvent *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);
+    swaps(&to->mask, n);
+    swapl(&to->touchid, n);
+    swapl(&to->root, n);
+    swapl(&to->event, n);
+    swapl(&to->child, n);
+    swapl(&to->root_x, n);
+    swapl(&to->root_y, n);
+    swapl(&to->event_x, n);
+    swapl(&to->event_y, n);
+    swapl(&to->x.integral, n);
+    swapl(&to->x.frac, n);
+    swapl(&to->y.integral, n);
+    swapl(&to->y.frac, n);
+    swapl(&to->touch_width_major.integral, n);
+    swapl(&to->touch_width_major.frac, n);
+    swapl(&to->touch_width_minor.integral, n);
+    swapl(&to->touch_width_minor.frac, n);
+    swapl(&to->tool_width_major.integral, n);
+    swapl(&to->tool_width_major.frac, n);
+    swapl(&to->tool_width_minor.integral, n);
+    swapl(&to->tool_width_minor.frac, n);
+    swaps(&to->orientation, n);
+    swaps(&to->flags, n);
+}
+
 static void SRawEvent(xXIRawEvent *from, xXIRawEvent *to)
 {
     char n;
@@ -889,6 +945,15 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
         case XI_RawButtonRelease:
             SRawEvent((xXIRawEvent*)from, (xXIRawEvent*)to);
             break;
+        case XI_TouchBegin:
+        case XI_TouchEnd:
+            SXITouchStateEvent((xXITouchStateEvent*)from,
+                               (xXITouchStateEvent*)to);
+            break;
+        case XI_TouchMotion:
+            SXITouchMotionEvent((xXITouchMotionEvent*)from,
+                                (xXITouchMotionEvent*)to);
+            break;
         default:
             ErrorF("[Xi] Unknown event type to swap. This is a bug.\n");
             break;
diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c
index 3077e1a..5de43ff 100644
--- a/Xi/xiallowev.c
+++ b/Xi/xiallowev.c
@@ -44,6 +44,7 @@
 int
 SProcXIAllowEvents(ClientPtr client)
 {
+    xXIAllowEventsDetailReq *req;
     char n;
 
     REQUEST(xXIAllowEventsReq);
@@ -52,6 +53,13 @@ SProcXIAllowEvents(ClientPtr client)
     swaps(&stuff->deviceid, n);
     swapl(&stuff->time, n);
 
+    /* See comment in ProcXIAllowEvents. */
+    if ((stuff->length << 2) == sizeof(xXIAllowEventsDetailReq))
+    {
+        req = (xXIAllowEventsDetailReq *) stuff;
+        swapl(&req->detail, n);
+    }
+
     return ProcXIAllowEvents(client);
 }
 
@@ -61,17 +69,31 @@ ProcXIAllowEvents(ClientPtr client)
     TimeStamp time;
     DeviceIntPtr dev;
     int ret = Success;
+    xXIAllowEventsDetailReq req;
 
-    REQUEST(xXIAllowEventsReq);
-    REQUEST_SIZE_MATCH(xXIAllowEventsReq);
+    /* XI 2.1 adds a detail field to the request to support touch events,
+     * so we need to cater for both versions of the request. */
+    if ((client->req_len << 2) == sizeof(xXIAllowEventsDetailReq))
+    {
+        req = *(xXIAllowEventsDetailReq*)client->requestBuffer;
+    }
+    else if ((client->req_len << 2) == sizeof(xXIAllowEventsReq))
+    {
+        memcpy(&req, client->requestBuffer, sizeof(xXIAllowEventsReq));
+        req.detail = 0;
+    }
+    else
+    {
+        return BadLength;
+    }
 
-    ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
+    ret = dixLookupDevice(&dev, req.deviceid, client, DixGetAttrAccess);
     if (ret != Success)
 	return ret;
 
-    time = ClientTimeToServerTime(stuff->time);
+    time = ClientTimeToServerTime(req.time);
 
-    switch (stuff->mode) {
+    switch (req.mode) {
     case XIReplayDevice:
 	AllowSome(client, time, dev, NOT_GRABBED);
 	break;
@@ -93,8 +115,12 @@ ProcXIAllowEvents(ClientPtr client)
         if (IsMaster(dev))
             AllowSome(client, time, dev, THAWED_BOTH);
 	break;
+    case XIAcceptTouch:
+    case XIReplayTouch:
+        AllowTouches(client, time, dev, req.mode, req.detail);
+        break;
     default:
-	client->errorValue = stuff->mode;
+	client->errorValue = req.mode;
 	ret = BadValue;
     }
 
diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
index 2966145..5bafe53 100644
--- a/Xi/xipassivegrab.c
+++ b/Xi/xipassivegrab.c
@@ -105,7 +105,8 @@ 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;
@@ -185,6 +186,10 @@ ProcXIPassiveGrabDevice(ClientPtr client)
                 status = GrabWindow(client, dev, stuff->grab_type,
                                     &param, &mask);
                 break;
+            case XIGrabtypeTouchBegin:
+                status = GrabTouch(client, dev, mod_dev, stuff->detail,
+                                   &param, &mask);
+                break;
         }
 
         if (status != GrabSuccess)
@@ -263,7 +268,8 @@ ProcXIPassiveUngrabDevice(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;
@@ -294,6 +300,7 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
         case XIGrabtypeKeycode:  tempGrab.type = XI_KeyPress;    break;
         case XIGrabtypeEnter:   tempGrab.type = XI_Enter;       break;
         case XIGrabtypeFocusIn: tempGrab.type = XI_FocusIn;     break;
+        case XIGrabtypeTouchBegin: tempGrab.type = XI_TouchBegin; break;
     }
     tempGrab.grabtype = GRABTYPE_XI2;
     tempGrab.modifierDevice = mod_dev;
diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
index 303c8b2..f913d2f 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(xXITouchClassInfo);
+        len += dev->touch->num_touches * sizeof(xXITouchInfo);
+    }
+
     return len;
 }
 
@@ -373,6 +379,114 @@ SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info)
     swaps(&info->sourceid, n);
 }
 
+/**
+ * List information for the given touchpoint.
+ *
+ * @return The number of bytes written into info.
+ */
+int
+ListTouchInfo(DeviceIntPtr dev, xXITouchClassInfo* info, int touchnum,
+              Bool reportState)
+{
+    TouchClassPtr t = dev->touch;
+    xXITouchInfo *tinfo;
+    int i;
+
+    info->type = XITouchClass;
+    info->length = sizeof(xXITouchClassInfo) >> 2;
+    info->length += (t->num_touches * sizeof(xXITouchInfo)) >> 2;
+    info->sourceid = dev->id;
+    info->mode = t->xy_mode;
+    info->min_x = t->min_x;
+    info->max_x = t->max_x;
+    info->min_y = t->min_y;
+    info->max_y = t->max_y;
+    info->min_touch_width = t->min_touch_width;
+    info->max_touch_width = t->max_touch_width;
+    info->max_touches = t->max_touches;
+    info->num_touches = t->num_touches;
+
+    tinfo = (xXITouchInfo *) &info[1];
+    for (i = 0; i < t->num_touches; i++)
+    {
+        tinfo->length = sizeof(xXITouchInfo) >> 2;
+        tinfo->touchid = t->touches[i].touchid;
+        tinfo->tool = t->touches[i].tool;
+
+        if (reportState)
+        {
+            tinfo->x = t->touches[i].last_x;
+            tinfo->y = t->touches[i].last_y;
+            tinfo->touch_major = t->touches[i].last_touch_major;
+            tinfo->touch_minor = t->touches[i].last_touch_minor;
+            tinfo->tool_major = t->touches[i].last_tool_major;
+            tinfo->tool_minor = t->touches[i].last_tool_minor;
+            tinfo->orientation = t->touches[i].last_orientation;
+        }
+        else {
+            tinfo->x = info->min_x;
+            tinfo->y = info->min_y;
+            tinfo->touch_major = info->min_touch_width;
+            tinfo->touch_minor = info->min_touch_width;
+            tinfo->tool_major = info->min_touch_width;
+            tinfo->tool_minor = info->min_touch_width;
+            tinfo->orientation = 0;
+        }
+
+        tinfo++;
+    }
+
+    return info->length << 2;
+}
+
+static void
+SwapTouchInfo(DeviceIntPtr dev, xXITouchClassInfo* info)
+{
+    xXITouchInfo *tinfo;
+    int num_touches = info->num_touches;
+    int i;
+    char n;
+
+    swaps(&info->type, n);
+    swaps(&info->length, n);
+    swaps(&info->sourceid, n);
+    swapl(&info->num_touches, n);
+    swapl(&info->max_touches, n);
+    swapl(&info->min_x.integral, n);
+    swapl(&info->min_x.frac, n);
+    swapl(&info->max_x.integral, n);
+    swapl(&info->max_x.frac, n);
+    swapl(&info->min_y.integral, n);
+    swapl(&info->min_y.frac, n);
+    swapl(&info->max_y.integral, n);
+    swapl(&info->max_y.frac, n);
+    swapl(&info->min_touch_width.integral, n);
+    swapl(&info->min_touch_width.frac, n);
+    swapl(&info->max_touch_width.integral, n);
+    swapl(&info->max_touch_width.frac, n);
+
+    tinfo = (xXITouchInfo *) &info[1];
+    for (i = 0; i < num_touches; i++) {
+        swaps(&tinfo->length, n);
+        swapl(&tinfo->touchid, n);
+        swapl(&tinfo->tool, n);
+        swapl(&tinfo->x.integral, n);
+        swapl(&tinfo->x.frac, n);
+        swapl(&tinfo->y.integral, n);
+        swapl(&tinfo->y.frac, n);
+        swapl(&tinfo->touch_major.integral, n);
+        swapl(&tinfo->touch_major.frac, n);
+        swapl(&tinfo->touch_minor.integral, n);
+        swapl(&tinfo->touch_minor.frac, n);
+        swapl(&tinfo->tool_major.integral, n);
+        swapl(&tinfo->tool_major.frac, n);
+        swapl(&tinfo->tool_minor.integral, n);
+        swapl(&tinfo->tool_minor.frac, n);
+        swaps(&tinfo->orientation, n);
+        tinfo++;
+    }
+}
+
 int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
 {
     DeviceIntPtr master = dev->u.master;
@@ -462,6 +576,14 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
         total_len += len;
     }
 
+    if (dev->touch)
+    {
+        (*nclasses)++;
+        len = ListTouchInfo(dev, (xXITouchClassInfo*)any, i, rc == Success);
+        any += len;
+        total_len += len;
+    }
+
     return total_len;
 }
 
@@ -489,6 +611,9 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
             case XIValuatorClass:
                 SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
                 break;
+            case XITouchClass:
+                SwapTouchInfo(dev, (xXITouchClassInfo*)any);
+                break;
         }
 
         any += len * 4;
diff --git a/Xi/xiquerydevice.h b/Xi/xiquerydevice.h
index 02f0659..9700ca7 100644
--- a/Xi/xiquerydevice.h
+++ b/Xi/xiquerydevice.h
@@ -44,4 +44,6 @@ 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, xXITouchClassInfo* info,
+	          int axisnumber, Bool reportState);
 #endif /* QUERYDEV_H */
diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
index 7aa3f0a..e64b898 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -141,6 +141,27 @@ ProcXISelectEvents(ClientPtr client)
                 return BadValue;
         }
 
+        /* Only one client per window may select for touch begin events;
+         * touch motion and end events may not be selected for. */
+        if (evmask->mask_len >= 1)
+        {
+            unsigned char *bits = (unsigned char*)&evmask[1];
+            if (BitIsOn(bits, XI_TouchBegin))
+            {
+                OtherInputMasks *inputMasks = wOtherInputMasks(win);
+                if (inputMasks &&
+                    (BitIsOn(inputMasks->xi2mask[evmask->deviceid],
+                             XI_TouchBegin) ||
+                     BitIsOn(inputMasks->xi2mask[XIAllDevices],
+                             XI_TouchBegin) ||
+                     BitIsOn(inputMasks->xi2mask[XIAllMasterDevices],
+                             XI_TouchBegin)))
+                    return BadValue;
+            }
+            if (BitIsOn(bits, XI_TouchMotion) || BitIsOn(bits, XI_TouchEnd))
+                return BadValue;
+        }
+
         if (XICheckInvalidMaskBits((unsigned char*)&evmask[1],
                                    evmask->mask_len * 4) != Success)
             return BadValue;
diff --git a/configure.ac b/configure.ac
index 95f7a76..188d129 100644
--- a/configure.ac
+++ b/configure.ac
@@ -794,7 +794,7 @@ WINDOWSWMPROTO="windowswmproto"
 APPLEWMPROTO="applewmproto >= 1.4"
 
 dnl Core modules for most extensions, et al.
-SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.2.99.3] [renderproto >= 0.11] [xextproto >= 7.0.99.3] [inputproto >= 1.9.99.902] [kbproto >= 1.0.3] fontsproto"
+SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.2.99.3] [renderproto >= 0.11] [xextproto >= 7.0.99.3] [inputproto >= 2.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 e506f6a..1eabc6c 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -752,6 +752,12 @@ FreeDeviceClass(int type, pointer *class)
                 free((*v));
                 break;
             }
+        case XITouchClass:
+            {
+                TouchClassPtr *t = (TouchClassPtr*)class;
+                free((*t));
+                break;
+            }
         case FocusClass:
             {
                 FocusClassPtr *f = (FocusClassPtr*)class;
@@ -860,6 +866,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);
@@ -1550,6 +1557,55 @@ 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.
+ * @min_x The minimum value for the x axis.
+ * @max_x The maximum value for the x axis.
+ * @min_y The minimum value for the y axis.
+ * @max_y The maximum value for the y axis.
+ * @min_touch_width The minimum value for touch/tool width.
+ * @max_touch_width The maximum value for touch/tool width.
+ */
+Bool
+InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches,
+                           unsigned int xy_mode, unsigned int focus_mode,
+                           double min_x, double max_x,
+                           double min_y, double max_y,
+                           double min_touch_width, double max_touch_width)
+{
+    if (device->touch)
+        return FALSE;
+
+    if (xy_mode != Absolute && xy_mode != Relative)
+        return FALSE;
+    if (focus_mode != Absolute && focus_mode != Relative)
+        return FALSE;
+    if (max_touches == 0)
+        return FALSE;
+    if (max_x < min_x || max_y < min_y || max_touch_width < min_touch_width)
+        return FALSE;
+
+    device->touch = calloc(1, sizeof(*device->touch) +
+                               max_touches * sizeof(*device->touch->touches));
+    if (!device->touch)
+        return FALSE;
+    device->touch->touches = (TouchInfoPtr) &device->touch[1];
+    device->touch->max_touches = max_touches;
+    device->touch->xy_mode = xy_mode;
+    device->touch->focus_mode = focus_mode;
+    /* FIXME: don't throw away float */
+    device->touch->min_x.integral = min_x;
+    device->touch->max_x.integral = max_x;
+    device->touch->min_y.integral = min_y;
+    device->touch->max_y.integral = max_y;
+    device->touch->min_touch_width.integral = min_touch_width;
+    device->touch->max_touch_width.integral = max_touch_width;
+
+    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 0f747c1..5461ad7 100644
--- a/dix/eventconvert.c
+++ b/dix/eventconvert.c
@@ -55,6 +55,8 @@ 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 eventToTouchStateEvent(TouchStateEvent *ev, xEvent **xi);
+static int eventToTouchMotionEvent(TouchMotionEvent *ev, xEvent **xi);
 
 /* Do not use, read comments below */
 BOOL EventIsKeyRepeat(xEvent *event);
@@ -139,6 +141,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 +189,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;
@@ -238,6 +246,11 @@ EventToXI2(InternalEvent *ev, xEvent **xi)
         case ET_RawButtonRelease:
         case ET_RawMotion:
             return eventToRawEvent(&ev->raw_event, xi);
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+            return eventToTouchStateEvent(&ev->touch_state_event, xi);
+        case ET_TouchMotion:
+            return eventToTouchMotionEvent(&ev->touch_motion_event, xi);
         default:
             break;
     }
@@ -650,6 +663,57 @@ eventToRawEvent(RawDeviceEvent *ev, xEvent **xi)
     return Success;
 }
 
+static int
+eventToTouchStateEvent(TouchStateEvent *ev, xEvent **xi)
+{
+    xXITouchStateEvent* state;
+    int len = sizeof(xXITouchStateEvent);
+
+    *xi = calloc(1, len);
+    state = (xXITouchStateEvent*)*xi;
+    state->type         = GenericEvent;
+    state->extension    = IReqCode;
+    state->length       = bytes_to_int32(len - sizeof(xEvent));
+    state->evtype       = GetXI2Type((InternalEvent*)ev);
+    state->deviceid     = ev->deviceid;
+    state->time         = ev->time;
+    state->sourceid     = ev->sourceid;
+    state->touchid      = ev->touchid;
+    state->tool         = ev->tool;
+
+    return Success;
+}
+
+static int
+eventToTouchMotionEvent(TouchMotionEvent *ev, xEvent **xi)
+{
+    xXITouchMotionEvent* motion;
+    int len = sizeof(xXITouchMotionEvent);
+
+    *xi = calloc(1, len);
+    motion = (xXITouchMotionEvent*)*xi;
+    motion->type              = GenericEvent;
+    motion->extension         = IReqCode;
+    motion->length            = bytes_to_int32(len - sizeof(xEvent));
+    motion->evtype            = GetXI2Type((InternalEvent*)ev);
+    motion->deviceid          = ev->deviceid;
+    motion->time              = ev->time;
+    motion->sourceid          = ev->sourceid;
+    motion->touchid           = ev->touchid;
+    motion->mask              = ev->mask;
+    motion->root_x            = ev->root_x;
+    motion->root_y            = ev->root_y;
+    motion->x                 = ev->x;
+    motion->y                 = ev->y;
+    motion->touch_width_major = ev->touch_width_minor;
+    motion->touch_width_minor = ev->touch_width_minor;
+    motion->tool_width_major  = ev->tool_width_major;
+    motion->tool_width_minor  = ev->tool_width_minor;
+    motion->orientation       = ev->orientation;
+
+    return Success;
+}
+
 /**
  * Return the corresponding core type for the given event or 0 if no core
  * equivalent exists.
@@ -721,6 +785,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/events.c b/dix/events.c
index a2c9d1b..d0323d9 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -195,8 +195,6 @@ typedef const char *string;
 #define XE_KBPTR (xE->u.keyButtonPointer)
 
 
-#define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
-
 CallbackListPtr EventCallback;
 CallbackListPtr DeviceEventCallback;
 
@@ -334,12 +332,6 @@ IsMaster(DeviceIntPtr dev)
     return dev->type == MASTER_POINTER || dev->type == MASTER_KEYBOARD;
 }
 
-static WindowPtr XYToWindow(
-    SpritePtr pSprite,
-    int x,
-    int y
-);
-
 /**
  * Max event opcode.
  */
@@ -447,7 +439,7 @@ GetWindowXI2Mask(DeviceIntPtr dev, WindowPtr win, xEvent* ev)
             (inputMasks->xi2mask[XIAllMasterDevices][evtype/8] && IsMaster(dev)));
 }
 
-static Mask
+Mask
 GetEventMask(DeviceIntPtr dev, xEvent *event, InputClients* other)
 {
     /* XI2 filters are only ever 8 bit, so let's return a 8 bit mask */
@@ -2262,6 +2254,32 @@ FixUpEventFromWindow(
             event->evtype == XI_PropertyEvent)
             return;
 
+        if (event->evtype == XI_TouchBegin || event->evtype == XI_TouchEnd)
+        {
+            xXITouchStateEvent* state = (xXITouchStateEvent*)xE;
+            state->root = pSprite->spriteTrace[0]->drawable.id;
+            state->event = pWin->drawable.id;
+            state->child = child;
+            return;
+        } else if (event->evtype == XI_TouchMotion)
+        {
+            xXITouchMotionEvent* motion = (xXITouchMotionEvent*)xE;
+            motion->root = pSprite->spriteTrace[0]->drawable.id;
+            motion->event = pWin->drawable.id;
+            if (pSprite->hot.pScreen == pWin->drawable.pScreen)
+            {
+                motion->event_x = motion->root_x - FP1616(pWin->drawable.x, 0);
+                motion->event_y = motion->root_y - FP1616(pWin->drawable.y, 0);
+                motion->child = child;
+            } else
+            {
+                motion->event_x = 0;
+                motion->event_y = 0;
+                motion->child = None;
+            }
+            return;
+        }
+
         event->root = pSprite->spriteTrace[0]->drawable.id;
         event->event = pWin->drawable.id;
         if (pSprite->hot.pScreen == pWin->drawable.pScreen)
@@ -2582,7 +2600,7 @@ PointInBorderSize(WindowPtr pWin, int x, int y)
  *
  * @returns the window at the given coordinates.
  */
-static WindowPtr
+WindowPtr
 XYToWindow(SpritePtr pSprite, int x, int y)
 {
     WindowPtr  pWin;
@@ -3451,9 +3469,9 @@ CheckPassiveGrabsOnWindow(
         tempGrab.detail.exact = event->detail.key;
         if (!match)
         {
-            tempGrab.type = GetXIType((InternalEvent*)event);
             tempGrab.grabtype = GRABTYPE_XI;
-            if (GrabMatchesSecond(&tempGrab, grab, FALSE))
+            if ((tempGrab.type = GetXIType((InternalEvent*)event)) &&
+                (GrabMatchesSecond(&tempGrab, grab, FALSE)))
                 match = XI_MATCH;
         }
 
diff --git a/dix/getevents.c b/dix/getevents.c
index e5134d3..7e3a241 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -46,6 +46,7 @@
 #include "mipointer.h"
 #include "eventstr.h"
 #include "eventconvert.h"
+#include "windowstr.h"
 
 #include <X11/extensions/XKBproto.h>
 #include "xkbsrv.h"
@@ -1232,6 +1233,159 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type,
 }
 
 /**
+ * Get events for a touch; always generates a TouchMotion event, and may
+ * generate a TouchBegin if this is the first touch of the sequence.
+ *
+ * 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, uint32_t touchid,
+               uint32_t tool, uint16_t mask, double x, double y,
+               double touch_width_major, double touch_width_minor,
+               double tool_width_major, double tool_width_minor,
+               unsigned int orientation)
+{
+    int num_events = 0;
+    uint32_t time = GetTimeInMillis();
+    int16_t rx, ry;
+    float rx_frac = 0.0, ry_frac = 0.0;
+    TouchMotionEvent *motion;
+    TouchStateEvent *state;
+    TouchInfoPtr touch = FindTouchPoint(pDev, touchid);
+    ScreenPtr screen = pDev->spriteInfo->sprite->hotPhys.pScreen;
+    AxisInfo axis;
+
+    /* refuse events from disabled devices */
+    if (!pDev->enabled)
+        return 0;
+
+    /* Sanity checks. */
+    if (!pDev->touch)
+        return 0;
+    if ((mask & XITouchOrientationMask) && orientation >= 360)
+        return 0;
+
+    /* 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 (pDev->touch->focus_mode == Absolute) {
+        if (mask & XITouchXMask) {
+            axis.min_value = pDev->touch->min_x.integral;
+            axis.max_value = pDev->touch->max_x.integral;
+            rx = rescaleValuatorAxis(x, 0.0, &rx_frac, &axis, NULL,
+                                     screen->width);
+        }
+        if (mask & XITouchYMask) {
+            axis.min_value = pDev->touch->min_y.integral;
+            axis.max_value = pDev->touch->max_y.integral;
+            ry = rescaleValuatorAxis(y, 0.0, &ry_frac, &axis, NULL,
+                                     screen->height);
+        }
+    }
+    else {
+        if (mask & XITouchXMask)
+            rx = pDev->spriteInfo->sprite->hotPhys.x;
+        if (mask & XITouchYMask)
+            ry = pDev->spriteInfo->sprite->hotPhys.y;
+    }
+
+    if (!touch) {
+        /* We need at least x and y co-ordinates to start a touch, to know
+         * where to deliver the TouchBegin. */
+        if (!(mask & XITouchXMask) || !(mask & XITouchYMask))
+            return 0;
+
+        touch = CreateTouchPoint(pDev, touchid, tool);
+        if (!touch)
+            return 0;
+
+        state = (TouchStateEvent *) events->event;
+        events++;
+        num_events++;
+        memset(state, 0, sizeof(TouchStateEvent));
+        state->header = ET_Internal;
+        state->type = ET_TouchBegin;
+        state->length = sizeof(TouchStateEvent);
+        state->time = time;
+        state->deviceid = pDev->id;
+        state->sourceid = pDev->id;
+        state->touchid = touchid;
+        state->tool = tool;
+        state->root = screen->root->drawable.id;
+        state->root_x = rx;
+        state->root_y = ry;
+    }
+
+    motion = (TouchMotionEvent *) events->event;
+    num_events++;
+    memset(motion, 0, sizeof(TouchMotionEvent));
+    motion->header = ET_Internal;
+    motion->type = ET_TouchMotion;
+    motion->length = sizeof(TouchMotionEvent);
+    motion->time = time;
+    motion->deviceid = pDev->id;
+    motion->sourceid = pDev->id;
+    motion->root = screen->root->drawable.id;
+    motion->touchid = touchid;
+    motion->mask = mask;
+
+    /* FIXME: don't throw away float */
+    if (mask & XITouchXMask) {
+        motion->x.integral = x;
+        motion->root_x = FP1616(rx, rx_frac);
+    }
+    if (mask & XITouchYMask) {
+        motion->y.integral = y;
+        motion->root_y = FP1616(ry, ry_frac);
+    }
+    if (mask & XITouchTouchSizeMask) {
+        motion->touch_width_major.integral = touch_width_major;
+        motion->touch_width_minor.integral = touch_width_minor;
+    }
+    if (mask & XITouchToolSizeMask) {
+        motion->tool_width_major.integral = tool_width_major;
+        motion->tool_width_minor.integral = tool_width_minor;
+    }
+    if (mask & XITouchOrientationMask) {
+        motion->orientation = orientation;
+    }
+
+    return num_events;
+}
+
+int
+GetTouchFinishEvents(EventList *events, DeviceIntPtr pDev, uint32_t touchid)
+{
+    TouchStateEvent *event;
+    TouchInfoPtr touch = FindTouchPoint(pDev, touchid);
+
+    /* refuse events from disabled devices */
+    if (!pDev->enabled)
+        return 0;
+
+    if (!touch) {
+        DebugF("[Xi] %s: Got touch finish event for dead touchpoint %d\n",
+               pDev->name, touchid);
+        return 0;
+    }
+
+    event = (TouchStateEvent *) events->event;
+    memset(event, 0, sizeof(TouchStateEvent));
+    event->header = ET_Internal;
+    event->type = ET_TouchEnd;
+    event->length = sizeof(TouchStateEvent);
+    event->time = GetTimeInMillis();
+    event->deviceid = pDev->id;
+    event->sourceid = pDev->id;
+    event->touchid = touchid;
+    event->tool = touch->tool;
+
+    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/inpututils.c b/dix/inpututils.c
index 6693c67..6b0529f 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -418,3 +418,67 @@ FreeInputAttributes(InputAttributes *attrs)
     free(attrs);
 }
 
+TouchInfoPtr
+FindTouchPoint(DeviceIntPtr dev, uint32_t touchid)
+{
+    int i;
+
+    if (!dev->touch)
+        return NULL;
+
+    for (i = 0; i < dev->touch->max_touches; i++)
+        if (dev->touch->touches[i].touchid == touchid)
+            return &dev->touch->touches[i];
+
+    return NULL;
+}
+
+TouchInfoPtr
+CreateTouchPoint(DeviceIntPtr dev, uint32_t touchid, uint32_t tool)
+{
+    int i;
+
+    if (!dev->touch || dev->touch->num_touches == dev->touch->max_touches)
+        return NULL;
+
+    if (FindTouchPoint(dev, touchid))
+        return NULL;
+
+    for (i = 0; i < dev->touch->max_touches; i++)
+    {
+        if (!dev->touch->touches[i].touchid)
+        {
+            dev->touch->num_touches++;
+            memset(&dev->touch->touches[i], 0, sizeof(TouchInfoRec));
+            dev->touch->touches[i].touchid = touchid;
+            dev->touch->touches[i].tool = tool;
+            return &dev->touch->touches[i];
+        }
+    }
+
+    return NULL;
+}
+
+void
+FinishTouchPoint(DeviceIntPtr dev, uint32_t touchid)
+{
+    TouchInfoPtr touch = FindTouchPoint(dev, touchid);
+
+    if (!touch)
+        return;
+
+    touch->touchid = 0;
+    touch->client_id = 0;
+    touch->frozen = 0;
+    touch->num_frozen_events = 0;
+    touch->win = None;
+    touch->tool = 0;
+    touch->last_x = dev->touch->min_x;
+    touch->last_y = dev->touch->min_y;
+    touch->last_touch_major = dev->touch->min_touch_width;
+    touch->last_touch_minor = dev->touch->min_touch_width;
+    touch->last_tool_major = dev->touch->min_touch_width;
+    touch->last_tool_minor = dev->touch->min_touch_width;
+    touch->last_orientation = 0;
+    dev->touch->num_touches--;
+}
diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
index 877eb03..df8ac49 100644
--- a/hw/xfree86/common/xf86Xinput.c
+++ b/hw/xfree86/common/xf86Xinput.c
@@ -1360,4 +1360,30 @@ xf86EnableDevice(DeviceIntPtr dev)
     EnableDevice(dev, TRUE);
 }
 
+void
+xf86PostTouchMotion(DeviceIntPtr dev, uint32_t touchid, uint32_t tool,
+                    uint16_t mask, double x, double y,
+                    double touch_major, double touch_minor,
+                    double tool_major, double tool_minor,
+                    unsigned int orientation)
+{
+    int i, nevents;
+
+    nevents = GetTouchEvents(xf86Events, dev, touchid, tool, mask, x, y,
+                             touch_major, touch_minor, tool_major, tool_minor,
+                             orientation);
+    for (i = 0; i < nevents; i++)
+        mieqEnqueue(dev, (InternalEvent *)((xf86Events + i)->event));
+}
+
+void
+xf86FiniTouchPoint(DeviceIntPtr dev, uint32_t touchid)
+{
+    int i, nevents;
+
+    nevents = GetTouchFinishEvents(xf86Events, dev, touchid);
+    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 20a3f1b..70f822c 100644
--- a/hw/xfree86/common/xf86Xinput.h
+++ b/hw/xfree86/common/xf86Xinput.h
@@ -199,6 +199,20 @@ extern _X_EXPORT void xf86EnableDevice(DeviceIntPtr dev);
 /* not exported */
 int xf86NewInputDevice(IDevPtr idev, DeviceIntPtr *pdev, BOOL is_auto);
 
+/* Multitouch API. */
+extern _X_EXPORT void xf86PostTouchMotion(DeviceIntPtr dev,
+                                          uint32_t touchid,
+                                          uint32_t tool,
+                                          uint16_t mask,
+                                          double x,
+                                          double y,
+                                          double touch_major,
+                                          double touch_minor,
+                                          double width_major,
+                                          double width_minor,
+                                          unsigned int orientation);
+extern _X_EXPORT void xf86FiniTouchPoint(DeviceIntPtr dev, uint32_t touchid);
+
 /* xf86Helper.c */
 extern _X_EXPORT void xf86AddInputDriver(InputDriverPtr driver, pointer module, int flags);
 extern _X_EXPORT void xf86DeleteInputDriver(int drvIndex);
diff --git a/include/dix.h b/include/dix.h
index a282a08..4f9bbc4 100644
--- a/include/dix.h
+++ b/include/dix.h
@@ -369,6 +369,13 @@ extern void AllowSome(
     DeviceIntPtr /* thisDev */,
     int /* newState */);
 
+extern void AllowTouches(
+    ClientPtr	/* client */,
+    TimeStamp /* time */,
+    DeviceIntPtr /* thisDev */,
+    int /* mode */,
+    uint32_t /* touchid */);
+
 extern void ReleaseActiveGrabs(
     ClientPtr client);
 
diff --git a/include/events.h b/include/events.h
index 375173a..9ceceda 100644
--- a/include/events.h
+++ b/include/events.h
@@ -26,6 +26,8 @@
 #define EVENTS_H
 typedef struct _DeviceEvent DeviceEvent;
 typedef struct _DeviceChangedEvent DeviceChangedEvent;
+typedef struct _TouchStateEvent TouchStateEvent;
+typedef struct _TouchMotionEvent TouchMotionEvent;
 #if XFreeXDGA
 typedef struct _DGAEvent DGAEvent;
 #endif
diff --git a/include/eventstr.h b/include/eventstr.h
index 433227e..da11d6a 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 */
 };
 
@@ -209,6 +212,43 @@ struct _RawDeviceEvent
     } valuators;
 };
 
+struct _TouchStateEvent
+{
+    unsigned char header; /**< Always ET_Internal */
+    enum EventType type;  /**< ET_TouchBegin or ET_TouchEnd */
+    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;     /**< Unique identifier for this touch */
+    uint32_t tool;        /**< Identifier of the physical tool */
+    Window root;          /**< Root window for events */
+    FP1616 root_x;        /**< x in screen co-ordinates */
+    FP1616 root_y;        /**< y in screen co-ordinates */
+};
+
+struct _TouchMotionEvent
+{
+    unsigned char header; /**< Always ET_Internal */
+    enum EventType type;  /**< ET_TouchMotion */
+    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;     /**< Unique identifier for this touch */
+    uint16_t mask;        /**< Components present in this event */
+    Window root;          /**< Root window event is relative to */
+    FP1616 root_x;        /**< x in screen co-ordinates */
+    FP1616 root_y;        /**< y in screen co-ordinates */
+    FP3232 x;
+    FP3232 y;
+    FP3232 touch_width_major;
+    FP3232 touch_width_minor;
+    FP3232 tool_width_major;
+    FP3232 tool_width_minor;
+    uint16_t orientation;
+};
+
 #ifdef XQUARTZ
 #define XQUARTZ_EVENT_MAXARGS 5
 struct _XQuartzEvent {
@@ -234,6 +274,8 @@ union _InternalEvent {
         } any;
         DeviceEvent device_event;
         DeviceChangedEvent changed_event;
+        TouchStateEvent touch_state_event;
+        TouchMotionEvent touch_motion_event;
 #if XFreeXDGA
         DGAEvent dga_event;
 #endif
diff --git a/include/exevents.h b/include/exevents.h
index 39e1c70..48212ad 100644
--- a/include/exevents.h
+++ b/include/exevents.h
@@ -202,6 +202,15 @@ GrabWindow(
 	GrabMask*              /* eventMask */);
 
 extern int
+GrabTouch(
+	ClientPtr              /* client */,
+	DeviceIntPtr           /* dev */,
+	DeviceIntPtr           /* mod_dev */,
+	int                    /* tool */,
+	GrabParameters*        /* param */,
+	GrabMask*              /* eventMask */);
+
+extern int
 SelectForWindow(
 	DeviceIntPtr           /* dev */,
 	WindowPtr              /* pWin */,
diff --git a/include/input.h b/include/input.h
index 0ae6094..5fd51a0 100644
--- a/include/input.h
+++ b/include/input.h
@@ -105,6 +105,9 @@ typedef struct _DeviceIntRec *DeviceIntPtr;
 typedef struct _ClassesRec *ClassesPtr;
 typedef struct _SpriteRec *SpritePtr;
 typedef union _GrabMask GrabMask;
+typedef struct _TouchInfoRec *TouchInfoPtr;
+
+#define rClient(obj) (clients[CLIENT_ID((obj)->resource)])
 
 typedef struct _EventList {
     xEvent* event;
@@ -318,6 +321,18 @@ 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 /*xy_mode*/,
+    unsigned int /*focus_mode*/,
+    double /*min_x*/,
+    double /*max_x*/,
+    double /*min_y*/,
+    double /*max_y*/,
+    double /*min_touch_width*/,
+    double /*max_touch_width*/);
+
 typedef void (*BellProcPtr)(
     int /*percent*/,
     DeviceIntPtr /*device*/,
@@ -471,6 +486,25 @@ extern int GetKeyboardValuatorEvents(
     int num_valuator,
     int *valuators);
 
+extern int GetTouchEvents(
+    EventListPtr events,
+    DeviceIntPtr pDev,
+    uint32_t touchid,
+    uint32_t tool,
+    uint16_t mask,
+    double x,
+    double y,
+    double touch_width_major,
+    double touch_width_minor,
+    double tool_width_major,
+    double tool_width_minor,
+    unsigned int orientation);
+
+extern int GetTouchFinishEvents(
+    EventListPtr events,
+    DeviceIntPtr pDev,
+    uint32_t touchid);
+
 extern int GetProximityEvents(
     EventListPtr events,
     DeviceIntPtr pDev,
@@ -534,8 +568,13 @@ 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 TouchInfoPtr CreateTouchPoint(DeviceIntPtr dev, uint32_t touchid,
+                                     uint32_t tool);
+extern TouchInfoPtr FindTouchPoint(DeviceIntPtr dev, uint32_t touchid);
+extern void FinishTouchPoint(DeviceIntPtr dev, uint32_t touchid);
 
 /* misc event helpers */
+extern Mask GetEventMask(DeviceIntPtr dev, xEvent* ev, InputClientsPtr clients);
 extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event);
 extern Mask GetWindowXI2Mask(DeviceIntPtr dev, WindowPtr win, xEvent* ev);
 void FixUpEventFromWindow(SpritePtr pSprite,
@@ -543,6 +582,7 @@ void FixUpEventFromWindow(SpritePtr pSprite,
                           WindowPtr pWin,
                           Window child,
                           Bool calcChild);
+extern WindowPtr XYToWindow(SpritePtr pSprite, int x, int y);
 
 /* Implemented by the DDX. */
 extern _X_EXPORT int NewInputDeviceRequest(
diff --git a/include/inputstr.h b/include/inputstr.h
index e1cf06a..544f3cd 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"
@@ -70,7 +72,7 @@ SOFTWARE.
  * 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 */
 
 /**
@@ -242,6 +244,39 @@ typedef struct _ValuatorClassRec {
     ValuatorAccelerationRec	accelScheme;
 } ValuatorClassRec, *ValuatorClassPtr;
 
+typedef struct _TouchInfoRec {
+    uint32_t              touchid;      /* 0 if currently unused */
+    int                   sourceid;
+    int                   client_id;
+    Window                win;
+    int                   frozen;
+    int                   size_frozen_events;
+    int                   num_frozen_events;
+    InternalEvent         *frozen_events;
+    uint32_t              tool;
+    FP3232                last_x;
+    FP3232                last_y;
+    FP3232                last_touch_major;
+    FP3232                last_touch_minor;
+    FP3232                last_tool_major;
+    FP3232                last_tool_minor;
+    uint16_t              last_orientation;
+} TouchInfoRec;
+
+typedef struct _TouchClassRec {
+    FP3232                min_x;
+    FP3232                max_x;
+    FP3232                min_y;
+    FP3232                max_y;
+    FP3232                min_touch_width;
+    FP3232                max_touch_width;
+    unsigned int          xy_mode;
+    unsigned int          focus_mode;
+    unsigned int          max_touches;
+    unsigned int          num_touches;
+    TouchInfoPtr          touches;
+} TouchClassRec, *TouchClassPtr;
+
 typedef struct _ButtonClassRec {
     int			sourceid;
     CARD8		numButtons;
@@ -346,6 +381,7 @@ typedef struct _LedFeedbackClassRec {
 typedef struct _ClassesRec {
     KeyClassPtr		key;
     ValuatorClassPtr	valuator;
+    TouchClassPtr       touch;
     ButtonClassPtr	button;
     FocusClassPtr	focus;
     ProximityClassPtr	proximity;
@@ -511,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 c674465..f21d632 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -127,7 +127,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 fa60b40..df4014f 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -282,6 +282,13 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
         case ET_RawMotion:
             event->raw_event.deviceid = dev->id;
             break;
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+            event->touch_state_event.deviceid = dev->id;
+            break;
+        case ET_TouchMotion:
+            event->touch_motion_event.deviceid = dev->id;
+            break;
         default:
             ErrorF("[mi] Unknown event type (%d), cannot change id.\n",
                    event->any.type);
@@ -388,6 +395,9 @@ mieqProcessDeviceEvent(DeviceIntPtr dev,
                 NewCurrentScreen (dev, DequeueScreen(dev), x, y);
             }
             break;
+        case ET_TouchMotion:
+            /* XXX FIXME: Add screen-crossing support here */
+            break;
         default:
             break;
     }
-- 
1.7.1



More information about the xorg-devel mailing list