[PATCH v2 31/42] Process and deliver touch events

Peter Hutterer peter.hutterer at who-t.net
Tue Dec 20 17:29:05 PST 2011


On Tue, Dec 20, 2011 at 04:32:52PM -0800, Chase Douglas wrote:
> From: Peter Hutterer <peter.hutterer at who-t.net>
> 
> Does not include pointer emulation handling.
> Does include partial ownership handling but not the actual processing of
> ownership events.
> 
> Note: this commit is a retroactive commit extracted from a series of ~50
> commits and may thus appear a bit more complicated than what you'd write out
> from scratch.
> 
> Pointer processing tree is roughly:
> - ProcessOtherEvents
>   - ProcessTouchEvents
>     - DeliverTouchEvents
>       - DeliverTouchBeginEvent|DeliverTouchEndEvent|...
>         - DeliverOneTouchEvent
> 
> Also hooks up the event history playing to the right function now.
> 
> Co-authored-by: Daniel Stone <daniel at fooishbar.org>
> Co-authored-by: Chase Douglas <chase.douglas at canonical.com>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
> Reviewed-by: Chase Douglas <chase.douglas at canonical.com>
> ---
> Sent on behalf of Peter to save time.
> 
> Changes to v1:
> - Add comment for RetrieveTouchDeliveryData

applied, thanks

Cheers,
  Peter
