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

Peter Hutterer peter.hutterer at who-t.net
Wed Jan 5 16:18:00 PST 2011


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

urgh, you need to start adding comments on what changed in respect to the
last patch.

reviewed bottum up this time to prevent the last hunks from not getting any
fresh-brain review, but it might also explain comments lacking context as
you go down this email :)

> ---
>  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;

shouldn't this check for ET_TouchEnd too?

> +
> +        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. */

a bit confusing imo, maybe "to reflect the device we're delivering the event
for".

> +        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",

typo "failed in DeliverTouchGrabEvents"

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

this seems like something that's useful in other functions as well.

> +        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 "

same typo here

> +                       "(%d)\n", sourcedev->name, err);
> +                return;
> +            }
> +            FixUpEventFromWindow(sprite, xi2, win, child, FALSE);
> +
> +            /* See comment in grab-delivery TouchBegin branch. */
> +            if (ev->type != ET_TouchBegin) {

\n before {

> +                for (j = 0; j < ti->num_listeners; j++)
> +                {
> +                    if (ti->listeners[j] == iclients->resource)
> +                        break;
> +                }
> +                if (j == ti->num_listeners)
> +                {
> +                    free(xi2);
> +                    continue;
> +                }
> +            }
> +
> +            if (XaceHook(XACE_RECEIVE_ACCESS, rClient(iclients), win,
> +                         (xEvent *) xi2, 1) != Success)
> +                continue;
> +            deliveries = TryClientEvents(rClient(iclients), deliverdev, xi2, 1,
> +                                         filter, filter, NullGrab);
> +            /* As only one event selection can receive events, bail as soon as
> +             * we find one. */
> +            if (ev->type == ET_TouchBegin && deliveries > 0)
> +            {
> +                ti->listeners[ti->num_listeners++] = iclients->resource;
> +                free(xi2);
> +                return;
> +            }
> +            else if (ev->type != ET_TouchBegin)
> +            {
> +                free(xi2);
> +                return;
> +            }
> +        }
> +    }
> +}
> +
> +/**
> + * Processes and delivers a TouchBegin, TouchMotion or a TouchEnd event.
> + *
> + * Due to having rather different delivery semantics (see the Xi 2.1 protocol
> + * spec for more information), this implements its own grab and event-selection
> + * delivery logic.
> + */
> +static void
> +ProcessTouchEvent(DeviceEvent *ev, DeviceIntPtr sourcedev)
> +{
> +    TouchClassPtr t;
> +    TouchPointInfoPtr ti;
> +    DeviceIntPtr masterdev = NULL;
> +    Window child;
> +    SpritePtr sprite;
> +    int touch;
> +
> +    /* We handle deliveries to MDs through the SD, rather than copying
> +     * the event and processing it twice. */
> +    if (IsMaster(sourcedev))
> +        return;

please expand this comment on the SD/MD handling. it's not specified in the
protocol IIRC so it's an implementation detail and the intended behaviour
should be documented somewhere. would make the sourcedev, deliverdev,
masterdev, etc. juggling easier to understand in DeliverTouch*.

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

this cries out for a macro with some nice, comforting, heart-warming name.

> +    DeliverTouchGrabEvents(sourcedev, ti, ev, child);
> +    DeliverTouchSelectionEvents(sourcedev, ti, ev, child);

I don't think using "Selection" is a good name choise. everywhere else we
either use "mask" or nothing (DeliverGrabbedEvent vs DeliverDeviceEvents).
"Selection" is imo too close to the copy/paste method and every time I read
it somewhere it throws me out for a second.

> +
> +    if (ev->type == ET_TouchEnd)
> +        FinishTouchPoint(sourcedev, ev->detail.touch);
> +}
> +
> +/**
>   * Main device event processing function.
>   * Called from when processing the events from the event queue.
>   *
> @@ -954,6 +1305,12 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
>      {
>          ProcessRawEvent(&ev->raw_event, device);
>          return;
> +    } else if (ev->any.type == ET_TouchBegin ||
> +               ev->any.type == ET_TouchMotion ||
> +               ev->any.type == ET_TouchEnd)
> +    {
> +        ProcessTouchEvent(&ev->device_event, device);
> +        return;
>      }
>  
>      if (IsPointerDevice(device))
> @@ -1152,6 +1509,30 @@ InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int
>          dev->proximity->in_proximity = FALSE;
>  }
>  
> +void
> +InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
> +                            int maxval, int resolution)
> +{
> +    TouchAxisInfoPtr ax;
> +
> +    if (!dev || !dev->touch || minval > maxval)
> +        return;
> +    if (axnum >= dev->touch->num_axes)
> +        return;
> +
> +    ax = dev->touch->axes + axnum;
> +
> +    ax->min_value = minval;
> +    ax->max_value = maxval;
> +    ax->resolution = resolution;
> +    ax->label = label;
> +
> +    if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X))
> +        dev->touch->x_axis = axnum;
> +    else if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y))
> +        dev->touch->y_axis = axnum;
> +}
> +
>  static void
>  FixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
>  		     ButtonClassPtr b, ValuatorClassPtr v, int first)
> @@ -1562,6 +1943,34 @@ GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
>      return AddPassiveGrabToList(client, grab);
>  }
>  
> +/* Touch grab */
> +int
> +GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev,
> +          GrabParameters *param, GrabMask *mask)
> +{
> +    WindowPtr pWin;
> +    GrabPtr grab;
> +    int rc;
> +
> +    rc = CheckGrabValues(client, param);
> +    if (rc != Success)
> +        return rc;
> +
> +    rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
> +    if (rc != Success)
> +	return rc;
> +    rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, DixGrabAccess);
> +    if (rc != Success)
> +	return rc;
> +
> +    grab = CreateGrab(client->index, dev, mod_dev, pWin, GRABTYPE_XI2,
> +                      mask, param, XI_TouchBegin, 0, NullWindow, NullCursor);
> +    if (!grab)
> +        return BadAlloc;
> +
> +    return AddPassiveGrabToList(client, grab);
> +}
> +
>  int
>  SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
>  		Mask mask, Mask exclusivemasks)
> @@ -1734,6 +2143,36 @@ InputClientGone(WindowPtr pWin, XID id)
>      FatalError("client not on device event list");
>  }
>  
> +/**
> + * Search for window in each touch trace for each device. Remove the window
> + * and all its subwindows from the trace when found. The initial window
> + * order is preserved.
> + */
> +void WindowGone(WindowPtr win)
> +{
> +    DeviceIntPtr dev;
> +
> +    for (dev = inputInfo.devices; dev; dev = dev->next) {
> +        TouchClassPtr t = dev->touch;
> +        int i;
> +
> +        if (!t)
> +            continue;
> +
> +        for (i = 0; i < t->num_touches; i++) {
> +            SpritePtr sprite = &t->touches[i].sprite;
> +            int j;
> +
> +            for (j = 0; j < sprite->spriteTraceGood; j++) {
> +                if (sprite->spriteTrace[j] == win) {
> +                    sprite->spriteTraceGood = j;
> +                    break;
> +                }
> +            }
> +        }
> +    }
> +}
> +
>  int
>  SendEvent(ClientPtr client, DeviceIntPtr d, Window dest, Bool propagate,
>  	  xEvent * ev, Mask mask, int count)
> diff --git a/Xi/extinit.c b/Xi/extinit.c
> index 82df7eb..8adec8f 100644
> --- a/Xi/extinit.c
> +++ b/Xi/extinit.c
> @@ -258,7 +258,8 @@ static int (*ProcIVector[])(ClientPtr) = {
>          ProcXIChangeProperty,                   /* 57 */
>          ProcXIDeleteProperty,                   /* 58 */
>          ProcXIGetProperty,                      /* 59 */
> -        ProcXIGetSelectedEvents                 /* 60 */
> +        ProcXIGetSelectedEvents,                /* 60 */
> +        ProcXIAllowTouchEvents,                 /* 61 */
>  };
>  
>  /* For swapped clients */
> @@ -323,7 +324,8 @@ static int (*SProcIVector[])(ClientPtr) = {
>          SProcXIChangeProperty,                   /* 57 */
>          SProcXIDeleteProperty,                   /* 58 */
>          SProcXIGetProperty,                      /* 59 */
> -        SProcXIGetSelectedEvents                 /* 60 */
> +        SProcXIGetSelectedEvents,                /* 60 */
> +        SProcXIAllowTouchEvents,                 /* 61 */
>  };
>  
>  /*****************************************************************
> @@ -881,6 +883,9 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
>          case XI_KeyRelease:
>          case XI_ButtonPress:
>          case XI_ButtonRelease:
> +        case XI_TouchBegin:
> +        case XI_TouchMotion:
> +        case XI_TouchEnd:
>              SDeviceEvent((xXIDeviceEvent*)from, (xXIDeviceEvent*)to);
>              break;
>          case XI_RawMotion:
> diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c
> index 3077e1a..93905fd 100644
> --- a/Xi/xiallowev.c
> +++ b/Xi/xiallowev.c
> @@ -35,6 +35,8 @@
>  
>  #include "inputstr.h"	/* DeviceIntPtr      */
>  #include "windowstr.h"	/* window structure  */
> +#include "eventstr.h"
> +#include "mi.h"
>  #include <X11/extensions/XI2.h>
>  #include <X11/extensions/XI2proto.h>
>  
> @@ -101,3 +103,110 @@ ProcXIAllowEvents(ClientPtr client)
>      return ret;
>  }
>  
> +int
> +SProcXIAllowTouchEvents(ClientPtr client)
> +{
> +    char n;
> +
> +    REQUEST(xXIAllowTouchEventsReq);
> +
> +    swaps(&stuff->length, n);
> +    swaps(&stuff->deviceid, n);
> +    swapl(&stuff->touchid, n);
> +
> +    return ProcXIAllowTouchEvents(client);
> +}
> +
> +int
> +ProcXIAllowTouchEvents(ClientPtr client)
> +{
> +    DeviceIntPtr dev;
> +    TouchPointInfoPtr ti;
> +    int ret, touch, i, nev;
> +    uint32_t flags = 0;
> +    ValuatorMask *mask = valuator_mask_new(0);
> +    EventList *events = InitEventList(GetMaximumEventsNum());
> +    DeviceEvent *ev;
> +
> +    REQUEST(xXIAllowTouchEventsReq);
> +    REQUEST_SIZE_MATCH(xXIAllowTouchEventsReq);
> +
> +    if (!mask || !events)
> +        return BadAlloc;
> +
> +    ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
> +    if (ret != Success)
> +        return ret;
> +    if (!dev->touch)
> +    {
> +        client->errorValue = stuff->deviceid;
> +        return BadDevice;
> +    }
> +
> +    touch = FindTouchPoint(dev, stuff->touchid);
> +    if (touch < 0)
> +    {
> +        client->errorValue = touch;
> +        return BadValue;
> +    }
> +    ti = &dev->touch->touches[touch];
> +
> +    if (ti->num_listeners == 0 ||
> +        CLIENT_ID(ti->listeners[0]) != client->index)
> +        return BadAccess;
> +
> +    if (stuff->mode & XITouchOwnerAccept)
> +    {
> +        ProcessInputEvents();
> +
> +        if (stuff->mode & ~(XITouchOwnerAccept | XITouchNoPointerEmulation))
> +        {
> +            client->errorValue = stuff->mode;
> +            return BadValue;
> +        }
> +
> +        if (stuff->mode & XITouchNoPointerEmulation)
> +            ti->emulate_pointer = FALSE;
> +
> +        flags |= XITouchOwnerAccepted;
> +        nev = GetTouchEvents(events, dev, stuff->touchid, mask, 0, flags);

FALSE instead of 0 please

> +        if (nev == 0)
> +            return BadAlloc;
> +        for (i = 0; i < nev; i++)
> +        {
> +            ev = (DeviceEvent *)((events + i)->event);
> +            mieqProcessDeviceEvent(dev, (InternalEvent *) ev, NULL);
> +        }

this could be interesting. you're injecting into the event stream here
directly, so there's a race condition where you end up with touch motion
events after sending a touch end event. note that when the request arrives,
a touch motion event may still be in the EQ, so we need some handling in
ProcessTouchEvent to discard those that are XITouchOwnerAccepted when they
go through the processing path.

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


indentation


>      {
>          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;

return touch->length * 4 for consistency with the other List*Info.

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

return val->length * 4 for consistency with the other List*Info.

> +}
> +
> +static void
> +SwapTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val)
> +{
> +    char n;
> +    swaps(&val->type, n);
> +    swaps(&val->length, n);
> +    swaps(&val->sourceid, n);
> +    swaps(&val->number, n);
> +    swapl(&val->label, n);
> +    swapl(&val->min.integral, n);
> +    swapl(&val->min.frac, n);
> +    swapl(&val->max.integral, n);
> +    swapl(&val->max.frac, n);
> +    swapl(&val->resolution, n);
> +}
> +
>  int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
>  {
>      DeviceIntPtr master = dev->u.master;
> @@ -462,6 +535,22 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
>          total_len += len;
>      }
>  
> +    if (dev->touch)
> +    {
> +        (*nclasses)++;
> +        len = ListTouchInfo(dev, (xXITouchInfo*)any);
> +        any += len;
> +        total_len += len;
> +
> +        for (i = 0; i < dev->touch->num_axes; i++)
> +        {
> +            (*nclasses)++;
> +            len = ListTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any, i);
> +            any += len;
> +            total_len += len;
> +        }
> +    }
> +
>      return total_len;
>  }
>  
> @@ -489,6 +578,12 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
>              case XIValuatorClass:
>                  SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
>                  break;
> +            case XITouchClass:
> +                SwapTouchInfo(dev, (xXITouchInfo*)any);
> +                break;
> +            case XITouchValuatorClass:
> +                SwapTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any);
> +                break;
>          }
>  
>          any += len * 4;
> diff --git a/Xi/xiquerydevice.h b/Xi/xiquerydevice.h
> index 02f0659..59326f7 100644
> --- a/Xi/xiquerydevice.h
> +++ b/Xi/xiquerydevice.h
> @@ -44,4 +44,7 @@ int ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info, Bool reportState);
>  int ListKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info);
>  int ListValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info,
>  		     int axisnumber, Bool reportState);
> +int ListTouchInfo(DeviceIntPtr dev, xXITouchInfo* info);
> +int ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
> +                          int axisnumber);
>  #endif /* QUERYDEV_H */
> diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
> index 22fbaf5..72ca794 100644
> --- a/Xi/xiselectev.c
> +++ b/Xi/xiselectev.c
> @@ -152,6 +152,47 @@ ProcXISelectEvents(ClientPtr client)
>              }
>          }
>  
> +        if (evmask->mask_len >= 1)
> +        {
> +            unsigned char *bits = (unsigned char*)&evmask[1];
> +
> +            /* All three touch events must be selected at once */
> +            if ((BitIsOn(bits, XI_TouchBegin) ||
> +                 BitIsOn(bits, XI_TouchMotion) ||
> +                 BitIsOn(bits, XI_TouchEnd)) &&
> +                (!BitIsOn(bits, XI_TouchBegin) ||
> +                 !BitIsOn(bits, XI_TouchMotion) ||
> +                 !BitIsOn(bits, XI_TouchEnd)))
> +            {
> +                client->errorValue = XI_TouchBegin;
> +                return BadValue;
> +            }
> +
> +            /* Only one client per window may select for touch events on the
> +             * same devices, including master devices.
> +             * XXX: This breaks if a device goes from floating to attached. */
> +            if (BitIsOn(bits, XI_TouchBegin))
> +            {
> +                OtherInputMasks *inputMasks = wOtherInputMasks(win);
> +                InputClients *iclient = NULL;
> +                if (inputMasks)
> +                    iclient = inputMasks->inputClients;
> +                for (; iclient; iclient = iclient->next)
> +                {
> +                    if (CLIENT_ID(iclient->resource) == client->index)
> +                        continue;
> +                    if (BitIsOn(iclient->xi2mask[evmask->deviceid],
> +                                XI_TouchBegin) ||
> +                        BitIsOn(iclient->xi2mask[XIAllDevices],
> +                                XI_TouchBegin) ||
> +                        (dev && (IsMaster(dev) || dev->u.master) &&
> +                         BitIsOn(iclient->xi2mask[XIAllMasterDevices],
> +                                 XI_TouchBegin)))
> +                        return BadAccess;
> +                }
> +            }
> +        }
> +

