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

Chase Douglas chase.douglas at canonical.com
Thu Jan 6 11:30:07 PST 2011


On 12/28/2010 12:58 PM, 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.
> 
> Signed-off-by: Daniel Stone <daniel at fooishbar.org>
> Co-authored-by: Chase Douglas <chase.douglas at canonical.com>

Everything looks pretty good! I made a few comments, none of which are
major.

Two things:

1. The protocol document implies that the server generates pointer
events from touch events. I didn't see any of that here (or maybe I
misread the protocol?)

2. This is missing the previously discussed ability to request that the
server buffer touch events until the client becomes the owner of the
events. My proposal is to rename XIAllowTouchEvents to
XIModifyTouchEventDelivery, and have it handle touch allowance and
setting propagation flags like the buffering request. I would suggest
that we merely rename the call in this patch and add a patch on top to
implement the buffering.

We're almost there!

> ---
>  Xi/exevents.c                  |  439 ++++++++++++++++++++++++++++++++++++++++
>  Xi/extinit.c                   |    9 +-
>  Xi/xiallowev.c                 |  109 ++++++++++
>  Xi/xiallowev.h                 |    2 +
>  Xi/xipassivegrab.c             |   18 ++-
>  Xi/xiquerydevice.c             |   95 +++++++++
>  Xi/xiquerydevice.h             |    3 +
>  Xi/xiselectev.c                |   41 ++++
>  configure.ac                   |    2 +-
>  dix/devices.c                  |   93 +++++++++
>  dix/eventconvert.c             |   12 +
>  dix/getevents.c                |  114 +++++++++++
>  dix/grabs.c                    |   78 +++++++-
>  dix/inpututils.c               |   58 ++++++
>  dix/window.c                   |    9 +-
>  hw/xfree86/common/xf86Xinput.c |   21 ++
>  hw/xfree86/common/xf86Xinput.h |    5 +
>  include/eventstr.h             |    4 +
>  include/exevents.h             |   20 ++
>  include/input.h                |   17 ++
>  include/inputstr.h             |   38 ++++-
>  include/protocol-versions.h    |    2 +-
>  mi/mieq.c                      |    3 +
>  23 files changed, 1181 insertions(+), 11 deletions(-)
> 
> diff --git a/Xi/exevents.c b/Xi/exevents.c
> index 327873e..9b19124 100644
> --- a/Xi/exevents.c
> +++ b/Xi/exevents.c
> @@ -44,6 +44,31 @@ SOFTWARE.
>  
>  ********************************************************/
>  
> +/*
> + * Copyright © 2010 Collabora Ltd.
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
> + * DEALINGS IN THE SOFTWARE.
> + *
> + * Author: Daniel Stone <daniel at fooishbar.org>
> + */
> +
>  /********************************************************************
>   *
>   *  Routines to register and initialize extension input devices.
> @@ -77,6 +102,7 @@ SOFTWARE.
>  #include "xiquerydevice.h" /* For List*Info */
>  #include "eventconvert.h"
>  #include "eventstr.h"
> +#include "xserver-properties.h"
>  
>  #include <X11/extensions/XKBproto.h>
>  #include "xkbsrv.h"
> @@ -926,6 +952,331 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
>  }
>  
>  /**
> + * Ensure a window trace is present in ti->sprite, constructing one for
> + * TouchBegin events.
> + */
> +static Bool
> +EnsureTouchSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, DeviceEvent *ev)
> +{
> +    TouchClassPtr t = sourcedev->touch;
> +    SpritePtr sprite = &ti->sprite;
> +    int i;
> +
> +    if (ev->type != ET_TouchBegin)
> +    {
> +        if (ev->type == ET_TouchMotion && sprite->spriteTraceGood <= 0)
> +            return FALSE;
> +
> +        return TRUE;
> +    }
> +
> +    if (t->mode == XIDirectTouch)
> +    {
> +        /* Focus immediately under the touchpoint in direct touch mode.
> +         * XXX: Do we need to handle crossing screens here? */
> +        sprite->spriteTrace[0] =
> +            sourcedev->spriteInfo->sprite->hotPhys.pScreen->root;
> +        XYToWindow(sprite, ev->root_x, ev->root_y);
> +    }
> +    else
> +    {
> +        WindowPtr *trace;
> +        SpritePtr srcsprite;
> +
> +        /* Find and reuse an existing touch's sprite if possible, else use the
> +         * device's sprite. */
> +        for (i = 0; i < t->num_touches; i++)
> +            if (t->touches[i].sprite.spriteTraceGood > 0)
> +                break;
> +        if (i < t->num_touches)
> +            srcsprite = &t->touches[i].sprite;
> +        else if (sourcedev->spriteInfo->sprite)
> +            srcsprite = sourcedev->spriteInfo->sprite;
> +        else
> +            return FALSE;
> +
> +        if (srcsprite->spriteTraceGood > sprite->spriteTraceSize)
> +        {
> +            trace = realloc(sprite->spriteTrace,
> +                            srcsprite->spriteTraceSize * sizeof(*trace));
> +            if (!trace)
> +            {
> +                sprite->spriteTraceGood = 0;
> +                return FALSE;
> +            }
> +            sprite->spriteTrace = trace;
> +            sprite->spriteTraceSize = srcsprite->spriteTraceGood;
> +        }
> +        memcpy(sprite->spriteTrace, srcsprite->spriteTrace,
> +               srcsprite->spriteTraceGood * sizeof(*trace));
> +        sprite->spriteTraceGood = srcsprite->spriteTraceGood;
> +    }
> +
> +    if (sprite->spriteTraceGood <= 0)
> +        return FALSE;
> +
> +    /* Mark which grabs/event selections we're delivering to: max one grab per
> +     * window plus the bottom-most event selection. */
> +    ti->listeners = calloc(sprite->spriteTraceGood + 1, sizeof(*ti->listeners));
> +    if (!ti->listeners)
> +    {
> +        sprite->spriteTraceGood = 0;
> +        return FALSE;
> +    }
> +    ti->num_listeners = 0;
> +
> +    return TRUE;
> +}
> +
> +/**
> + * Delivers a touch event to all interested grabbing clients.  Will update
> + * ti->num_grabs for all events, and also ti->listeners for TouchBegin
> + * events.  May also update ev->flags.
> + */
> +static void
> +DeliverTouchGrabEvents(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
> +                       DeviceEvent *ev, Window child)
> +{
> +    SpritePtr sprite = &ti->sprite;
> +    DeviceIntPtr masterdev = sourcedev->u.master;
> +    DeviceIntPtr deliverdev;
> +    GrabPtr grab;
> +    xEvent *xi2 = NULL;
> +    Mask filter;
> +    WindowPtr win;
> +    int i, j, err, deliveries;
> +
> +    ti->num_grabs = 0;
> +    for (i = 0; i < sprite->spriteTraceGood; i++)
> +    {
> +        win = sprite->spriteTrace[i];
> +
> +        deliverdev = sourcedev;
> +        grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
> +        if (!grab && masterdev)
> +        {
> +            deliverdev = masterdev;
> +            grab = CheckPassiveGrabsOnWindow(win, deliverdev, ev, FALSE, FALSE);
> +        }
> +        if (!grab)
> +            continue;
> +
> +        /* Change the device ID in the event to reflect who we're delivering
> +         * to. */
> +        free(xi2);
> +        xi2 = NULL;
> +        ev->deviceid = deliverdev->id;
> +        err = EventToXI2((InternalEvent *) ev, &xi2);
> +        if (err != Success)
> +        {
> +            ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEvent (%d)\n",
> +                   sourcedev->name, err);
> +            return;
> +        }
> +        FixUpEventFromWindow(sprite, xi2, win, child, FALSE);
> +        filter = GetEventFilter(deliverdev, xi2);
> +
> +        /* Stash the list of currently-active listeners away at TouchBegin time,
> +         * then subsequently check against that list to make sure we only
> +         * deliver to that same list later on, so we don't deliver TouchMotion
> +         * events to clients which have never seen the corresponding
> +         * TouchBegin. */
> +        if (ev->type != ET_TouchBegin)
> +        {
> +            for (j = 0; j < ti->num_listeners; j++)
> +            {
> +                if (ti->listeners[j] == grab->resource)
> +                    break;
> +            }
> +            if (j == ti->num_listeners)
> +                continue;
> +        }
> +
> +        if (XaceHook(XACE_RECEIVE_ACCESS, rClient(grab), win,
> +                     (xEvent *) xi2, 1) != Success)
> +            continue;
> +        deliveries = TryClientEvents(rClient(grab), deliverdev, xi2, 1,
> +                                     filter, filter, NullGrab);
> +        if (deliveries > 0)
> +        {
> +            ti->num_grabs++;
> +            /* If we've made a delivery, the next one definitely won't be
> +             * to the owner. */
> +            ev->flags &= ~XITouchOwner;
> +            if (ev->type == ET_TouchBegin)
> +                ti->listeners[ti->num_listeners++] = grab->resource;
> +        }
> +    }
> +
> +    free(xi2);
> +}
> +
> +/**
> + * Delivers a touch event to all interested selecting clients.  Will update
> + * ti->listeners for TouchBegin events.
> + */
> +static void
> +DeliverTouchSelectionEvents(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
> +                            DeviceEvent *ev, Window child)
> +{
> +    SpritePtr sprite = &ti->sprite;
> +    DeviceIntPtr deliverdev;
> +    DeviceIntPtr masterdev = sourcedev->u.master;
> +    xEvent *xi2 = NULL;
> +    Mask filter = GetEventFilter(sourcedev, xi2);
> +    WindowPtr win;
> +    OtherInputMasks *inputMasks;
> +    InputClients *iclients;
> +    int evbyte = GetXI2Type((InternalEvent *) ev) / 8;
> +    int i, j, err, deliveries;
> +
> +    for (i = sprite->spriteTraceGood; i >= 0; i--)
> +    {
> +        /* First check whether anyone has selected for touch events on this
> +         * window at all. */
> +        win = sprite->spriteTrace[i];
> +        inputMasks = wOtherInputMasks(win);
> +        if (!inputMasks)
> +            continue;
> +        iclients = inputMasks->inputClients;
> +        if (!iclients)
> +            continue;
> +#define test_xi2_mask(x, d, b, f) ((x)->xi2mask[d][b] & f)

I like defining macros above a function and undefining them after the
end of the function. It makes it easier to read the code, imo. A static
inline function would be even better for type checking.

> +        if (!test_xi2_mask(inputMasks, sourcedev->id, evbyte, filter) &&
> +            !test_xi2_mask(inputMasks, XIAllDevices, evbyte, filter) &&
> +            !(masterdev &&
> +              (test_xi2_mask(inputMasks, masterdev->id, evbyte, filter) ||
> +               test_xi2_mask(inputMasks, XIAllMasterDevices, evbyte, filter))))
> +            continue;
> +
> +        for (; iclients; iclients = iclients->next)
> +        {
> +            /* Now find the particular client who selected for touch events. */
> +            if (test_xi2_mask(iclients, sourcedev->id, evbyte, filter) ||
> +                test_xi2_mask(iclients, XIAllDevices, evbyte, filter))
> +                deliverdev = sourcedev;
> +            else if (masterdev &&
> +                     (test_xi2_mask(iclients, masterdev->id, evbyte, filter) ||
> +                      test_xi2_mask(iclients, XIAllMasterDevices, evbyte,
> +                                    filter)))
> +                deliverdev = masterdev;
> +            else
> +                continue;
> +#undef test_xi2_mask
> +
> +            ev->deviceid = deliverdev->id;
> +            err = EventToXI2((InternalEvent *) ev, &xi2);
> +            if (err != Success)
> +            {
> +                ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEvent "
> +                       "(%d)\n", sourcedev->name, err);
> +                return;
> +            }
> +            FixUpEventFromWindow(sprite, xi2, win, child, FALSE);
> +
> +            /* See comment in grab-delivery TouchBegin branch. */
> +            if (ev->type != ET_TouchBegin) {
> +                for (j = 0; j < ti->num_listeners; j++)
> +                {
> +                    if (ti->listeners[j] == iclients->resource)
> +                        break;
> +                }
> +                if (j == ti->num_listeners)
> +                {
> +                    free(xi2);
> +                    continue;
> +                }
> +            }

Don't we just need to check:

if (to->listeners[ti->num_listeners - 1] == iclients->resource)

instead of looping over all the resources? Essentially, isn't the
selection client always the last client in the list since
DeliverTouchSelectionEvents is called after DeliverTouchGrabEvents?

> +
> +            if (XaceHook(XACE_RECEIVE_ACCESS, rClient(iclients), win,
> +                         (xEvent *) xi2, 1) != Success)
> +                continue;
> +            deliveries = TryClientEvents(rClient(iclients), deliverdev, xi2, 1,
> +                                         filter, filter, NullGrab);
> +            /* As only one event selection can receive events, bail as soon as
> +             * we find one. */
> +            if (ev->type == ET_TouchBegin && deliveries > 0)
> +            {
> +                ti->listeners[ti->num_listeners++] = iclients->resource;
> +                free(xi2);
> +                return;
> +            }
> +            else if (ev->type != ET_TouchBegin)
> +            {
> +                free(xi2);
> +                return;
> +            }
> +        }
> +    }
> +}

