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

Peter Hutterer peter.hutterer at who-t.net
Tue Dec 21 21:10:07 PST 2010


On Fri, Dec 17, 2010 at 05:13:33PM +0000, Daniel Stone wrote:
> 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.
> 
> Based on work from both myself and Chase Douglas.
> 
> Signed-off-by: Daniel Stone <daniel at fooishbar.org>
> Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
> ---
>  Xi/exevents.c                  |  346 ++++++++++++++++++++++++++++++++++++++++
>  Xi/extinit.c                   |    9 +-
>  Xi/xiallowev.c                 |  110 +++++++++++++
>  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             |   13 ++
>  dix/events.c                   |    7 +-
>  dix/getevents.c                |  113 +++++++++++++
>  dix/grabs.c                    |   55 +++++++
>  dix/inpututils.c               |   58 +++++++
>  dix/window.c                   |    9 +-
>  hw/xfree86/common/xf86Xinput.c |   21 +++
>  hw/xfree86/common/xf86Xinput.h |    4 +
>  include/eventstr.h             |    5 +
>  include/exevents.h             |   20 +++
>  include/input.h                |   17 ++
>  include/inputstr.h             |  118 +++++++++-----
>  include/protocol-versions.h    |    2 +-
>  mi/mieq.c                      |    3 +
>  24 files changed, 1111 insertions(+), 53 deletions(-)
> 
> diff --git a/Xi/exevents.c b/Xi/exevents.c
> index 327873e..c401634 100644
> --- a/Xi/exevents.c
> +++ b/Xi/exevents.c
> @@ -77,6 +77,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 +927,263 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
>  }
>  
>  /**
> + * 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 selection
> + * delivery logic.
> + */
> +static void
> +ProcessTouchEvent(DeviceEvent *ev, DeviceIntPtr sourcedev)
> +{
> +    TouchClassPtr t;
> +    TouchPointInfoPtr ti;
> +    DeviceIntPtr masterdev = NULL, deliverdev = NULL;
> +    Window child;
> +    WindowPtr win;
> +    SpritePtr sprite;
> +    xXIDeviceEvent *xi2;
> +    Mask filter;
> +    int err, touch, i, j, deliveries;
> +
> +    /* We handle deliveries to MDs through the SD, rather than copying
> +     * the event and processing it twice. */

no. if the current hierarchy event processing isn't good enough, fix it but
don't shortcut it like this.

> +    if (ev->sourceid != ev->deviceid)
> +        return;
> +
> +    if (sourcedev->u.master)
> +        masterdev = sourcedev->u.master;

needs an IsMaster() check. u.master is also used as u.lastSlave for master
devices. the sourceid != deviceid check will eliminate master devices, but
the IsMaster() check is still the explicit preferred check.

> +
> +    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];
> +
> +    /* Find our window trace, or construct one if necessary.  In direct touch
> +     * mode, we focus immediately under the touchpoint, so we need to build a
> +     * window trace; in Relative, we just use the device's sprite, or reuse
> +     * an existing touch's sprite if possible. */
> +    sprite = &ti->sprite;
> +    if (ev->type == ET_TouchBegin)
> +    {
> +        if (t->mode == XIDirectTouch)
> +        {
> +            /* 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;
> +

please add a comment what this loop and the other conditions do.

> +            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;
> +
> +            if (srcsprite->spriteTraceGood > sprite->spriteTraceSize)
> +            {
> +                trace = realloc(sprite->spriteTrace,
> +                                srcsprite->spriteTraceSize * sizeof(*trace));
> +                if (!trace)
> +                {
> +                    sprite->spriteTraceGood = 0;
> +                    return;
> +                }
> +                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;
> +
> +        /* Mark which grabs/selections we're delivering to: max one grab per
> +         * window plus the bottom-most selection. */

s/selection/event mask/ or event selection maybe? selection has multiple
meanings in X.

> +        ti->listeners = calloc(sprite->spriteTraceGood + 1,
> +                               sizeof(*ti->listeners));
> +        if (!ti->listeners)
> +        {
> +            sprite->spriteTraceGood = 0;
> +            return;
> +        }
> +        ti->num_listeners = 0;
> +    }
> +    else
> +    {
> +        if (sprite->spriteTraceGood <= 0)
> +            return;
> +    }
> +

this condition needs to go into a separate function. by the time I had
digested this block, I had lost track of what the function was supposed to
do...

> +    /* 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;
> +        ti->pending_finish = TRUE;
> +        ev->flags |= XITouchPendingFinish;
> +    }
> +
> +    /* 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;
> +
> +    err = EventToXI2((InternalEvent *) ev, (xEvent **) &xi2);
> +    if (err != Success)
> +    {
> +        ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEvent (%d)\n",
> +               sourcedev->name, err);
> +        return;
> +    }
> +    filter = GetEventFilter(sourcedev, (xEvent *) xi2);
> +    child = sprite->spriteTrace[sprite->spriteTraceGood - 1]->drawable.id;
> +
> +    /* First search the stack going from root to child looking for grabs,
> +     * delivering to every grab we find ... */
> +    ti->num_grabs = 0;
> +    for (i = 0; i < sprite->spriteTraceGood; i++)
> +    {
> +        GrabPtr grab;
> +        enum EventType saved_evtype = ev->type;
> +
> +        win = sprite->spriteTrace[i];
> +
> +        /* Grabs can only be established on one type, so fool
> +         * CheckPassiveGrabsOnWindow into thinking that it's a TouchBegin. */

no. fix CheckPassiveGrabsOnWindow that it works with other touch events too.

> +        ev->type = ET_TouchBegin;
> +        deliverdev = sourcedev;
> +        grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
> +        if (!grab && masterdev)
> +        {
> +            deliverdev = masterdev;
> +            grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
> +        }
> +        ev->type = saved_evtype;
> +        if (!grab)
> +            continue;
> +        xi2->deviceid = deliverdev->id;

I really don't like calling EventToXI2() above and then messing with the
fields manually down here. the main reason we have internal and protocol
events is that we can just pass the internal event to the conversion func
and not worry about the details.

> +
> +        /* 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;
> +        }

I don't quite understand the need for this. Once the sprite trace is
established, anything in listeners should be ok to deliver to, right?
if so, we don't need this for loop and can just run it on TouchBegin.

> +
> +        if (XaceHook(XACE_RECEIVE_ACCESS, rClient(grab), win,
> +                     (xEvent *) xi2, 1) != Success)
> +            continue;
> +        FixUpEventFromWindow(sprite, (xEvent *) xi2, win, child, FALSE);
> +        deliveries = TryClientEvents(rClient(grab), deliverdev,
> +                                     (xEvent *) xi2, 1, filter, filter,
> +                                     NullGrab);
> +        if (deliveries > 0)
> +        {
> +            ti->num_grabs++;
> +            xi2->flags &= ~XITouchOwner;
> +            if (ev->type == ET_TouchBegin)
> +                ti->listeners[ti->num_listeners++] = grab->resource;
> +        }
> +    }

here would be an awesome spot for deciding that we need to move into another
function...

> +
> +    /* ... then go backwards looking for selections, but stop at the first
> +     * one we find. */
> +    deliveries = 0;
> +    while (--i >= 0)
> +    {
> +        int evbyte = xi2->evtype / 8;
> +        OtherInputMasks *inputMasks;
> +        InputClients *iclients;
> +
> +        win = sprite->spriteTrace[i];
> +        inputMasks = wOtherInputMasks(win);
> +        if (!inputMasks)
> +            continue;
> +        iclients = inputMasks->inputClients;
> +        if (!iclients)
> +            continue;
> +
> +        FixUpEventFromWindow(sprite, (xEvent *) xi2, win, child, FALSE);
> +        if (!(inputMasks->xi2mask[sourcedev->id][evbyte] & filter) &&
> +            !(inputMasks->xi2mask[XIAllDevices][evbyte] & filter) &&
> +            !(masterdev &&
> +              ((inputMasks->xi2mask[masterdev->id][evbyte] & filter) ||
> +               (inputMasks->xi2mask[XIAllMasterDevices][evbyte] & filter))))
> +            continue;
> +
> +        for (; iclients; iclients = iclients->next)
> +        {
> +            if ((iclients->xi2mask[sourcedev->id][evbyte] & filter) ||
> +                (iclients->xi2mask[XIAllDevices][evbyte] & filter))
> +                deliverdev = sourcedev;
> +            else if (masterdev &&
> +                     ((iclients->xi2mask[masterdev->id][evbyte] & filter) ||
> +                      (iclients->xi2mask[XIAllMasterDevices][evbyte] & filter)))
> +                deliverdev = masterdev;
> +            else
> +                continue;
> +            xi2->deviceid = deliverdev->id;
> +
> +            /* 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)
> +                    continue;
> +            }
> +
> +            if (XaceHook(XACE_RECEIVE_ACCESS, rClient(iclients), win,
> +                         (xEvent *) xi2, 1) != Success)
> +                continue;
> +            deliveries = TryClientEvents(rClient(iclients), deliverdev,
> +                                         (xEvent *) xi2, 1, filter, filter,
> +                                         NullGrab);
> +            if (ev->type == ET_TouchBegin && deliveries > 0)
> +                ti->listeners[ti->num_listeners++] = iclients->resource;
> +            break;
> +        }
> +
> +        if (deliveries > 0)
> +            break;
> +    }
> +
> +    if (ev->type == ET_TouchEnd)
> +        FinishTouchPoint(sourcedev, ev->detail.touch);
> +
> +    free(xi2);
> +}
> +
> +/**
>   * Main device event processing function.
>   * Called from when processing the events from the event queue.
>   *
> @@ -954,6 +1212,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 +1416,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 +1850,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 +2050,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 546ccb4..940ef98 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 */
>  };
>  
>  /*****************************************************************
> @@ -880,6 +882,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..6b9fcc6 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,111 @@ 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;
> +    ValuatorMask *mask = valuator_mask_new(0);
> +    EventList *events = InitEventList(GetMaximumEventsNum());

size of 1 seems to be sufficient? unless you call GPE from GetTouchEvent(),
but in this case you need to bump the value returned by
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;

indentation

> +    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;
> +
> +        /* XXX: Should probably take flags in GetTouchEvents. */
> +        nev = GetTouchEvents(events, dev, stuff->touchid, mask, 0);
> +        if (nev == 0)
> +            return BadAlloc;
> +        for (i = 0; i < nev; i++)
> +        {
> +            ev = (DeviceEvent *)((events + i)->event);
> +            if (ev->type == ET_TouchMotion)
> +                ev->flags |= XITouchOwnerAccepted;
> +            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);
> +        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 2966145..c5f7bf0 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((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 7aa3f0a..64ee173 100644
> --- a/Xi/xiselectev.c
> +++ b/Xi/xiselectev.c
> @@ -141,6 +141,47 @@ ProcXISelectEvents(ClientPtr client)
>                  return BadValue;
>          }
>  
> +        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 client goes from floating to attached. */

s/client/device?

> +            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((unsigned char*)&evmask[1],
>                                     evmask->mask_len * 4) != Success)
>              return BadValue;
> diff --git a/configure.ac b/configure.ac
> index a5967ad..532412d 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 7b894f0..75a00ac 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:
> @@ -575,6 +584,7 @@ eventToDeviceEvent(DeviceEvent *ev, xEvent **xi)
>      xde->sourceid       = ev->sourceid;
>      xde->root_x         = FP1616(ev->root_x, ev->root_x_frac);
>      xde->root_y         = FP1616(ev->root_y, ev->root_y_frac);
> +    xde->flags          = ev->flags;
>  
>      if (ev->key_repeat)
>          xde->flags      |= XIKeyRepeat;
> @@ -727,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/events.c b/dix/events.c
> index 77e76ac..106e024 100644
> --- a/dix/events.c
> +++ b/dix/events.c
> @@ -435,7 +435,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 */
> @@ -1277,6 +1277,7 @@ ComputeFreezes(void)
>      if (replayDev)
>      {
>          DeviceEvent* event = replayDev->deviceGrab.sync.event;
> +        SpritePtr pSprite = replayDev->spriteInfo->sprite;
>  
>  	syncEvents.replayDev = (DeviceIntPtr)NULL;
>  
> @@ -3415,9 +3416,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 794df42..75ec34b 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,118 @@ 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)
> +{
> +    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;
> +
> +    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..49e51f0 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);
> +                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);
>  
> 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..ede9858 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);

indentation

> +    }
>      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);