please split this out into a helper function. it's self-contained anyway.

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

can we make this distinct pointers instead? i hate this approach when it's
not on the wire. especially since a few lines later the sprite trace on the
touch sprites is malloced anywya, so now you have a nice mix malloc and
non-malloc that's not really documented anywhere.

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

i suppose a new flag define for pointer emulation would be useful, even if
it doesn't do anything yet. this way, we don't have to break the abi again
when we know what to do with pointer emulation.

> +{
> +    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. */

change the xf86 api to xf86CreateTouchEvent() for the first (returning the
touchid) and then xf86PostTouchEvent() for subsequent calls.


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

if pointer emulation is enabled, this needs to call moveAbsolute and
positionSprite, aside from calling GPE somewhere too. please add a fixme so
that at least we remember that.

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

this can't be right. either the break is out of place here or the while
should be an if(). a memmove should do here, though I suppose the compiler
will do the right thing either way.

> +                ti->num_listeners--;
> +                ti->num_grabs--;
> +
> +                ProcessInputEvents();
> +                nev = GetTouchEvents(events, dev, ti->id, mask, 0, 0);

parameter 5 should be FALSE, not 0.

> +                for (j = 0; j < nev; j++)
> +                {
> +                    ev = (InternalEvent *)((events + j)->event);
> +                    mieqProcessDeviceEvent(dev, ev, NULL);
> +                }
> +
> +                break;
> +            }
> +        }
> +    }
> +}
> +
>  static void
>  FreeGrab(GrabPtr pGrab)
>  {
> +    if (pGrab->grabtype == GRABTYPE_XI2 && pGrab->type == XI_TouchBegin)
> +        FreeTouchGrab(pGrab);
> +
>      free(pGrab->modifiersDetail.pMask);
>      free(pGrab->detail.pMask);
>  
> @@ -243,6 +298,23 @@ GrabSupersedesSecond(GrabPtr pFirstGrab, GrabPtr pSecondGrab)
>  }
>  
>  /**
> + * Returns the event type to match when comparing grabs.
> + */
> +static uint32_t