> 
>  Xi/exevents.c |  401 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  dix/touch.c   |    6 +-
>  include/dix.h |    6 +
>  3 files changed, 411 insertions(+), 2 deletions(-)
> 
> diff --git a/Xi/exevents.c b/Xi/exevents.c
> index 5cf60f8..b5fc826 100644
> --- a/Xi/exevents.c
> +++ b/Xi/exevents.c
> @@ -104,6 +104,7 @@ SOFTWARE.
>  #include "eventconvert.h"
>  #include "eventstr.h"
>  #include "inpututils.h"
> +#include "mi.h"
>  
>  #include <X11/extensions/XKBproto.h>
>  #include "xkbsrv.h"
> @@ -1013,6 +1014,258 @@ UpdateDeviceState(DeviceIntPtr device, DeviceEvent* event)
>      return DEFAULT;
>  }
>  
> +/**
> + * A client that does not have the TouchOwnership mask set may not receive a
> + * TouchBegin event if there is at least one grab active.
> + *
> + * @return TRUE if the client selected for ownership events on the given
> + * window for this device, FALSE otherwise
> + */
> +static inline Bool
> +TouchClientWantsOwnershipEvents(ClientPtr client, DeviceIntPtr dev, WindowPtr win)
> +{
> +    InputClients *iclient;
> +
> +    nt_list_for_each_entry(iclient, wOtherInputMasks(win)->inputClients, next)
> +    {
> +        if (rClient(iclient) != client)
> +            continue;
> +
> +        return xi2mask_isset(iclient->xi2mask, dev, XI_TouchOwnership);
> +    }
> +
> +    return FALSE;
> +}
> +
> +static void
> +TouchSendOwnershipEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, int reason, XID resource)
> +{
> +    int nev, i;
> +    InternalEvent *tel = InitEventList(GetMaximumEventsNum());
> +
> +    nev = GetTouchOwnershipEvents(tel, dev, ti, reason, resource, 0);
> +    for (i = 0; i < nev; i++)
> +        mieqProcessDeviceEvent(dev, tel + i, NULL);
> +
> +    FreeEventList(tel, GetMaximumEventsNum());
> +}
> +
> +/**
> + * Attempts to deliver a touch event to the given client.
> + */
> +static Bool
> +DeliverOneTouchEvent(ClientPtr client, DeviceIntPtr dev, TouchPointInfoPtr ti,
> +                     GrabPtr grab, WindowPtr win, InternalEvent *ev)
> +{
> +    int err;
> +    xEvent *xi2;
> +    Mask filter;
> +    Window child = DeepestSpriteWin(&ti->sprite)->drawable.id;
> +
> +    /* FIXME: owner event handling */
> +
> +    /* If the client does not have the ownership mask set and is not
> +     * the current owner of the touch, only pretend we delivered */
> +    if (!grab && ti->num_grabs != 0 &&
> +           !TouchClientWantsOwnershipEvents(client, dev,win))
> +           return TRUE;
> +
> +    /* If we fail here, we're going to leave a client hanging. */
> +    err = EventToXI2(ev, &xi2);
> +    if (err != Success)
> +        FatalError("[Xi] %s: XI2 conversion failed in %s"
> +                   " (%d)\n", dev->name, __func__, err);
> +
> +    FixUpEventFromWindow(&ti->sprite, xi2, win, child, FALSE);
> +    filter = GetEventFilter(dev, xi2);
> +    if (XaceHook(XACE_RECEIVE_ACCESS, client, win, xi2, 1) != Success)
> +        return FALSE;
> +    err = TryClientEvents(client, dev, xi2, 1, filter, filter, NullGrab);
> +    free(xi2);
> +
> +    /* Returning the value from TryClientEvents isn't useful, since all our
> +     * resource-gone cleanups will update the delivery list anyway. */
> +    return TRUE;
> +}
> +
> +/**
> + * Copy the event's valuator information into the touchpoint, we may need
> + * this for emulated TouchEnd events.
> + */
> +static void
> +TouchCopyValuatorData(DeviceEvent *ev, TouchPointInfoPtr ti)
> +{
> +    int i;
> +    for (i = 0; i < sizeof(ev->valuators.mask) * 8; i++)
> +        if (BitIsOn(ev->valuators.mask, i))
> +            valuator_mask_set_double(ti->valuators, i, ev->valuators.data[i]);
> +}
> +
> +/**
> + * Given a touch event and a potential listener, retrieve info needed for
> + * processing the event.
> + *
> + * @param dev The device generating the touch event.
> + * @param ti The touch point info record for the touch event.
> + * @param ev The touch event to process.
> + * @param listener The touch event listener that may receive the touch event.
> + * @param[out] client The client that should receive the touch event.
> + * @param[out] win The window to deliver the event on.
> + * @param[out] grab The grab to deliver the event through, if any.
> + * @param[out] mask The XI 2.x event mask of the grab or selection, if any.
> + * @return TRUE if an event should be delivered to the listener, FALSE
> + *         otherwise.
> + */
> +static Bool
> +RetrieveTouchDeliveryData(DeviceIntPtr dev, TouchPointInfoPtr ti,
> +                          InternalEvent *ev, TouchListener *listener,
> +                          ClientPtr *client, WindowPtr *win, GrabPtr *grab,
> +                          XI2Mask **mask)
> +{
> +     int rc;
> +     InputClients *iclients = NULL;
> +
> +    if (listener->type == LISTENER_GRAB ||
> +        listener->type == LISTENER_POINTER_GRAB)
> +    {
> +        rc = dixLookupResourceByType((pointer*)grab, listener->listener,
> +                RT_PASSIVEGRAB,
> +                serverClient, DixSendAccess);
> +        if (rc != Success)
> +        {
> +            /* the grab doesn't exist but we have a grabbing listener - this
> +             * is an implicit/active grab */
> +            rc = dixLookupClient(client, listener->listener, serverClient, DixSendAccess);
> +            if (rc != Success)
> +                return FALSE;
> +
> +            *grab = dev->deviceGrab.grab;
> +            if (!*grab)
> +                return FALSE;
> +        }
> +
> +        *client = rClient(*grab);
> +        *win = (*grab)->window;
> +        *mask = (*grab)->xi2mask;
> +    } else {
> +        if (listener->level == CORE)
> +            rc = dixLookupWindow(win, listener->listener,
> +                                 serverClient, DixSendAccess);
> +        else
> +            rc = dixLookupResourceByType((pointer*)win, listener->listener,
> +                                         RT_INPUTCLIENT,
> +                                         serverClient, DixSendAccess);
> +        if (rc != Success)
> +            return FALSE;
> +
> +
> +        if (listener->level == XI2)
> +        {
> +            int evtype;
> +            if (ti->emulate_pointer && listener->type == LISTENER_POINTER_REGULAR)
> +                evtype = GetXI2Type(TouchGetPointerEventType(ev));
> +            else
> +                evtype = GetXI2Type(ev->any.type);
> +
> +            nt_list_for_each_entry(iclients, wOtherInputMasks(*win)->inputClients, next)
> +                if (xi2mask_isset(iclients->xi2mask, dev, evtype))
> +                    break;
> +            BUG_WARN(!iclients);
> +            if (!iclients)
> +                return FALSE;
> +        } else if (listener->level == XI)
> +        {
> +            int xi_type = GetXIType(TouchGetPointerEventType(ev));
> +            Mask xi_filter = event_get_filter_from_type(dev, xi_type);
> +            nt_list_for_each_entry(iclients, wOtherInputMasks(*win)->inputClients, next)
> +                if (iclients->mask[dev->id] & xi_filter)
> +                    break;
> +            BUG_WARN(!iclients);
> +            if (!iclients)
> +                return FALSE;
> +        } else
> +        {
> +            int coretype = GetCoreType(TouchGetPointerEventType(ev));
> +            Mask core_filter = event_get_filter_from_type(dev, coretype);
> +
> +            /* all others */
> +            nt_list_for_each_entry(iclients, (InputClients*)wOtherClients(*win), next)
> +                if (iclients->mask[XIAllDevices] & core_filter)
> +                    break;
> +            /* if owner selected, iclients is NULL */
> +        }
> +
> +        *client = iclients ? rClient(iclients) : wClient(*win);
> +        *mask = iclients ? iclients->xi2mask : NULL;
> +        *grab = NULL;
> +    }
> +
> +    return TRUE;
> +}
> +
> +/**
> + * Processes and delivers a TouchBegin, TouchUpdate, or a
> + * TouchEnd event.
> + *
> + * Due to having rather different delivery semantics (see the Xi 2.2 protocol
> + * spec for more information), this implements its own grab and event-selection
> + * delivery logic.
> + */
> +static void
> +ProcessTouchEvent(InternalEvent *ev, DeviceIntPtr dev)
> +{
> +    TouchClassPtr t = dev->touch;
> +    TouchPointInfoPtr ti;
> +    uint32_t touchid;
> +    int type = ev->any.type;
> +    int emulate_pointer = !!(ev->device_event.flags & TOUCH_POINTER_EMULATED);
> +
> +    if (!t)
> +        return;
> +
> +    if (ev->any.type == ET_TouchOwnership)
> +        touchid = ev->touch_ownership_event.touchid;
> +    else
> +        touchid = ev->device_event.touchid;
> +
> +    if (type == ET_TouchBegin) {
> +        ti = TouchBeginTouch(dev, ev->device_event.sourceid, touchid,
> +                             emulate_pointer);
> +    } else
> +        ti = TouchFindByClientID(dev, touchid);
> +
> +    if (!ti)
> +    {
> +        DebugF("[Xi] %s: Failed to get event %d for touchpoint %d\n",
> +               dev->name, type, touchid);
> +        return;
> +    }
> +
> +    if (emulate_pointer && IsMaster(dev))
> +        CheckMotion(&ev->device_event, dev);
> +
> +    /* Make sure we have a valid window trace for event delivery; must be
> +     * called after event type mutation. */
> +    /* FIXME: check this */
> +    if (!TouchEnsureSprite(dev, ti, ev))
> +        return;
> +
> +    /* TouchOwnership events are handled separately from the rest, as they
> +     * have more complex semantics. */
> +    if (ev->any.type == ET_TouchOwnership)
> +        /* FIXME: process me! */;
> +    else
> +    {
> +        TouchCopyValuatorData(&ev->device_event, ti);
> +        /* WARNING: the event type may change to TouchUpdate in
> +         * DeliverTouchEvents if a TouchEnd was delivered to a grabbing
> +         * owner */
> +        DeliverTouchEvents(dev, ti, (InternalEvent *) ev, 0);
> +        if (ev->any.type == ET_TouchEnd)
> +            TouchEndTouch(dev, ti);
> +    }
> +}
> +
>  
>  /**
>   * Process DeviceEvents and DeviceChangedEvents.
> @@ -1162,12 +1415,160 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
>          case  ET_RawTouchEnd:
>              DeliverRawEvent(&ev->raw_event, device);
>              break;
> +        case  ET_TouchBegin:
> +        case  ET_TouchUpdate:
> +        case  ET_TouchOwnership:
> +        case  ET_TouchEnd:
> +            ProcessTouchEvent(ev, device);
> +            break;
>          default:
>              ProcessDeviceEvent(ev, device);
>              break;
>      }
>  }
>  
> +static int
> +DeliverTouchBeginEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev,
> +                       TouchListener *listener, ClientPtr client,
> +                       WindowPtr win, GrabPtr grab, XI2Mask *xi2mask)
> +{
> +    enum TouchListenerState state;
> +    int rc = Success;
> +    Bool has_ownershipmask;
> +
> +    has_ownershipmask = xi2mask_isset(xi2mask, dev, XI_TouchOwnership);
> +
> +    if (TouchResourceIsOwner(ti, listener->listener) || has_ownershipmask)
> +        rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
> +    if (!TouchResourceIsOwner(ti, listener->listener))
> +    {
> +        if (has_ownershipmask)
> +            state = LISTENER_AWAITING_OWNER;
> +        else
> +            state = LISTENER_AWAITING_BEGIN;
> +    } else
> +    {
> +        if (has_ownershipmask)
> +            TouchSendOwnershipEvent(dev, ti, 0, listener->listener);
> +        state = LISTENER_IS_OWNER;
> +    }
> +    listener->state = state;
> +
> +    return rc;
> +}
> +
> +static int
> +DeliverTouchEndEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev,
> +                     TouchListener *listener, ClientPtr client,
> +                     WindowPtr win, GrabPtr grab, XI2Mask *xi2mask)
> +{
> +    int rc = Success;
> +
> +    /* Event in response to reject */
> +    if (ev->device_event.flags & TOUCH_REJECT)
> +    {
> +        if (listener->state != LISTENER_HAS_END)
> +            rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
> +        listener->state = LISTENER_HAS_END;
> +    } else if (TouchResourceIsOwner(ti, listener->listener))
> +    {
> +        /* FIXME: what about early acceptance */
> +        if (!(ev->device_event.flags & TOUCH_ACCEPT))
> +        {
> +            if (listener->state != LISTENER_HAS_END)
> +                rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
> +            listener->state = LISTENER_HAS_END;
> +        }
> +        if (ti->num_listeners > 1 &&
> +           (ev->device_event.flags & (TOUCH_ACCEPT|TOUCH_REJECT)) == 0)
> +        {
> +            ev->any.type = ET_TouchUpdate;
> +            ev->device_event.flags |= TOUCH_PENDING_END;
> +            ti->pending_finish = TRUE;
> +        }
> +    }
> +
> +    return rc;
> +}
> +
> +static int
> +DeliverTouchEvent(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev,
> +                  TouchListener *listener, ClientPtr client,
> +                  WindowPtr win, GrabPtr grab, XI2Mask *xi2mask)
> +{
> +    Bool has_ownershipmask = FALSE;
> +    int rc = Success;
> +
> +    if (xi2mask)
> +        has_ownershipmask = xi2mask_isset(xi2mask, dev, XI_TouchOwnership);
> +
> +    if (ev->any.type == ET_TouchOwnership)
> +    {
> +        ev->touch_ownership_event.deviceid = dev->id;
> +        if (!TouchResourceIsOwner(ti, listener->listener))
> +            goto out;
> +        rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
> +        listener->state = LISTENER_IS_OWNER;
> +    } else
> +        ev->device_event.deviceid = dev->id;
> +
> +    if (ev->any.type == ET_TouchBegin)
> +    {
> +        rc = DeliverTouchBeginEvent(dev, ti, ev, listener, client, win, grab, xi2mask);
> +    } else if (ev->any.type == ET_TouchUpdate)
> +    {
> +        if (TouchResourceIsOwner(ti, listener->listener) || has_ownershipmask)
> +            rc = DeliverOneTouchEvent(client, dev, ti, grab, win, ev);
> +    } else if (ev->any.type == ET_TouchEnd)
> +        rc = DeliverTouchEndEvent(dev, ti, ev, listener, client, win, grab, xi2mask);
> +
> +out:
> +    return rc;
> +}
> +
> +/**
> + * Delivers a touch events to all interested clients.  For TouchBegin events,
> + * will update ti->listeners, ti->num_listeners, and ti->num_grabs.
> + * May also mutate ev (type and flags) upon successful delivery.  If
> + * @resource is non-zero, will only attempt delivery to the owner of that
> + * resource.
> + *
> + * @return TRUE if the event was delivered at least once, FALSE otherwise
> + */
> +void
> +DeliverTouchEvents(DeviceIntPtr dev, TouchPointInfoPtr ti,
> +                   InternalEvent *ev, XID resource)
> +{
> +    int i;
> +
> +    if (ev->any.type == ET_TouchBegin &&
> +        !(ev->device_event.flags & (TOUCH_CLIENT_ID|TOUCH_REPLAYING)))
> +        TouchSetupListeners(dev, ti, ev);
> +
> +    TouchEventHistoryPush(ti, &ev->device_event);
> +
> +    for (i = 0; i < ti->num_listeners; i++)
> +    {
> +        GrabPtr grab = NULL;
> +        ClientPtr client;
> +        WindowPtr win;
> +        XI2Mask *mask;
> +        TouchListener *listener = &ti->listeners[i];
> +
> +        if (resource && listener->listener != resource)
> +            continue;
> +
> +        if (!RetrieveTouchDeliveryData(dev, ti, ev, listener, &client, &win,
> +                                       &grab, &mask))
> +            continue;
> +
> +        DeliverTouchEvent(dev, ti, ev, listener, client, win, grab, mask);
> +    }
> +
> +    if (ti->emulate_pointer)
> +        UpdateDeviceState(dev, &ev->device_event);
> +}
> +
>  int
>  InitProximityClassDeviceStruct(DeviceIntPtr dev)
>  {
> diff --git a/dix/touch.c b/dix/touch.c
> index 71198cd..421c3bb 100644
> --- a/dix/touch.c
> +++ b/dix/touch.c
> @@ -490,7 +490,8 @@ TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource)
>          flags |= TOUCH_POINTER_EMULATED;
>      /* send fake begin event to next owner */
>      nev = GetTouchEvents(tel, dev, ti->client_id, XI_TouchBegin, flags, mask);
> -    /* FIXME: deliver the event */
> +    for (i = 0; i < nev; i++)
> +        DeliverTouchEvents(dev, ti, tel + i, resource);
>  
>      valuator_mask_free(&mask);
>      FreeEventList(tel, GetMaximumEventsNum());
> @@ -500,7 +501,7 @@ TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource)
>      {
>          DeviceEvent *ev = &ti->history[i];
>          ev->flags |= TOUCH_REPLAYING;
> -        /* FIXME: deliver the event */
> +        DeliverTouchEvents(dev, ti, (InternalEvent*)ev, resource);
>      }
>  }
>  
> @@ -841,3 +842,4 @@ TouchSetupListeners(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev)
>              return;
>      }
>  }
> +
> diff --git a/include/dix.h b/include/dix.h
> index 7043201..4f21b41 100644
> --- a/include/dix.h
> +++ b/include/dix.h
> @@ -405,6 +405,12 @@ extern int DeliverOneGrabbedEvent(
>      DeviceIntPtr /* dev */,
>      enum InputLevel /* level */);
>  
> +extern void DeliverTouchEvents(
> +    DeviceIntPtr /* dev */,
> +    TouchPointInfoPtr /* ti */,
> +    InternalEvent* /* ev */,
> +    XID /* resource */);
> +
>  extern void InitializeSprite(
>      DeviceIntPtr /* pDev */,
>      WindowPtr    /* pWin */);
> -- 
> 1.7.5.4
> 


More information about the xorg-devel mailing list