indentation

> +    }
>  }
>  
>  
> diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
> index b9006ab..29166ad 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, int end)
> +{
> +    int i, nevents;
> +
> +    nevents = GetTouchEvents(xf86Events, dev, touchid, mask, end);
> +    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..35dcfdf 100644
> --- a/hw/xfree86/common/xf86Xinput.h
> +++ b/hw/xfree86/common/xf86Xinput.h
> @@ -141,6 +141,8 @@ 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, int end);
>  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 +150,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 377cceb..1766c1b 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 */
> @@ -117,6 +121,7 @@ struct _DeviceEvent
>      Window      root; /**< Root window of the event */
>      int corestate;    /**< Core key/button state BEFORE the event */
>      int key_repeat;   /**< Internally-generated key repeat event */
> +    uint32_t flags;   /**< Flags to pass into the generated event */
>  };
>  

this and the matching hunk to eventToDeviceEvent() should be a separate
patch. we could merge this now.

>  
> diff --git a/include/exevents.h b/include/exevents.h
> index bfee385..ae45054 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 c1db544..cddef91 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,13 @@ extern int GetKeyboardValuatorEvents(
>      int key_code,
>      const ValuatorMask *mask);
>  
> +extern int GetTouchEvents(
> +    EventListPtr events,
> +    DeviceIntPtr pDev,
> +    unsigned int touchid,
> +    const ValuatorMask *mask,
> +    int end);
> +
>  extern int GetProximityEvents(
>      EventListPtr events,
>      DeviceIntPtr pDev,
> @@ -524,8 +537,12 @@ 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);
>  extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event);
>  extern Mask GetWindowXI2Mask(DeviceIntPtr dev, WindowPtr win, xEvent* ev);
>  void FixUpEventFromWindow(SpritePtr pSprite,
> diff --git a/include/inputstr.h b/include/inputstr.h
> index 264d715..8931598 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 */
>  
>  /**
> @@ -203,6 +205,47 @@ typedef struct _GrabRec {
>      unsigned char       xi2mask[EMASKSIZE][XI2MASKSIZE];
>  } GrabRec;
>  
> +/**
> + * Sprite information for a device.
> + */
> +typedef struct _SpriteRec {
> +    CursorPtr	current;
> +    BoxRec	hotLimits;	/* logical constraints of hot spot */
> +    Bool	confined;	/* confined to screen */
> +    RegionPtr	hotShape;	/* additional logical shape constraint */
> +    BoxRec	physLimits;	/* physical constraints of hot spot */
> +    WindowPtr	win;		/* window of logical position */
> +    HotSpot	hot;		/* logical pointer position */
> +    HotSpot	hotPhys;	/* physical pointer position */
> +#ifdef PANORAMIX
> +    ScreenPtr	screen;		/* all others are in Screen 0 coordinates */
> +    RegionRec   Reg1;	        /* Region 1 for confining motion */
> +    RegionRec   Reg2;		/* Region 2 for confining virtual motion */
> +    WindowPtr   windows[MAXSCREENS];
> +    WindowPtr	confineWin;	/* confine window */ 
> +#endif
> +    /* The window trace information is used at dix/events.c to avoid having
> +     * to compute all the windows between the root and the current pointer
> +     * window each time a button or key goes down. The grabs on each of those
> +     * windows must be checked.
> +     * spriteTraces should only be used at dix/events.c! */
> +    WindowPtr *spriteTrace;
> +    int spriteTraceSize;
> +    int spriteTraceGood;
> +
> +    /* Due to delays between event generation and event processing, it is
> +     * possible that the pointer has crossed screen boundaries between the
> +     * time in which it begins generating events and the time when
> +     * those events are processed.
> +     *
> +     * pEnqueueScreen: screen the pointer was on when the event was generated
> +     * pDequeueScreen: screen the pointer was on when the event is processed
> +     */
> +    ScreenPtr pEnqueueScreen;
> +    ScreenPtr pDequeueScreen;
> +
> +} SpriteRec;
> +
>  typedef struct _KeyClassRec {
>      int			sourceid;
>      CARD8		down[DOWN_LENGTH];

please split this hunk into a separate commit

> @@ -243,6 +286,36 @@ typedef struct _ValuatorClassRec {
>      ValuatorAccelerationRec	accelScheme;
>  } ValuatorClassRec, *ValuatorClassPtr;
>  
> +typedef struct _TouchPointInfo {
> +    Bool        active;
> +    Bool        pending_finish;
> +    uint32_t    id;
> +    SpriteRec   sprite;
> +    int         *valuators;
> +    XID         *listeners;
> +    int         num_listeners;
> +    int         num_grabs;
> +    Bool        emulate_pointer;
> +} TouchPointInfoRec, *TouchPointInfoPtr;
> +
please add short comments to all fields. at least pending_finish, valuators
and liteners are not self-explanatory enough.

> +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;
> +    unsigned int          last_touchid;
> +    int                   x_axis;
> +    int                   y_axis;
> +} TouchClassRec, *TouchClassPtr;
> +
>  typedef struct _ButtonClassRec {
>      int			sourceid;
>      CARD8		numButtons;
> @@ -347,6 +420,7 @@ typedef struct _LedFeedbackClassRec {
>  typedef struct _ClassesRec {
>      KeyClassPtr		key;
>      ValuatorClassPtr	valuator;
> +    TouchClassPtr	touch;
>      ButtonClassPtr	button;
>      FocusClassPtr	focus;
>      ProximityClassPtr	proximity;
> @@ -360,47 +434,6 @@ typedef struct _ClassesRec {
>  } ClassesRec;
>  
>  
> -/**
> - * Sprite information for a device.
> - */
> -typedef struct _SpriteRec {
> -    CursorPtr	current;
> -    BoxRec	hotLimits;	/* logical constraints of hot spot */
> -    Bool	confined;	/* confined to screen */
> -    RegionPtr	hotShape;	/* additional logical shape constraint */
> -    BoxRec	physLimits;	/* physical constraints of hot spot */
> -    WindowPtr	win;		/* window of logical position */
> -    HotSpot	hot;		/* logical pointer position */
> -    HotSpot	hotPhys;	/* physical pointer position */
> -#ifdef PANORAMIX
> -    ScreenPtr	screen;		/* all others are in Screen 0 coordinates */
> -    RegionRec   Reg1;	        /* Region 1 for confining motion */
> -    RegionRec   Reg2;		/* Region 2 for confining virtual motion */
> -    WindowPtr   windows[MAXSCREENS];
> -    WindowPtr	confineWin;	/* confine window */ 
> -#endif
> -    /* The window trace information is used at dix/events.c to avoid having
> -     * to compute all the windows between the root and the current pointer
> -     * window each time a button or key goes down. The grabs on each of those
> -     * windows must be checked.
> -     * spriteTraces should only be used at dix/events.c! */
> -    WindowPtr *spriteTrace;
> -    int spriteTraceSize;
> -    int spriteTraceGood;
> -
> -    /* Due to delays between event generation and event processing, it is
> -     * possible that the pointer has crossed screen boundaries between the
> -     * time in which it begins generating events and the time when
> -     * those events are processed.
> -     *
> -     * pEnqueueScreen: screen the pointer was on when the event was generated
> -     * pDequeueScreen: screen the pointer was on when the event is processed
> -     */
> -    ScreenPtr pEnqueueScreen;
> -    ScreenPtr pDequeueScreen;
> -
> -} SpriteRec;
> -
>  /* Device properties */
>  typedef struct _XIPropertyValue
>  {
> @@ -512,6 +545,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

I admit that towards the end my concentration let off so I've probably
missed some things there. splitting the functions into more bite-sized ones
would be much appreciated. a 200 line ProcessTouchEvent() that does
everything from generating sprite traces to delivering events is a bit
heavy.

some test cases for xi2-protocol swapping would be appreciated (as
follow-up, this patch is long enough as it is). I'd also like to see some
test cases for all the CreateTouch() and friends so we know when we
accidentally break it in the future.

Cheers,
  Peter


More information about the xorg-devel mailing list