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

Chase Douglas chase.douglas at canonical.com
Tue Dec 20 16:32:52 PST 2011


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

 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