From e389a19a730055d4550ff708da83c78099a516fd Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Tue, 20 Dec 2011 16:32:52 -0800 Subject: 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 Co-authored-by: Chase Douglas Signed-off-by: Peter Hutterer Reviewed-by: Chase Douglas Reviewed-by: Chase Douglas --- Xi/exevents.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dix/touch.c | 5 +- include/dix.h | 6 + 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 #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 704320133..4f21b4105 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 */); -- cgit v1.2.3