pGrab->type is CARD8 and XI_TouchBegin must be uint16 (XGE provides for has
16 bit event types), so the uint32_t seems to provide size precision where
none is needed. a simple int would do, I think.

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

same here with uint32_t.

>      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)

why not return a TouchInfoRec*/NULL here instead of the index? afaict, the
return value is only used to index into the array later, so we might as well
return the pointer directly. same goes for CreateTouchPoint.

> +{
> +    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;

I admit I got a bit lost here, but - don't we need to reset the valuators
somewhere here as well?

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

if this is for the XI_TouchMotion, it should include XI.h instead.

> +
>  #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 */

how big is valuators?
wouldn't hurt to have a nvaluators field here, or just use a valuator mask?
right now, if I just get passed a TPI I wouldn't know the size of the array.


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

s/tab/spaces please

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

axis number?? is this to hold the axis index for the x/y axis?
why not force the implementation to have x/y on 0/1, that's what we do for
pointer events too.

I don't see anything fundamentally wrong with the patch and I think my
comments are more on style and general cleanups. I guess there will still be
a bit of churn on the patch though. and I haven't actually tested it yet
either.

Cheers,
  Peter

> +} TouchClassRec, *TouchClassPtr;
> +
>  typedef struct _ButtonClassRec {
>      int			sourceid;
>      CARD8		numButtons;
> @@ -388,6 +422,7 @@ typedef struct _LedFeedbackClassRec {
>  typedef struct _ClassesRec {
>      KeyClassPtr		key;
>      ValuatorClassPtr	valuator;
> +    TouchClassPtr	touch;
>      ButtonClassPtr	button;
>      FocusClassPtr	focus;
>      ProximityClassPtr	proximity;
> @@ -512,6 +547,7 @@ typedef struct _DeviceIntRec {
>      int			id;
>      KeyClassPtr		key;
>      ValuatorClassPtr	valuator;
> +    TouchClassPtr	touch;
>      ButtonClassPtr	button;
>      FocusClassPtr	focus;
>      ProximityClassPtr	proximity;
> diff --git a/include/protocol-versions.h b/include/protocol-versions.h
> index c8c7f5f..42b7d0e 100644
> --- a/include/protocol-versions.h
> +++ b/include/protocol-versions.h
> @@ -131,7 +131,7 @@
>  
>  /* X Input */
>  #define SERVER_XI_MAJOR_VERSION			2
> -#define SERVER_XI_MINOR_VERSION			0
> +#define SERVER_XI_MINOR_VERSION			1
>  
>  /* XKB */
>  #define SERVER_XKB_MAJOR_VERSION		1
> diff --git a/mi/mieq.c b/mi/mieq.c
> index 01da52a..42aec2c 100644
> --- a/mi/mieq.c
> +++ b/mi/mieq.c
> @@ -269,6 +269,9 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
>          case ET_ProximityOut:
>          case ET_Hierarchy:
>          case ET_DeviceChanged:
> +        case ET_TouchBegin:
> +        case ET_TouchEnd:
> +        case ET_TouchMotion:
>              event->device_event.deviceid = dev->id;
>              break;
>  #if XFreeXDGA
> -- 
> 1.7.2.3
> 


More information about the xorg-devel mailing list