The delivery portion (from my last comment to here) is the same as for
the grab delivery function. Should it be split out?

> +
> +/**
> + * Processes and delivers a TouchBegin, TouchMotion or a TouchEnd event.
> + *
> + * Due to having rather different delivery semantics (see the Xi 2.1 protocol
> + * spec for more information), this implements its own grab and event-selection
> + * delivery logic.
> + */
> +static void
> +ProcessTouchEvent(DeviceEvent *ev, DeviceIntPtr sourcedev)
> +{
> +    TouchClassPtr t;
> +    TouchPointInfoPtr ti;
> +    DeviceIntPtr masterdev = NULL;
> +    Window child;
> +    SpritePtr sprite;
> +    int touch;
> +
> +    /* We handle deliveries to MDs through the SD, rather than copying
> +     * the event and processing it twice. */
> +    if (IsMaster(sourcedev))
> +        return;
> +
> +    if (sourcedev->u.master)
> +        masterdev = sourcedev->u.master;
> +
> +    if (!sourcedev->touch)
> +        return;
> +    t = sourcedev->touch;
> +
> +    touch = FindTouchPoint(sourcedev, ev->detail.touch);
> +    if (touch < 0)
> +    {
> +        DebugF("[Xi] %s: Received event for inactive touchpoint %d\n",
> +               sourcedev->name, ev->detail.touch);
> +        return;
> +    }
> +    ti = &t->touches[touch];
> +    sprite = &ti->sprite;
> +
> +    /* If we get a TouchEnd event but someone still has an open grab, just
> +     * flip pending_finish and wait for ownership to settle. */
> +    if (ev->type == ET_TouchEnd && ti->num_grabs)
> +    {
> +        ev->type = ET_TouchMotion;
> +        ev->flags |= XITouchPendingFinish;
> +        ti->pending_finish = TRUE;
> +    }
> +
> +    /* The first delivery we make will be to the owner, so set the owner flag
> +     * here, and clear it once we've made a delivery. */
> +    ev->flags |= XITouchOwner;
> +
> +    /* Make sure we have a valid window trace for event delivery. */
> +    if (!EnsureTouchSprite(sourcedev, ti, ev))
> +        return;
> +
> +    /* Now deliver first to grabbing clients, then to clients with an
> +     * applicable event selection. */
> +    child = sprite->spriteTrace[sprite->spriteTraceGood - 1]->drawable.id;
> +    DeliverTouchGrabEvents(sourcedev, ti, ev, child);
> +    DeliverTouchSelectionEvents(sourcedev, ti, ev, child);
> +
> +    if (ev->type == ET_TouchEnd)
> +        FinishTouchPoint(sourcedev, ev->detail.touch);
> +}
> +
> +/**
>   * Main device event processing function.
>   * Called from when processing the events from the event queue.
>   *
> @@ -954,6 +1305,12 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
>      {
>          ProcessRawEvent(&ev->raw_event, device);
>          return;
> +    } else if (ev->any.type == ET_TouchBegin ||
> +               ev->any.type == ET_TouchMotion ||
> +               ev->any.type == ET_TouchEnd)
> +    {
> +        ProcessTouchEvent(&ev->device_event, device);
> +        return;
>      }
>  
>      if (IsPointerDevice(device))
> @@ -1152,6 +1509,30 @@ InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int
>          dev->proximity->in_proximity = FALSE;
>  }
>  
> +void
> +InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
> +                            int maxval, int resolution)
> +{
> +    TouchAxisInfoPtr ax;
> +
> +    if (!dev || !dev->touch || minval > maxval)
> +        return;
> +    if (axnum >= dev->touch->num_axes)
> +        return;
> +
> +    ax = dev->touch->axes + axnum;
> +
> +    ax->min_value = minval;
> +    ax->max_value = maxval;
> +    ax->resolution = resolution;
> +    ax->label = label;
> +
> +    if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X))
> +        dev->touch->x_axis = axnum;
> +    else if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y))
> +        dev->touch->y_axis = axnum;
> +}
> +
>  static void
>  FixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
>  		     ButtonClassPtr b, ValuatorClassPtr v, int first)
> @@ -1562,6 +1943,34 @@ GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
>      return AddPassiveGrabToList(client, grab);
>  }
>  
> +/* Touch grab */
> +int
> +GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
> +          GrabParameters *param, GrabMask *mask)
> +{
> +    WindowPtr pWin;
> +    GrabPtr grab;
> +    int rc;
> +
> +    rc = CheckGrabValues(client, param);
> +    if (rc != Success)
> +        return rc;
> +
> +    rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
> +    if (rc != Success)
> +	return rc;
> +    rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixGrabAccess);
> +    if (rc != Success)
> +	return rc;
> +
> +    grab = CreateGrab(client->index, dev, mod_dev, pWin, GRABTYPE_XI2,
> +                      mask, param, XI_TouchBegin, 0, NullWindow, NullCursor);
> +    if (!grab)
> +        return BadAlloc;
> +
> +    return AddPassiveGrabToList(client, grab);
> +}
> +
>  int
>  SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
>  		Mask mask, Mask exclusivemasks)
> @@ -1734,6 +2143,36 @@ InputClientGone(WindowPtr pWin, XID id)
>      FatalError("client not on device event list");
>  }
>  
> +/**
> + * Search for window in each touch trace for each device. Remove the window
> + * and all its subwindows from the trace when found. The initial window
> + * order is preserved.
> + */
> +void WindowGone(WindowPtr win)
> +{
> +    DeviceIntPtr dev;
> +
> +    for (dev = inputInfo.devices; dev; dev = dev->next) {
> +        TouchClassPtr t = dev->touch;
> +        int i;
> +
> +        if (!t)
> +            continue;
> +
> +        for (i = 0; i < t->num_touches; i++) {
> +            SpritePtr sprite = &t->touches[i].sprite;
> +            int j;
> +
> +            for (j = 0; j < sprite->spriteTraceGood; j++) {
> +                if (sprite->spriteTrace[j] == win) {
> +                    sprite->spriteTraceGood = j;
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +}
> +
>  int
>  SendEvent(ClientPtr client, DeviceIntPtr d, Window dest, Bool propagate,
>  	  xEvent * ev, Mask mask, int count)
> diff --git a/Xi/extinit.c b/Xi/extinit.c
> index 82df7eb..8adec8f 100644
> --- a/Xi/extinit.c
> +++ b/Xi/extinit.c
> @@ -258,7 +258,8 @@ static int (*ProcIVector[])(ClientPtr) = {
>          ProcXIChangeProperty,                   /* 57 */
>          ProcXIDeleteProperty,                   /* 58 */
>          ProcXIGetProperty,                      /* 59 */
> -        ProcXIGetSelectedEvents                 /* 60 */
> +        ProcXIGetSelectedEvents,                /* 60 */
> +        ProcXIAllowTouchEvents,                 /* 61 */
>  };
>  
>  /* For swapped clients */
> @@ -323,7 +324,8 @@ static int (*SProcIVector[])(ClientPtr) = {
>          SProcXIChangeProperty,                   /* 57 */
>          SProcXIDeleteProperty,                   /* 58 */
>          SProcXIGetProperty,                      /* 59 */
> -        SProcXIGetSelectedEvents                 /* 60 */
> +        SProcXIGetSelectedEvents,                /* 60 */
> +        SProcXIAllowTouchEvents,                 /* 61 */
>  };
>  
>  /*****************************************************************
> @@ -881,6 +883,9 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
>          case XI_KeyRelease:
>          case XI_ButtonPress:
>          case XI_ButtonRelease:
> +        case XI_TouchBegin:
> +        case XI_TouchMotion:
> +        case XI_TouchEnd:
>              SDeviceEvent((xXIDeviceEvent*)from, (xXIDeviceEvent*)to);
>              break;
>          case XI_RawMotion:
> diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c
> index 3077e1a..93905fd 100644
> --- a/Xi/xiallowev.c
> +++ b/Xi/xiallowev.c
> @@ -35,6 +35,8 @@
>  
>  #include "inputstr.h"	/* DeviceIntPtr      */
>  #include "windowstr.h"	/* window structure  */
> +#include "eventstr.h"
> +#include "mi.h"
>  #include <X11/extensions/XI2.h>
>  #include <X11/extensions/XI2proto.h>
>  
> @@ -101,3 +103,110 @@ ProcXIAllowEvents(ClientPtr client)
>      return ret;
>  }
>  
> +int
> +SProcXIAllowTouchEvents(ClientPtr client)
> +{
> +    char n;
> +
> +    REQUEST(xXIAllowTouchEventsReq);
> +
> +    swaps(&stuff->length, n);
> +    swaps(&stuff->deviceid, n);
> +    swapl(&stuff->touchid, n);
> +
> +    return ProcXIAllowTouchEvents(client);
> +}
> +
> +int
> +ProcXIAllowTouchEvents(ClientPtr client)
> +{
> +    DeviceIntPtr dev;
> +    TouchPointInfoPtr ti;
> +    int ret, touch, i, nev;
> +    uint32_t flags = 0;
> +    ValuatorMask *mask = valuator_mask_new(0);
> +    EventList *events = InitEventList(GetMaximumEventsNum());
> +    DeviceEvent *ev;
> +
> +    REQUEST(xXIAllowTouchEventsReq);
> +    REQUEST_SIZE_MATCH(xXIAllowTouchEventsReq);
> +
> +    if (!mask || !events)
> +        return BadAlloc;
> +
> +    ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
> +    if (ret != Success)
> +        return ret;
> +    if (!dev->touch)
> +    {
> +        client->errorValue = stuff->deviceid;
> +        return BadDevice;
> +    }
> +
> +    touch = FindTouchPoint(dev, stuff->touchid);
> +    if (touch < 0)
> +    {
> +        client->errorValue = touch;
> +        return BadValue;
> +    }
> +    ti = &dev->touch->touches[touch];
> +
> +    if (ti->num_listeners == 0 ||
> +        CLIENT_ID(ti->listeners[0]) != client->index)
> +        return BadAccess;
> +
> +    if (stuff->mode & XITouchOwnerAccept)
> +    {
> +        ProcessInputEvents();

Shouldn't this ^^ be after we've validated the flags below, as is done
for the reject case?

> +
> +        if (stuff->mode & ~(XITouchOwnerAccept | XITouchNoPointerEmulation))
> +        {
> +            client->errorValue = stuff->mode;
> +            return BadValue;
> +        }
> +
> +        if (stuff->mode & XITouchNoPointerEmulation)
> +            ti->emulate_pointer = FALSE;
> +
> +        flags |= XITouchOwnerAccepted;
> +        nev = GetTouchEvents(events, dev, stuff->touchid, mask, 0, flags);
> +        if (nev == 0)
> +            return BadAlloc;
> +        for (i = 0; i < nev; i++)
> +        {
> +            ev = (DeviceEvent *)((events + i)->event);
> +            mieqProcessDeviceEvent(dev, (InternalEvent *) ev, NULL);
> +        }
> +
> +        ti->num_listeners = 1;
> +    }
> +    else if (stuff->mode & XITouchOwnerReject)
> +    {
> +        if (stuff->mode & ~XITouchOwnerReject)
> +        {
> +            client->errorValue = stuff->mode;
> +            return BadValue;
> +        }
> +
> +        for (i = 0; i < ti->num_listeners - 1; i++)
> +            ti->listeners[i] = ti->listeners[i + 1];
> +        ti->num_listeners--;
> +
> +        ProcessInputEvents();
> +        nev = GetTouchEvents(events, dev, stuff->touchid, mask, 0, 0);
> +        if (nev == 0)
> +            return BadAlloc;
> +        for (i = 0; i < nev; i++)
> +        {
> +            ev = (DeviceEvent *)((events + i)->event);
> +            mieqProcessDeviceEvent(dev, (InternalEvent *) ev, NULL);
> +        }
> +    }
> +    else
> +    {
> +        client->errorValue = stuff->mode;
> +        return BadValue;
> +    }
> +
> +    return Success;
> +}
> diff --git a/Xi/xiallowev.h b/Xi/xiallowev.h
> index 3a417b9..ca45ee3 100644
> --- a/Xi/xiallowev.h
> +++ b/Xi/xiallowev.h
> @@ -32,5 +32,7 @@
>  
>  int ProcXIAllowEvents(ClientPtr client);
>  int SProcXIAllowEvents(ClientPtr client);
> +int ProcXIAllowTouchEvents(ClientPtr client);
> +int SProcXIAllowTouchEvents(ClientPtr client);
>  
>  #endif /* XIALLOWEV_H */
> diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
> index e99b6e5..972988e 100644
> --- a/Xi/xipassivegrab.c
> +++ b/Xi/xipassivegrab.c
> @@ -105,19 +105,30 @@ ProcXIPassiveGrabDevice(ClientPtr client)
>      if (stuff->grab_type != XIGrabtypeButton &&
>          stuff->grab_type != XIGrabtypeKeycode &&
>          stuff->grab_type != XIGrabtypeEnter &&
> -        stuff->grab_type != XIGrabtypeFocusIn)
> +        stuff->grab_type != XIGrabtypeFocusIn &&
> +        stuff->grab_type != XIGrabtypeTouchBegin)
>      {
>          client->errorValue = stuff->grab_type;
>          return BadValue;
>      }
>  
>      if ((stuff->grab_type == XIGrabtypeEnter ||
> -         stuff->grab_type == XIGrabtypeFocusIn) && stuff->detail != 0)
> +         stuff->grab_type == XIGrabtypeFocusIn ||
> +         stuff->grab_type == XIGrabtypeTouchBegin) &&
> +        stuff->detail != 0)
>      {
>          client->errorValue = stuff->detail;
>          return BadValue;
>      }
>  
> +    if (stuff->grab_type == XIGrabtypeTouchBegin &&
> +        (stuff->grab_mode != GrabModeAsync ||
> +         stuff->paired_device_mode != GrabModeAsync))
> +    {
> +        client->errorValue = GrabModeSync;
> +        return BadValue;
> +    }
> +
>      if (XICheckInvalidMaskBits(client, (unsigned char*)&stuff[1],
>                                 stuff->mask_len * 4) != Success)
>          return BadValue;
> @@ -185,6 +196,9 @@ ProcXIPassiveGrabDevice(ClientPtr client)
>                  status = GrabWindow(client, dev, stuff->grab_type,
>                                      &param, &mask);
>                  break;
> +            case XIGrabtypeTouchBegin:
> +                status = GrabTouch(client, dev, mod_dev, &param, &mask);
> +                break;
>          }
>  
>          if (status != GrabSuccess)
> diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
> index fdd2c05..947cd8d 100644
> --- a/Xi/xiquerydevice.c
> +++ b/Xi/xiquerydevice.c
> @@ -232,6 +232,12 @@ SizeDeviceClasses(DeviceIntPtr dev)
>      if (dev->valuator)
>          len += sizeof(xXIValuatorInfo) * dev->valuator->numAxes;
>  
> +    if (dev->touch)
> +    {
> +        len += sizeof(xXITouchInfo);
> +        len += sizeof(xXITouchValuatorInfo) * dev->touch->num_axes;
> +    }
> +
>      return len;
>  }
>  
> @@ -373,6 +379,73 @@ SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info)
>      swaps(&info->sourceid, n);
>  }
>  
> +/**
> + * List multitouch information
> + *
> + * @return The number of bytes written into info.
> + */
> +int
> +ListTouchInfo(DeviceIntPtr dev, xXITouchInfo *touch)
> +{
> +    touch->type = XITouchClass;
> +    touch->length = sizeof(xXITouchInfo) >> 2;
> +    touch->sourceid = dev->id;
> +    touch->mode = dev->touch->mode;
> +    touch->num_touches = dev->touch->num_touches;
> +
> +    return touch->length << 2;
> +}
> +
> +static void
> +SwapTouchInfo(DeviceIntPtr dev, xXITouchInfo* touch)
> +{
> +    char n;
> +    swaps(&touch->type, n);
> +    swaps(&touch->length, n);
> +    swaps(&touch->sourceid, n);
> +}
> +
> +/**
> + * List multitouch axis information
> + *
> + * @return The number of bytes written into info.
> + */
> +int
> +ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
> +                      int axisnumber)
> +{
> +    TouchClassPtr t = dev->touch;
> +
> +    val->type = XITouchValuatorClass;
> +    val->length = sizeof(xXITouchValuatorInfo) >> 2;
> +    val->sourceid = dev->id;
> +    val->number = axisnumber;
> +    val->label = t->axes[axisnumber].label;
> +    val->min.integral = t->axes[axisnumber].min_value;
> +    val->min.frac = 0;
> +    val->max.integral = t->axes[axisnumber].max_value;
> +    val->max.frac = 0;
> +    val->resolution = t->axes[axisnumber].resolution;
> +
> +    return val->length << 2;
> +}
> +
> +static void
> +SwapTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val)
> +{
> +    char n;
> +    swaps(&val->type, n);
> +    swaps(&val->length, n);
> +    swaps(&val->sourceid, n);
> +    swaps(&val->number, n);
> +    swapl(&val->label, n);
> +    swapl(&val->min.integral, n);
> +    swapl(&val->min.frac, n);
> +    swapl(&val->max.integral, n);
> +    swapl(&val->max.frac, n);
> +    swapl(&val->resolution, n);
> +}
> +
>  int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
>  {
>      DeviceIntPtr master = dev->u.master;
> @@ -462,6 +535,22 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
>          total_len += len;
>      }
>  
> +    if (dev->touch)
> +    {
> +        (*nclasses)++;
> +        len = ListTouchInfo(dev, (xXITouchInfo*)any);
> +        any += len;
> +        total_len += len;
> +
> +        for (i = 0; i < dev->touch->num_axes; i++)
> +        {
> +            (*nclasses)++;
> +            len = ListTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any, i);
> +            any += len;
> +            total_len += len;
> +        }
> +    }
> +
>      return total_len;
>  }
>  
> @@ -489,6 +578,12 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
>              case XIValuatorClass:
>                  SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
>                  break;
> +            case XITouchClass:
> +                SwapTouchInfo(dev, (xXITouchInfo*)any);
> +                break;
> +            case XITouchValuatorClass:
> +                SwapTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any);
> +                break;
>          }
>  
>          any += len * 4;
> diff --git a/Xi/xiquerydevice.h b/Xi/xiquerydevice.h
> index 02f0659..59326f7 100644
> --- a/Xi/xiquerydevice.h
> +++ b/Xi/xiquerydevice.h
> @@ -44,4 +44,7 @@ int ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info, Bool reportState);
>  int ListKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info);
>  int ListValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info,
>  		     int axisnumber, Bool reportState);
> +int ListTouchInfo(DeviceIntPtr dev, xXITouchInfo* info);
> +int ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
> +                          int axisnumber);
>  #endif /* QUERYDEV_H */
> diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
> index 22fbaf5..72ca794 100644
> --- a/Xi/xiselectev.c
> +++ b/Xi/xiselectev.c
> @@ -152,6 +152,47 @@ ProcXISelectEvents(ClientPtr client)
>              }
>          }
>  
> +        if (evmask->mask_len >= 1)
> +        {
> +            unsigned char *bits = (unsigned char*)&evmask[1];
> +
> +            /* All three touch events must be selected at once */
> +            if ((BitIsOn(bits, XI_TouchBegin) ||
> +                 BitIsOn(bits, XI_TouchMotion) ||
> +                 BitIsOn(bits, XI_TouchEnd)) &&
> +                (!BitIsOn(bits, XI_TouchBegin) ||
> +                 !BitIsOn(bits, XI_TouchMotion) ||
> +                 !BitIsOn(bits, XI_TouchEnd)))
> +            {
> +                client->errorValue = XI_TouchBegin;
> +                return BadValue;
> +            }
> +
> +            /* Only one client per window may select for touch events on the
> +             * same devices, including master devices.
> +             * XXX: This breaks if a device goes from floating to attached. */

Maybe we should prohibit XIAllMasterDevices and any slave device
selection at the same time, just as is done for XIAllDevices?

> +            if (BitIsOn(bits, XI_TouchBegin))
> +            {
> +                OtherInputMasks *inputMasks = wOtherInputMasks(win);
> +                InputClients *iclient = NULL;
> +                if (inputMasks)
> +                    iclient = inputMasks->inputClients;
> +                for (; iclient; iclient = iclient->next)
> +                {
> +                    if (CLIENT_ID(iclient->resource) == client->index)
> +                        continue;
> +                    if (BitIsOn(iclient->xi2mask[evmask->deviceid],
> +                                XI_TouchBegin) ||
> +                        BitIsOn(iclient->xi2mask[XIAllDevices],
> +                                XI_TouchBegin) ||
> +                        (dev && (IsMaster(dev) || dev->u.master) &&
> +                         BitIsOn(iclient->xi2mask[XIAllMasterDevices],
> +                                 XI_TouchBegin)))
> +                        return BadAccess;
> +                }
> +            }
> +        }
> +
>          if (XICheckInvalidMaskBits(client, (unsigned char*)&evmask[1],
>                                     evmask->mask_len * 4) != Success)
>              return BadValue;
> diff --git a/configure.ac b/configure.ac
> index 1ceffe7..55e00ed 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -783,7 +783,7 @@ WINDOWSWMPROTO="windowswmproto"
>  APPLEWMPROTO="applewmproto >= 1.4"
>  
>  dnl Core modules for most extensions, et al.
> -SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.4] [renderproto >= 0.11] [xextproto >= 7.1.99] [inputproto >= 1.9.99.902] [kbproto >= 1.0.3] fontsproto"
> +SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.4] [renderproto >= 0.11] [xextproto >= 7.1.99] [inputproto >= 2.0.99.1] [kbproto >= 1.0.3] fontsproto"
>  # Make SDK_REQUIRED_MODULES available for inclusion in xorg-server.pc
>  AC_SUBST(SDK_REQUIRED_MODULES)
>  
> diff --git a/dix/devices.c b/dix/devices.c
> index 6c0dc42..c532db8 100644
> --- a/dix/devices.c
> +++ b/dix/devices.c
> @@ -754,6 +754,20 @@ FreeDeviceClass(int type, pointer *class)
>                  free((*v));
>                  break;
>              }
> +        case XITouchClass:
> +            {
> +                TouchClassPtr *t = (TouchClassPtr*)class;
> +                int i;
> +
> +                for (i = 0; i < (*t)->num_touches; i++)
> +                {
> +                    free((*t)->touches[i].sprite.spriteTrace);
> +                    free((*t)->touches[i].listeners);
> +                }
> +
> +                free((*t));
> +                break;
> +            }
>          case FocusClass:
>              {
>                  FocusClassPtr *f = (FocusClassPtr*)class;
> @@ -862,6 +876,7 @@ FreeAllDeviceClasses(ClassesPtr classes)
>  
>      FreeDeviceClass(KeyClass, (pointer)&classes->key);
>      FreeDeviceClass(ValuatorClass, (pointer)&classes->valuator);
> +    FreeDeviceClass(XITouchClass, (pointer)&classes->touch);
>      FreeDeviceClass(ButtonClass, (pointer)&classes->button);
>      FreeDeviceClass(FocusClass, (pointer)&classes->focus);
>      FreeDeviceClass(ProximityClass, (pointer)&classes->proximity);
> @@ -1543,6 +1558,84 @@ InitPointerDeviceStruct(DevicePtr device, CARD8 *map, int numButtons, Atom* btn_
>  	   InitPtrFeedbackClassDeviceStruct(dev, controlProc));
>  }
>  
> +/**
> + * Sets up multitouch capabilities on @device.
> + *
> + * @max_touches The maximum number of simultaneous touches, or 0 for unlimited.
> + * @mode The mode of the touch device (XIDirectTouch or XIDependentTouch).
> + * @num_axes The number of touch valuator axes.
> + */
> +Bool
> +InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches,
> +                           unsigned int mode, unsigned int num_axes)
> +{
> +    TouchClassPtr touch;
> +    int *valuators;
> +    int i;
> +
> +    if (device->touch)
> +        return FALSE;
> +
> +    if ((mode != XIDirectTouch && mode != XIDependentTouch) ||
> +        max_touches == 0 || num_axes < 2)
> +        return FALSE;
> +
> +    if (num_axes > MAX_VALUATORS)
> +    {
> +        LogMessage(X_WARNING,
> +                   "Device '%s' has %d axes, only using first %d.\n",
> +                   device->name, num_axes, MAX_VALUATORS);
> +        num_axes = MAX_VALUATORS;
> +    }
> +
> +    touch = calloc(1,
> +                   sizeof(TouchClassRec) +
> +                   num_axes * sizeof(TouchAxisInfoRec) +
> +                   max_touches * sizeof(TouchPointInfoRec) +
> +                   max_touches * num_axes * sizeof(int));
> +    if (!touch)
> +        return FALSE;
> +
> +    touch->axes = (TouchAxisInfoPtr)(touch + 1);
> +    touch->touches = (TouchPointInfoPtr)(touch->axes + num_axes);
> +
> +    valuators = (int *)(touch->touches + max_touches);
> +    for (i = 0; i < max_touches; i++)
> +    {
> +        touch->touches[i].valuators = valuators;
> +        valuators += num_axes;
> +    }
> +
> +    for (i = 0; i < max_touches; i++)
> +    {
> +        SpritePtr sprite = &touch->touches[i].sprite;
> +
> +        sprite->spriteTrace = calloc(32, sizeof(*sprite->spriteTrace));
> +        if (!sprite->spriteTrace)
> +        {
> +            free(touch);
> +            return FALSE;
> +        }
> +        sprite->spriteTraceSize = 32;
> +        sprite->spriteTrace[0] = screenInfo.screens[0]->root;
> +        sprite->hot.pScreen = screenInfo.screens[0];
> +        sprite->hotPhys.pScreen = screenInfo.screens[0];
> +
> +        touch->touches[i].id = -1;
> +    }
> +
> +    touch->num_axes = num_axes;
> +    touch->num_touches = max_touches;
> +    touch->mode = mode;
> +    touch->last_touchid = (unsigned int) -1;
> +    touch->x_axis = -1;
> +    touch->y_axis = -1;
> +
> +    device->touch = touch;
> +
> +    return TRUE;
> +}
> +
>  /*
>   * Check if the given buffer contains elements between low (inclusive) and
>   * high (inclusive) only.
> diff --git a/dix/eventconvert.c b/dix/eventconvert.c
> index 76d9a3e..63957b5 100644
> --- a/dix/eventconvert.c
> +++ b/dix/eventconvert.c
> @@ -139,6 +139,9 @@ EventToCore(InternalEvent *event, xEvent *core)
>          case ET_RawButtonPress:
>          case ET_RawButtonRelease:
>          case ET_RawMotion:
> +        case ET_TouchBegin:
> +        case ET_TouchEnd:
> +        case ET_TouchMotion:
>              return BadMatch;
>          default:
>              /* XXX: */
> @@ -184,6 +187,9 @@ EventToXI(InternalEvent *ev, xEvent **xi, int *count)
>          case ET_RawButtonPress:
>          case ET_RawButtonRelease:
>          case ET_RawMotion:
> +        case ET_TouchBegin:
> +        case ET_TouchEnd:
> +        case ET_TouchMotion:
>              *count = 0;
>              *xi = NULL;
>              return BadMatch;
> @@ -225,6 +231,9 @@ EventToXI2(InternalEvent *ev, xEvent **xi)
>          case ET_ButtonRelease:
>          case ET_KeyPress:
>          case ET_KeyRelease:
> +        case ET_TouchBegin:
> +        case ET_TouchMotion:
> +        case ET_TouchEnd:
>              return eventToDeviceEvent(&ev->device_event, xi);
>          case ET_ProximityIn:
>          case ET_ProximityOut:
> @@ -728,6 +737,9 @@ GetXI2Type(InternalEvent *event)
>          case ET_RawMotion:      xi2type = XI_RawMotion;        break;
>          case ET_FocusIn:        xi2type = XI_FocusIn;          break;
>          case ET_FocusOut:       xi2type = XI_FocusOut;         break;
> +        case ET_TouchBegin:     xi2type = XI_TouchBegin;       break;
> +        case ET_TouchEnd:       xi2type = XI_TouchEnd;         break;
> +        case ET_TouchMotion:    xi2type = XI_TouchMotion;      break;
>          default:
>              break;
>      }
> diff --git a/dix/getevents.c b/dix/getevents.c
> index 794df42..0da3536 100644
> --- a/dix/getevents.c
> +++ b/dix/getevents.c
> @@ -47,6 +47,7 @@
>  #include "eventstr.h"
>  #include "eventconvert.h"
>  #include "inpututils.h"
> +#include "windowstr.h"
>  
>  #include <X11/extensions/XKBproto.h>
>  #include "xkbsrv.h"
> @@ -1287,6 +1288,119 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type, const Valuato
>  }
>  
>  /**
> + * Get events for a touch. Generates a TouchBegin event if end is not set and
> + * the touch id is not active. Generates a TouchMotion event if end is not set
> + * and the touch id is active. Generates a TouchEnd event if end is set and the
> + * touch id is active.
> + *
> + * events is not NULL-terminated; the return value is the number of events.
> + * The DDX is responsible for allocating the event structure in the first
> + * place via GetMaximumEventsNum(), and for freeing it.
> + */
> +int
> +GetTouchEvents(EventList *events, DeviceIntPtr pDev, unsigned int touchid,
> +               const ValuatorMask *mask_in, Bool end, uint32_t flags)
> +{
> +    int x, y; /* in screen co-ord space */
> +    float x_frac = 0.0, y_frac = 0.0; /* as above */
> +    DeviceEvent *event;
> +    ValuatorMask mask;
> +    int touch, i;
> +    TouchClassPtr t = pDev->touch;
> +    ScreenPtr scr = pDev->spriteInfo->sprite->hotPhys.pScreen;
> +    CARD32 ms = GetTimeInMillis();
> +
> +    if (!pDev->enabled)
> +        return 0;
> +
> +    if (!t)
> +        return 0;
> +
> +    if (t->x_axis < 0 || t->y_axis < 0)
> +        return 0;
> +
> +    event = (DeviceEvent *)events->event;
> +    init_event(pDev, event, ms);
> +
> +    touch = FindTouchPoint(pDev, touchid);
> +    if (touch < 0) {
> +        /* We obviously can't finish a non-existent touch. */
> +        if (end)
> +            return 0;
> +
> +        /* If we're starting a touch, we must have x & y co-ordinates. */
> +        if (!valuator_mask_isset(mask_in, t->x_axis) ||
> +            !valuator_mask_isset(mask_in, t->y_axis))
> +            return 0;
> +
> +        /* Touch IDs must increase monotonically.
> +         * XXX: Deal with this so the drivers don't have to. */
> +        if (touchid - t->last_touchid > INT_MAX) {
> +            LogMessage(X_WARNING, "[dix] %s: New touch ID %d going backwards, "
> +                       "dropping touch.\n", pDev->name, touchid);
> +            return 0;
> +        }
> +
> +        touch = CreateTouchPoint(pDev, touchid);
> +        if (touch < 0)
> +            return 0;
> +
> +        event->type = ET_TouchBegin;
> +        t->last_touchid = touchid;
> +    }
> +    else if (end) {
> +        event->type = ET_TouchEnd;
> +    }
> +    else {
> +        event->type = ET_TouchMotion;
> +    }
> +
> +    valuator_mask_copy(&mask, mask_in);
> +
> +    /* Get our screen event co-ordinates (root_x/root_y/event_x/event_y):
> +     * these come from the touchpoint in Absolute mode, or the sprite in
> +     * Relative. */
> +    if (t->mode == XIDirectTouch) {
> +        if (valuator_mask_isset(&mask, t->x_axis))
> +            x = valuator_mask_get(&mask, t->x_axis);
> +        else
> +            x = t->touches[touch].valuators[t->x_axis];
> +        x = rescaleValuatorAxis(x, 0.0, &x_frac,
> +                                (AxisInfoPtr)(t->axes + t->x_axis),
> +                                NULL, scr->width);
> +
> +        if (valuator_mask_isset(&mask, t->y_axis))
> +            y = valuator_mask_get(&mask, t->y_axis);
> +        else
> +            y = t->touches[touch].valuators[t->y_axis];
> +        y = rescaleValuatorAxis(y, 0.0, &y_frac,
> +                                (AxisInfoPtr)(t->axes + t->y_axis),
> +                                NULL, scr->height);
> +    }
> +    else {
> +        x = pDev->spriteInfo->sprite->hotPhys.x;
> +        y = pDev->spriteInfo->sprite->hotPhys.y;
> +    }
> +
> +    event->root = scr->root->drawable.id;
> +    event->root_x = x;
> +    event->root_y = y;
> +    event->root_x_frac = x_frac;
> +    event->root_y_frac = y_frac;
> +    event->detail.touch = touchid;
> +    event->flags = flags;
> +
> +    set_valuators(pDev, event, &mask);
> +    for (i = 0; i < t->num_axes; i++)
> +    {
> +        if (valuator_mask_isset(&mask, i))
> +            t->touches[touch].valuators[i] = valuator_mask_get(&mask, i);
> +    }
> +
> +    return 1;
> +}
> +
> +/**
>   * Synthesize a single motion event for the core pointer.
>   *
>   * Used in cursor functions, e.g. when cursor confinement changes, and we need
> diff --git a/dix/grabs.c b/dix/grabs.c
> index 69c58df..9c9f14b 100644
> --- a/dix/grabs.c
> +++ b/dix/grabs.c
> @@ -60,6 +60,7 @@ SOFTWARE.
>  #include "dixgrabs.h"
>  #include "xace.h"
>  #include "exevents.h"
> +#include "mi.h"
>  
>  #define BITMASK(i) (((Mask)1) << ((i) & 31))
>  #define MASKIDX(i) ((i) >> 5)
> @@ -114,9 +115,63 @@ CreateGrab(
>  
>  }
>  
> +/* As touch grabs don't turn into active grabs with their own resources, we
> + * need to walk all the touches and remove this grab from any delivery
> + * lists. */
> +static void
> +FreeTouchGrab(GrabPtr pGrab)
> +{
> +    TouchPointInfoPtr ti;
> +    DeviceIntPtr dev;
> +    InternalEvent *ev;
> +    ValuatorMask *mask = valuator_mask_new(0); /* XXX use a static */
> +    EventList *events = InitEventList(GetMaximumEventsNum());
> +    int i, j, nev;
> +
> +    if (!mask || !events)
> +        FatalError("FreeTouchGrab: couldn't allocate valuator_mask/events\n");
> +
> +    for (dev = inputInfo.devices; dev; dev = dev->next)
> +    {
> +        if (!dev->touch)
> +            continue;
> +
> +        for (i = 0; i < dev->touch->num_touches; i++)
> +        {
> +            ti = &dev->touch->touches[i];
> +            for (j = 0; j < ti->num_listeners; j++)
> +            {
> +                if (ti->listeners[j] != pGrab->resource)
> +                    continue;
> +
> +                while (j < ti->num_listeners - 1)
> +                {
> +                    ti->listeners[j] = ti->listeners[j + 1];
> +                    break;
> +                }
> +                ti->num_listeners--;
> +                ti->num_grabs--;
> +
> +                ProcessInputEvents();
> +                nev = GetTouchEvents(events, dev, ti->id, mask, 0, 0);
> +                for (j = 0; j < nev; j++)
> +                {
> +                    ev = (InternalEvent *)((events + j)->event);
> +                    mieqProcessDeviceEvent(dev, ev, NULL);
> +                }

Shouldn't we only be sending ownership change events if the owner (the
first in the listeners array) has its grab removed?

Also, we should be adding something to the path for when a client goes
away to remove its resource from the listeners.

> +
> +                break;
> +            }
> +        }
> +    }
> +}
> +
>  static void
>  FreeGrab(GrabPtr pGrab)
>  {
> +    if (pGrab->grabtype == GRABTYPE_XI2 && pGrab->type == XI_TouchBegin)
> +        FreeTouchGrab(pGrab);
> +
>      free(pGrab->modifiersDetail.pMask);
>      free(pGrab->detail.pMask);
>  
> @@ -243,6 +298,23 @@ GrabSupersedesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab)
>  }
>  
>  /**
> + * Returns the event type to match when comparing grabs.
> + */
> +static uint32_t
> +GetGrabEventMatch(GrabPtr pGrab)
> +{
> +    if (pGrab->grabtype != GRABTYPE_XI2)
> +        return pGrab->type;
> +
> +    if (pGrab->type == XI_TouchBegin ||
> +        pGrab->type == XI_TouchMotion ||
> +        pGrab->type == XI_TouchEnd)
> +        return XI_TouchBegin;
> +
> +    return pGrab->type;
> +}
> +
> +/**
>   * Compares two grabs and returns TRUE if the first grab matches the second
>   * grab. 
>   * 
> @@ -261,6 +333,8 @@ GrabMatchesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab, Bool ignoreDevice)
>      unsigned int any_modifier = (pFirstGrab->grabtype == GRABTYPE_XI2) ?
>                                  (unsigned int)XIAnyModifier :
>                                  (unsigned int)AnyModifier;
> +    uint32_t first_type = GetGrabEventMatch(pFirstGrab);
> +    uint32_t second_type = GetGrabEventMatch(pSecondGrab);
>  
>      if (pFirstGrab->grabtype != pSecondGrab->grabtype)
>          return FALSE;
> @@ -288,8 +362,8 @@ GrabMatchesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab, Bool ignoreDevice)
>               (pFirstGrab->modifierDevice != pSecondGrab->modifierDevice)))
>              return FALSE;
>  
> -    if (pFirstGrab->type != pSecondGrab->type)
> -	return FALSE;
> +    if (first_type != second_type)
> +        return FALSE;
>  
>      if (GrabSupersedesSecond(pFirstGrab, pSecondGrab) ||
>  	GrabSupersedesSecond(pSecondGrab, pFirstGrab))
> diff --git a/dix/inpututils.c b/dix/inpututils.c
> index 2877804..13e8131 100644
> --- a/dix/inpututils.c
> +++ b/dix/inpututils.c
> @@ -548,3 +548,61 @@ CountBits(const uint8_t *mask, int len)
>  
>      return ret;
>  }
> +
> +int
> +FindTouchPoint(DeviceIntPtr dev, unsigned int touchid)
> +{
> +    int i;
> +    TouchClassPtr t = dev->touch;
> +
> +    if (!t)
> +        return -1;
> +
> +    for (i = 0; i < t->num_touches; i++)
> +        if (t->touches[i].active && t->touches[i].id == touchid)
> +            return i;
> +
> +    return -1;
> +}
> +
> +int
> +CreateTouchPoint(DeviceIntPtr dev, unsigned int touchid)
> +{
> +    int i;
> +    TouchClassPtr t = dev->touch;
> +
> +    if (!t)
> +        return -1;
> +
> +    if (FindTouchPoint(dev, touchid) >= 0)
> +        return -1;
> +
> +    for (i = 0; i < t->num_touches; i++)
> +        if (!t->touches[i].active) {
> +            t->touches[i].active = TRUE;
> +            t->touches[i].id = touchid;
> +            return i;
> +        }
> +
> +    return -1;
> +}
> +
> +void
> +FinishTouchPoint(DeviceIntPtr dev, unsigned int touchid)
> +{
> +    int touch = FindTouchPoint(dev, touchid);
> +    TouchPointInfoPtr ti;
> +
> +    if (touch < 0)
> +        return;
> +
> +    ti = &dev->touch->touches[touch];
> +
> +    ti->active = FALSE;
> +    ti->pending_finish = FALSE;
> +    ti->num_grabs = 0;
> +    ti->sprite.spriteTraceGood = 0;
> +    free(ti->listeners);
> +    ti->listeners = NULL;
> +    ti->num_listeners = 0;
> +}
> diff --git a/dix/window.c b/dix/window.c
> index d140dda..50d12a5 100644
> --- a/dix/window.c
> +++ b/dix/window.c
> @@ -110,6 +110,7 @@ Equipment Corporation.
>  #include "windowstr.h"
>  #include "input.h"
>  #include "inputstr.h"
> +#include "exevents.h"
>  #include "resource.h"
>  #include "colormapst.h"
>  #include "cursorstr.h"
> @@ -2869,8 +2870,10 @@ UnmapWindow(WindowPtr pWin, Bool fromConfigure)
>  	if (!fromConfigure && pScreen->PostValidateTree)
>  	    (*pScreen->PostValidateTree)(pLayerWin->parent, pWin, VTUnmap);
>      }
> -    if (wasRealized && !fromConfigure)
> +    if (wasRealized && !fromConfigure) {
>  	WindowsRestructured ();
> +	WindowGone(pWin);
> +    }
>      return Success;
>  }
>  
> @@ -2953,8 +2956,10 @@ UnmapSubwindows(WindowPtr pWin)
>  	if (anyMarked && pScreen->PostValidateTree)
>  	    (*pScreen->PostValidateTree)(pLayerWin->parent, pHead, VTUnmap);
>      }
> -    if (wasRealized)
> +    if (wasRealized) {
>  	WindowsRestructured ();
> +	WindowGone(pWin);
> +    }
>  }
>  
>  
> diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
> index b9006ab..ce84079 100644
> --- a/hw/xfree86/common/xf86Xinput.c
> +++ b/hw/xfree86/common/xf86Xinput.c
> @@ -1345,6 +1345,16 @@ xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
>  			   max_res, mode);
>  }
>  
> +void
> +xf86InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label,
> +                                int minval, int maxval, int resolution)
> +{
> +    if (!dev || !dev->touch)
> +        return;
> +
> +    InitTouchValuatorAxisStruct(dev, axnum, label, minval, maxval, resolution);
> +}
> +
>  /*
>   * Set the valuator values to be in synch with dix/event.c
>   * DefineInitialRootWindow().
> @@ -1396,4 +1406,15 @@ xf86EnableDevice(DeviceIntPtr dev)
>      EnableDevice(dev, TRUE);
>  }
>  
> +void
> +xf86PostTouchEvent(DeviceIntPtr dev, unsigned int touchid,
> +                   const ValuatorMask *mask, Bool end, uint32_t flags)
> +{
> +    int i, nevents;
> +
> +    nevents = GetTouchEvents(xf86Events, dev, touchid, mask, end, flags);
> +    for (i = 0; i < nevents; i++)
> +        mieqEnqueue(dev, (InternalEvent *)((xf86Events + i)->event));
> +}
> +
>  /* end of xf86Xinput.c */
> diff --git a/hw/xfree86/common/xf86Xinput.h b/hw/xfree86/common/xf86Xinput.h
> index 1b0b16f..cf2a4af 100644
> --- a/hw/xfree86/common/xf86Xinput.h
> +++ b/hw/xfree86/common/xf86Xinput.h
> @@ -141,6 +141,9 @@ extern _X_EXPORT void xf86PostKeyEventP(DeviceIntPtr device, unsigned int key_co
>  		       const int *valuators);
>  extern _X_EXPORT void xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code,
>                             int is_down);
> +extern _X_EXPORT void xf86PostTouchEvent(DeviceIntPtr dev, unsigned int touchid,
> +                                         const ValuatorMask *mask, Bool end,
> +                                         uint32_t flags);
>  extern _X_EXPORT InputInfoPtr xf86FirstLocalDevice(void);
>  extern _X_EXPORT int xf86ScaleAxis(int Cx, int to_max, int to_min, int from_max, int from_min);
>  extern _X_EXPORT void xf86XInputSetScreen(InputInfoPtr pInfo, int screen_number, int x, int y);
> @@ -148,6 +151,8 @@ extern _X_EXPORT void xf86ProcessCommonOptions(InputInfoPtr pInfo, pointer optio
>  extern _X_EXPORT void xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
>  				int maxval, int resolution, int min_res,
>  				int max_res, int mode);
> +extern _X_EXPORT void xf86InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label,
> +				int minval, int maxval, int resolution);
>  extern _X_EXPORT void xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum);
>  extern _X_EXPORT void xf86AddEnabledDevice(InputInfoPtr pInfo);
>  extern _X_EXPORT void xf86RemoveEnabledDevice(InputInfoPtr pInfo);
> diff --git a/include/eventstr.h b/include/eventstr.h
> index e1f5003..4d2ae68 100644
> --- a/include/eventstr.h
> +++ b/include/eventstr.h
> @@ -65,6 +65,9 @@ enum EventType {
>      ET_RawButtonRelease,
>      ET_RawMotion,
>      ET_XQuartz,
> +    ET_TouchBegin,
> +    ET_TouchEnd,
> +    ET_TouchMotion,
>      ET_Internal = 0xFF /* First byte */
>  };
>  
> @@ -90,6 +93,7 @@ struct _DeviceEvent
>      union {
>          uint32_t button;  /**< Button number */
>          uint32_t key;     /**< Key code */
> +        uint32_t touch;   /**< Touch ID */
>      } detail;
>      int16_t root_x;       /**< Pos relative to root window in integral data */
>      float root_x_frac;    /**< Pos relative to root window in frac part */
> diff --git a/include/exevents.h b/include/exevents.h
> index dc59430..32a3962 100644
> --- a/include/exevents.h
> +++ b/include/exevents.h
> @@ -51,6 +51,14 @@ extern _X_EXPORT void InitValuatorAxisStruct(
>  	int                    /* max_res */,
>  	int                    /* mode */);
>  
> +extern _X_EXPORT void InitTouchValuatorAxisStruct(
> +	DeviceIntPtr           /* dev */,
> +	int                    /* axnum */,
> +	Atom                   /* label */,
> +	int                    /* minval */,
> +	int                    /* maxval */,
> +	int                    /* resolution */);
> +
>  /* Input device properties */
>  extern _X_EXPORT void XIDeleteAllDeviceProperties(
>          DeviceIntPtr            /* device */
> @@ -199,6 +207,14 @@ GrabWindow(
>  	GrabMask*              /* eventMask */);
>  
>  extern int
> +GrabTouch(
> +	ClientPtr              /* client */,
> +	DeviceIntPtr           /* dev */,
> +	DeviceIntPtr           /* mod_dev */,
> +	GrabParameters*        /* param */,
> +	GrabMask*              /* eventMask */);
> +
> +extern int
>  SelectForWindow(
>  	DeviceIntPtr           /* dev */,
>  	WindowPtr              /* pWin */,
> @@ -222,6 +238,10 @@ InputClientGone(
>  	WindowPtr              /* pWin */,
>  	XID                    /* id */);
>  
> +extern void
> +WindowGone(
> +	WindowPtr              /* win */);
> +
>  extern int
>  SendEvent (
>  	ClientPtr              /* client */,
> diff --git a/include/input.h b/include/input.h
> index 56992d1..8728ae9 100644
> --- a/include/input.h
> +++ b/include/input.h
> @@ -314,6 +314,12 @@ extern _X_EXPORT Bool InitAbsoluteClassDeviceStruct(
>  extern _X_EXPORT Bool InitFocusClassDeviceStruct(
>      DeviceIntPtr /*device*/);
>  
> +extern _X_EXPORT Bool InitTouchClassDeviceStruct(
> +    DeviceIntPtr /*device*/,
> +    unsigned int /*max_touches*/,
> +    unsigned int /*mode*/,
> +    unsigned int /*numAxes*/);
> +
>  typedef void (*BellProcPtr)(
>      int /*percent*/,
>      DeviceIntPtr /*device*/,
> @@ -463,6 +469,14 @@ extern int GetKeyboardValuatorEvents(
>      int key_code,
>      const ValuatorMask *mask);
>  
> +extern int GetTouchEvents(
> +    EventListPtr events,
> +    DeviceIntPtr pDev,
> +    unsigned int touchid,
> +    const ValuatorMask *mask,
> +    Bool end,
> +    uint32_t flags);
> +
>  extern int GetProximityEvents(
>      EventListPtr events,
>      DeviceIntPtr pDev,
> @@ -524,6 +538,9 @@ extern DeviceIntPtr GetXTestDevice(DeviceIntPtr master);
>  extern void SendDevicePresenceEvent(int deviceid, int type);
>  extern _X_EXPORT InputAttributes *DuplicateInputAttributes(InputAttributes *attrs);
>  extern _X_EXPORT void FreeInputAttributes(InputAttributes *attrs);
> +extern int CreateTouchPoint(DeviceIntPtr dev, unsigned int touchid);
> +extern int FindTouchPoint(DeviceIntPtr dev, unsigned int touchid);
> +extern void FinishTouchPoint(DeviceIntPtr dev, unsigned int touchid);
>  
>  /* misc event helpers */
>  extern Mask GetEventMask(DeviceIntPtr dev, xEvent* ev, InputClientsPtr clients);
> diff --git a/include/inputstr.h b/include/inputstr.h
> index 7de7405..0385828 100644
> --- a/include/inputstr.h
> +++ b/include/inputstr.h
> @@ -49,6 +49,8 @@ SOFTWARE.
>  #ifndef INPUTSTRUCT_H
>  #define INPUTSTRUCT_H
>  
> +#include <X11/extensions/XI2proto.h>
> +
>  #include <pixman.h>
>  #include "input.h"
>  #include "window.h"
> @@ -71,7 +73,7 @@ extern _X_EXPORT int CountBits(const uint8_t *mask, int len);
>   * events to the protocol, the server will not support these events until
>   * this number here is bumped.
>   */
> -#define XI2LASTEVENT    17 /* XI_RawMotion */
> +#define XI2LASTEVENT    XI_TouchMotion
>  #define XI2MASKSIZE     ((XI2LASTEVENT + 7)/8) /* no of bits for masks */
>  
>  /**
> @@ -284,6 +286,38 @@ typedef struct _ValuatorClassRec {
>      ValuatorAccelerationRec	accelScheme;
>  } ValuatorClassRec, *ValuatorClassPtr;
>  
> +typedef struct _TouchPointInfo {
> +    Bool        active;             /* whether or not the touch is active */
> +    Bool        pending_finish;     /* true if the touch is physically inactive
> +                                     * but still owned by a grab */
> +    uint32_t    id;
> +    SpriteRec   sprite;             /* window trace for delivery */
> +    int         *valuators;         /* last-recorded valuators */
> +    XID         *listeners;         /* grabs/event selection IDs receiving
> +                                     * events for this touch */
> +    int         num_listeners;
> +    int         num_grabs;
> +    Bool        emulate_pointer;
> +} TouchPointInfoRec, *TouchPointInfoPtr;
> +
> +typedef struct _TouchAxisInfo {
> +    int		resolution;
> +    int		min_value;
> +    int		max_value;
> +    Atom	label;
> +} TouchAxisInfoRec, *TouchAxisInfoPtr;
> +
> +typedef struct _TouchClassRec {
> +    TouchAxisInfoPtr      axes;
> +    unsigned short        num_axes;
> +    TouchPointInfoPtr     touches;
> +    unsigned short        num_touches;
> +    CARD8                 mode;         /* ::XIDirectTouch, XIDependentTouch */
> +    unsigned int          last_touchid;
> +    int                   x_axis;       /* axis number */
> +    int                   y_axis;       /* axis number */
> +} TouchClassRec, *TouchClassPtr;
> +
>  typedef struct _ButtonClassRec {
>      int			sourceid;
>      CARD8		numButtons;
> @@ -388,6 +422,7 @@ typedef struct _LedFeedbackClassRec {
>  typedef struct _ClassesRec {
>      KeyClassPtr		key;
>      ValuatorClassPtr	valuator;
> +    TouchClassPtr	touch;
>      ButtonClassPtr	button;
>      FocusClassPtr	focus;
>      ProximityClassPtr	proximity;
> @@ -512,6 +547,7 @@ typedef struct _DeviceIntRec {
>      int			id;
>      KeyClassPtr		key;
>      ValuatorClassPtr	valuator;
> +    TouchClassPtr	touch;
>      ButtonClassPtr	button;
>      FocusClassPtr	focus;
>      ProximityClassPtr	proximity;
> diff --git a/include/protocol-versions.h b/include/protocol-versions.h
> index c8c7f5f..42b7d0e 100644
> --- a/include/protocol-versions.h
> +++ b/include/protocol-versions.h
> @@ -131,7 +131,7 @@
>  
>  /* X Input */
>  #define SERVER_XI_MAJOR_VERSION			2
> -#define SERVER_XI_MINOR_VERSION			0
> +#define SERVER_XI_MINOR_VERSION			1
>  
>  /* XKB */
>  #define SERVER_XKB_MAJOR_VERSION		1
> diff --git a/mi/mieq.c b/mi/mieq.c
> index 01da52a..42aec2c 100644
> --- a/mi/mieq.c
> +++ b/mi/mieq.c
> @@ -269,6 +269,9 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
>          case ET_ProximityOut:
>          case ET_Hierarchy:
>          case ET_DeviceChanged:
> +        case ET_TouchBegin:
> +        case ET_TouchEnd:
> +        case ET_TouchMotion:
>              event->device_event.deviceid = dev->id;
>              break;
>  #if XFreeXDGA



More information about the xorg-devel mailing list