summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2011-12-20 16:32:52 -0800
committerChase Douglas <chase.douglas@canonical.com>2011-12-21 10:34:12 -0800
commit637d7108e915dafce862510dbc8a9e36ff21c0fd (patch)
treef43dd95897099cd429f0abfdfe91787dda25a22b
parentbb945137a23d4961139a63ff26e4c9aee2fc9d17 (diff)
Process and deliver touch events
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@fooishbar.org> Co-authored-by: Chase Douglas <chase.douglas@canonical.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Chase Douglas <chase.douglas@canonical.com> Reviewed-by: Chase Douglas <chase.douglas@canonical.com>
-rw-r--r--Xi/exevents.c401
-rw-r--r--dix/touch.c5
-rw-r--r--include/dix.h6
3 files changed, 410 insertions, 2 deletions
diff --git a/Xi/exevents.c b/Xi/exevents.c
index 5cf60f8f1..b5fc82689 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 3e45e35a4..5615f2be6 100644
--- a/dix/touch.c
+++ b/dix/touch.c
@@ -496,7 +496,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());
@@ -506,7 +507,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);
}
}
diff --git a/include/dix.h b/include/dix.h
index 60462ecde..f3f68d39c 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 */);