/* * Copyright © 2011 Collabra Ltd. * Copyright © 2011 Red Hat, Inc. * * 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 */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include "inputstr.h" #include "scrnintstr.h" #include "dixgrabs.h" #include "eventstr.h" #include "exevents.h" #include "exglobals.h" #include "inpututils.h" #include "eventconvert.h" #include "windowstr.h" #include "mi.h" #define TOUCH_HISTORY_SIZE 100 /* If a touch queue resize is needed, the device id's bit is set. */ static unsigned char resize_waiting[(MAXDEVICES + 7) / 8]; /** * Some documentation about touch points: * The driver submits touch events with it's own (unique) touch point ID. * The driver may re-use those IDs, the DDX doesn't care. It just passes on * the data to the DIX. In the server, the driver's ID is referred to as the * DDX id anyway. * * On a TouchBegin, we create a DDXTouchPointInfo that contains the DDX id * and the client ID that this touchpoint will have. The client ID is the * one visible on the protocol. * * TouchUpdate and TouchEnd will only be processed if there is an active * touchpoint with the same DDX id. * * The DDXTouchPointInfo struct is stored dev->last.touches. When the event * being processed, it becomes a TouchPointInfo in dev->touch-touches which * contains amongst other things the sprite trace and delivery information. */ /** * Check which devices need a bigger touch event queue and grow their * last.touches by half it's current size. * * @param client Always the serverClient * @param closure Always NULL * * @return Always True. If we fail to grow we probably will topple over soon * anyway and re-executing this won't help. */ static Bool TouchResizeQueue(ClientPtr client, pointer closure) { int i; OsBlockSignals(); /* first two ids are reserved */ for (i = 2; i < MAXDEVICES; i++) { DeviceIntPtr dev; DDXTouchPointInfoPtr tmp; size_t size; if (!BitIsOn(resize_waiting, i)) continue; ClearBit(resize_waiting, i); /* device may have disappeared by now */ dixLookupDevice(&dev, i, serverClient, DixWriteAccess); if (!dev) continue; /* Need to grow the queue means dropping events. Grow sufficiently so we * don't need to do it often */ size = dev->last.num_touches + dev->last.num_touches / 2 + 1; tmp = realloc(dev->last.touches, size * sizeof(*dev->last.touches)); if (tmp) { int j; dev->last.touches = tmp; for (j = dev->last.num_touches; j < size; j++) TouchInitDDXTouchPoint(dev, &dev->last.touches[j]); dev->last.num_touches = size; } } OsReleaseSignals(); return TRUE; } /** * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the * associated DDXTouchPointInfoRec. * * @param dev The device to create the touch point for * @param ddx_id Touch id assigned by the driver/ddx * @param create Create the touchpoint if it cannot be found */ DDXTouchPointInfoPtr TouchFindByDDXID(DeviceIntPtr dev, uint32_t ddx_id, Bool create) { DDXTouchPointInfoPtr ti; int i; if (!dev->touch) return NULL; for (i = 0; i < dev->last.num_touches; i++) { ti = &dev->last.touches[i]; if (ti->active && ti->ddx_id == ddx_id) return ti; } return create ? TouchBeginDDXTouch(dev, ddx_id) : NULL; } /** * Given a unique DDX ID for a touchpoint, create a touchpoint record and * return it. * * If no other touch points are active, mark new touchpoint for pointer * emulation. * * Returns NULL on failure (i.e. if another touch with that ID is already active, * allocation failure). */ DDXTouchPointInfoPtr TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id) { static int next_client_id = 1; int i; TouchClassPtr t = dev->touch; DDXTouchPointInfoPtr ti = NULL; Bool emulate_pointer; if (!t) return NULL; emulate_pointer = (t->mode == XIDirectTouch); /* Look for another active touchpoint with the same DDX ID. DDX * touchpoints must be unique. */ if (TouchFindByDDXID(dev, ddx_id, FALSE)) return NULL; for (i = 0; i < dev->last.num_touches; i++) { /* Only emulate pointer events on the first touch */ if (dev->last.touches[i].active) emulate_pointer = FALSE; else if (!ti) /* ti is now first non-active touch rec */ ti = &dev->last.touches[i]; if (!emulate_pointer && ti) break; } if (ti) { int client_id; ti->active = TRUE; ti->ddx_id = ddx_id; client_id = next_client_id; next_client_id++; if (next_client_id == 0) next_client_id = 1; ti->client_id = client_id; ti->emulate_pointer = emulate_pointer; return ti; } /* If we get here, then we've run out of touches and we need to drop the * event (we're inside the SIGIO handler here) schedule a WorkProc to * grow the queue for us for next time. */ ErrorFSigSafe("%s: not enough space for touch events (max %u touchpoints). " "Dropping this event.\n", dev->name, dev->last.num_touches); if (!BitIsOn(resize_waiting, dev->id)) { SetBit(resize_waiting, dev->id); QueueWorkProc(TouchResizeQueue, serverClient, NULL); } return NULL; } void TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti) { TouchClassPtr t = dev->touch; if (!t) return; ti->active = FALSE; } void TouchInitDDXTouchPoint(DeviceIntPtr dev, DDXTouchPointInfoPtr ddxtouch) { memset(ddxtouch, 0, sizeof(*ddxtouch)); ddxtouch->valuators = valuator_mask_new(dev->valuator->numAxes); } Bool TouchInitTouchPoint(TouchClassPtr t, ValuatorClassPtr v, int index) { TouchPointInfoPtr ti; if (index >= t->num_touches) return FALSE; ti = &t->touches[index]; memset(ti, 0, sizeof(*ti)); ti->valuators = valuator_mask_new(v->numAxes); if (!ti->valuators) return FALSE; ti->sprite.spriteTrace = calloc(32, sizeof(*ti->sprite.spriteTrace)); if (!ti->sprite.spriteTrace) { valuator_mask_free(&ti->valuators); return FALSE; } ti->sprite.spriteTraceSize = 32; ti->sprite.spriteTrace[0] = screenInfo.screens[0]->root; ti->sprite.hot.pScreen = screenInfo.screens[0]; ti->sprite.hotPhys.pScreen = screenInfo.screens[0]; ti->client_id = -1; return TRUE; } void TouchFreeTouchPoint(DeviceIntPtr device, int index) { TouchPointInfoPtr ti; if (!device->touch || index >= device->touch->num_touches) return; ti = &device->touch->touches[index]; if (ti->active) TouchEndTouch(device, ti); valuator_mask_free(&ti->valuators); free(ti->sprite.spriteTrace); ti->sprite.spriteTrace = NULL; free(ti->listeners); ti->listeners = NULL; free(ti->history); ti->history = NULL; ti->history_size = 0; ti->history_elements = 0; } /** * Given a client-facing ID (e.g. DeviceEvent::detail.touch), find the * associated TouchPointInfoRec. */ TouchPointInfoPtr TouchFindByClientID(DeviceIntPtr dev, uint32_t client_id) { TouchClassPtr t = dev->touch; TouchPointInfoPtr ti; int i; if (!t) return NULL; for (i = 0; i < t->num_touches; i++) { ti = &t->touches[i]; if (ti->active && ti->client_id == client_id) return ti; } return NULL; } /** * Given a unique ID for a touchpoint, create a touchpoint record in the * server. * * Returns NULL on failure (i.e. if another touch with that ID is already active, * allocation failure). */ TouchPointInfoPtr TouchBeginTouch(DeviceIntPtr dev, int sourceid, uint32_t touchid, Bool emulate_pointer) { int i; TouchClassPtr t = dev->touch; TouchPointInfoPtr ti; void *tmp; if (!t) return NULL; /* Look for another active touchpoint with the same client ID. It's * technically legitimate for a touchpoint to still exist with the same * ID but only once the 32 bits wrap over and you've used up 4 billion * touch ids without lifting that one finger off once. In which case * you deserve a medal or something, but not error handling code. */ if (TouchFindByClientID(dev, touchid)) return NULL; try_find_touch: for (i = 0; i < t->num_touches; i++) { ti = &t->touches[i]; if (!ti->active) { ti->active = TRUE; ti->client_id = touchid; ti->sourceid = sourceid; ti->emulate_pointer = emulate_pointer; return ti; } } /* If we get here, then we've run out of touches: enlarge dev->touch and * try again. */ tmp = realloc(t->touches, (t->num_touches + 1) * sizeof(*ti)); if (tmp) { t->touches = tmp; t->num_touches++; if (TouchInitTouchPoint(t, dev->valuator, t->num_touches - 1)) goto try_find_touch; } return NULL; } /** * Releases a touchpoint for use: this must only be called after all events * related to that touchpoint have been sent and finalised. Called from * ProcessTouchEvent and friends. Not by you. */ void TouchEndTouch(DeviceIntPtr dev, TouchPointInfoPtr ti) { if (ti->emulate_pointer) { GrabPtr grab; if ((grab = dev->deviceGrab.grab)) { if (dev->deviceGrab.fromPassiveGrab && !dev->button->buttonsDown && !dev->touch->buttonsDown && GrabIsPointerGrab(grab)) (*dev->deviceGrab.DeactivateGrab) (dev); } } ti->active = FALSE; ti->pending_finish = FALSE; ti->sprite.spriteTraceGood = 0; free(ti->listeners); ti->listeners = NULL; ti->num_listeners = 0; ti->num_grabs = 0; ti->client_id = 0; TouchEventHistoryFree(ti); valuator_mask_zero(ti->valuators); } /** * Allocate the event history for this touch pointer. Calling this on a * touchpoint that already has an event history does nothing but counts as * as success. * * @return TRUE on success, FALSE on allocation errors */ Bool TouchEventHistoryAllocate(TouchPointInfoPtr ti) { if (ti->history) return TRUE; ti->history = calloc(TOUCH_HISTORY_SIZE, sizeof(*ti->history)); ti->history_elements = 0; if (ti->history) ti->history_size = TOUCH_HISTORY_SIZE; return ti->history != NULL; } void TouchEventHistoryFree(TouchPointInfoPtr ti) { free(ti->history); ti->history = NULL; ti->history_size = 0; ti->history_elements = 0; } /** * Store the given event on the event history (if one exists) * A touch event history consists of one TouchBegin and several TouchUpdate * events (if applicable) but no TouchEnd event. * If more than one TouchBegin is pushed onto the stack, the push is * ignored, calling this function multiple times for the TouchBegin is * valid. */ void TouchEventHistoryPush(TouchPointInfoPtr ti, const DeviceEvent *ev) { if (!ti->history) return; switch (ev->type) { case ET_TouchBegin: /* don't store the same touchbegin twice */ if (ti->history_elements > 0) return; break; case ET_TouchUpdate: break; case ET_TouchEnd: return; /* no TouchEnd events in the history */ default: return; } /* We only store real events in the history */ if (ev->flags & (TOUCH_CLIENT_ID | TOUCH_REPLAYING)) return; ti->history[ti->history_elements++] = *ev; /* FIXME: proper overflow fixes */ if (ti->history_elements > ti->history_size - 1) { ti->history_elements = ti->history_size - 1; DebugF("source device %d: history size %d overflowing for touch %u\n", ti->sourceid, ti->history_size, ti->client_id); } } void TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource) { InternalEvent *tel; ValuatorMask *mask; int i, nev; int flags; if (!ti->history) return; tel = InitEventList(GetMaximumEventsNum()); mask = valuator_mask_new(0); valuator_mask_set_double(mask, 0, ti->history[0].valuators.data[0]); valuator_mask_set_double(mask, 1, ti->history[0].valuators.data[1]); flags = TOUCH_CLIENT_ID | TOUCH_REPLAYING; if (ti->emulate_pointer) flags |= TOUCH_POINTER_EMULATED; /* Generate events based on a fake touch begin event to get DCCE events if * needed */ /* FIXME: This needs to be cleaned up */ nev = GetTouchEvents(tel, dev, ti->client_id, XI_TouchBegin, flags, mask); for (i = 0; i < nev; i++) { /* Send saved touch begin event */ if (tel[i].any.type == ET_TouchBegin) { DeviceEvent *ev = &ti->history[0]; ev->flags |= TOUCH_REPLAYING; DeliverTouchEvents(dev, ti, (InternalEvent*)ev, resource); } else {/* Send DCCE event */ tel[i].any.time = ti->history[0].time; DeliverTouchEvents(dev, ti, tel + i, resource); } } valuator_mask_free(&mask); FreeEventList(tel, GetMaximumEventsNum()); /* First event was TouchBegin, already replayed that one */ for (i = 1; i < ti->history_elements; i++) { DeviceEvent *ev = &ti->history[i]; ev->flags |= TOUCH_REPLAYING; DeliverTouchEvents(dev, ti, (InternalEvent *) ev, resource); } } Bool TouchBuildDependentSpriteTrace(DeviceIntPtr dev, SpritePtr sprite) { int i; TouchClassPtr t = dev->touch; WindowPtr *trace; SpritePtr srcsprite; /* All touches should have the same sprite trace, so 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].pending_finish && t->touches[i].sprite.spriteTraceGood > 0) break; if (i < t->num_touches) srcsprite = &t->touches[i].sprite; else if (dev->spriteInfo->sprite) srcsprite = dev->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; return TRUE; } /** * Ensure a window trace is present in ti->sprite, constructing one for * TouchBegin events. */ Bool TouchBuildSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, InternalEvent *ev) { TouchClassPtr t = sourcedev->touch; SpritePtr sprite = &ti->sprite; 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->device_event.root_x, ev->device_event.root_y); } else if (!TouchBuildDependentSpriteTrace(sourcedev, sprite)) return FALSE; 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, plus any active grab. */ ti->listeners = calloc(sprite->spriteTraceGood + 2, sizeof(*ti->listeners)); if (!ti->listeners) { sprite->spriteTraceGood = 0; return FALSE; } ti->num_listeners = 0; return TRUE; } /** * Copy the touch event into the pointer_event, switching the required * fields to make it a correct pointer event. * * @param event The original touch event * @param[in] motion_event The respective motion event * @param[in] button_event The respective button event (if any) * * @returns The number of converted events. * @retval 0 An error occured * @retval 1 only the motion event is valid * @retval 2 motion and button event are valid */ int TouchConvertToPointerEvent(const InternalEvent *event, InternalEvent *motion_event, InternalEvent *button_event) { int ptrtype; int nevents = 0; BUG_RETURN_VAL(!event, 0); BUG_RETURN_VAL(!motion_event, 0); switch (event->any.type) { case ET_TouchUpdate: nevents = 1; break; case ET_TouchBegin: nevents = 2; /* motion + press */ ptrtype = ET_ButtonPress; break; case ET_TouchEnd: nevents = 2; /* motion + release */ ptrtype = ET_ButtonRelease; break; default: BUG_WARN_MSG(1, "Invalid event type %d\n", event->any.type); return 0; } BUG_WARN_MSG(!(event->device_event.flags & TOUCH_POINTER_EMULATED), "Non-emulating touch event\n"); *motion_event = *event; motion_event->any.type = ET_Motion; motion_event->device_event.detail.button = 0; motion_event->device_event.flags = XIPointerEmulated; if (nevents > 1) { BUG_RETURN_VAL(!button_event, 0); *button_event = *event; button_event->any.type = ptrtype; button_event->device_event.flags = XIPointerEmulated; /* detail is already correct */ } return nevents; } /** * Return the corresponding pointer emulation internal event type for the given * touch event or 0 if no such event type exists. */ int TouchGetPointerEventType(const InternalEvent *event) { int type = 0; switch (event->any.type) { case ET_TouchBegin: type = ET_ButtonPress; break; case ET_TouchUpdate: type = ET_Motion; break; case ET_TouchEnd: type = ET_ButtonRelease; break; default: break; } return type; } /** * @returns TRUE if the specified grab or selection is the current owner of * the touch sequence. */ Bool TouchResourceIsOwner(TouchPointInfoPtr ti, XID resource) { return (ti->listeners[0].listener == resource); } /** * Add the resource to this touch's listeners. */ void TouchAddListener(TouchPointInfoPtr ti, XID resource, int resource_type, enum InputLevel level, enum TouchListenerType type, enum TouchListenerState state, WindowPtr window, GrabPtr grab) { ti->listeners[ti->num_listeners].listener = resource; ti->listeners[ti->num_listeners].resource_type = resource_type; ti->listeners[ti->num_listeners].level = level; ti->listeners[ti->num_listeners].state = state; ti->listeners[ti->num_listeners].type = type; ti->listeners[ti->num_listeners].window = window; ti->listeners[ti->num_listeners].grab = grab; if (grab) ti->num_grabs++; ti->num_listeners++; } /** * Remove the resource from this touch's listeners. * * @return TRUE if the resource was removed, FALSE if the resource was not * in the list */ Bool TouchRemoveListener(TouchPointInfoPtr ti, XID resource) { int i; for (i = 0; i < ti->num_listeners; i++) { if (ti->listeners[i].listener == resource) { int j; if (ti->listeners[i].grab) { ti->listeners[i].grab = NULL; ti->num_grabs--; } for (j = i; j < ti->num_listeners - 1; j++) ti->listeners[j] = ti->listeners[j + 1]; ti->num_listeners--; ti->listeners[ti->num_listeners].listener = 0; ti->listeners[ti->num_listeners].state = LISTENER_AWAITING_BEGIN; return TRUE; } } return FALSE; } static void TouchAddGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev, GrabPtr grab) { enum TouchListenerType type = LISTENER_GRAB; /* FIXME: owner_events */ if (grab->grabtype == XI2) { if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchOwnership)) TouchEventHistoryAllocate(ti); if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin)) type = LISTENER_POINTER_GRAB; } else if (grab->grabtype == XI || grab->grabtype == CORE) { TouchEventHistoryAllocate(ti); type = LISTENER_POINTER_GRAB; } /* grab listeners are always RT_NONE since we keep the grab pointer */ TouchAddListener(ti, grab->resource, RT_NONE, grab->grabtype, type, LISTENER_AWAITING_BEGIN, grab->window, grab); } /** * Add one listener if there is a grab on the given window. */ static void TouchAddPassiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti, WindowPtr win, InternalEvent *ev) { GrabPtr grab; Bool check_core = IsMaster(dev) && ti->emulate_pointer; /* FIXME: make CheckPassiveGrabsOnWindow only trigger on TouchBegin */ grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core, FALSE); if (!grab) return; TouchAddGrabListener(dev, ti, ev, grab); } static Bool TouchAddRegularListener(DeviceIntPtr dev, TouchPointInfoPtr ti, WindowPtr win, InternalEvent *ev) { InputClients *iclients = NULL; OtherInputMasks *inputMasks = NULL; uint16_t evtype = 0; /* may be event type or emulated event type */ enum TouchListenerType type = LISTENER_REGULAR; int mask; evtype = GetXI2Type(ev->any.type); mask = EventIsDeliverable(dev, ev->any.type, win); if (!mask && !ti->emulate_pointer) return FALSE; else if (!mask) { /* now try for pointer event */ mask = EventIsDeliverable(dev, TouchGetPointerEventType(ev), win); if (mask) { evtype = GetXI2Type(TouchGetPointerEventType(ev)); type = LISTENER_POINTER_REGULAR; } } if (!mask) return FALSE; inputMasks = wOtherInputMasks(win); if (mask & EVENT_XI2_MASK) { nt_list_for_each_entry(iclients, inputMasks->inputClients, next) { if (!xi2mask_isset(iclients->xi2mask, dev, evtype)) continue; if (!xi2mask_isset(iclients->xi2mask, dev, XI_TouchOwnership)) TouchEventHistoryAllocate(ti); TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI2, type, LISTENER_AWAITING_BEGIN, win, NULL); return TRUE; } } if (mask & EVENT_XI1_MASK) { int xitype = GetXIType(TouchGetPointerEventType(ev)); Mask xi_filter = event_get_filter_from_type(dev, xitype); nt_list_for_each_entry(iclients, inputMasks->inputClients, next) { if (!(iclients->mask[dev->id] & xi_filter)) continue; TouchEventHistoryAllocate(ti); TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI, LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN, win, NULL); return TRUE; } } if (mask & EVENT_CORE_MASK) { int coretype = GetCoreType(TouchGetPointerEventType(ev)); Mask core_filter = event_get_filter_from_type(dev, coretype); OtherClients *oclients; /* window owner */ if (IsMaster(dev) && (win->eventMask & core_filter)) { TouchEventHistoryAllocate(ti); TouchAddListener(ti, win->drawable.id, RT_WINDOW, CORE, LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN, win, NULL); return TRUE; } /* all others */ nt_list_for_each_entry(oclients, wOtherClients(win), next) { if (!(oclients->mask & core_filter)) continue; TouchEventHistoryAllocate(ti); TouchAddListener(ti, oclients->resource, RT_OTHERCLIENT, CORE, type, LISTENER_AWAITING_BEGIN, win, NULL); return TRUE; } } return FALSE; } static void TouchAddActiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev, GrabPtr grab) { if (!ti->emulate_pointer && (grab->grabtype == CORE || grab->grabtype == XI)) return; if (!ti->emulate_pointer && grab->grabtype == XI2 && (grab->type != XI_TouchBegin && grab->type != XI_TouchEnd && grab->type != XI_TouchUpdate)) return; TouchAddGrabListener(dev, ti, ev, grab); } void TouchSetupListeners(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev) { int i; SpritePtr sprite = &ti->sprite; WindowPtr win; if (dev->deviceGrab.grab) TouchAddActiveGrabListener(dev, ti, ev, dev->deviceGrab.grab); /* We set up an active touch listener for existing touches, but not any * passive grab or regular listeners. */ if (ev->any.type != ET_TouchBegin) return; /* First, find all grabbing clients from the root window down * to the deepest child window. */ for (i = 0; i < sprite->spriteTraceGood; i++) { win = sprite->spriteTrace[i]; TouchAddPassiveGrabListener(dev, ti, win, ev); } /* Find the first client with an applicable event selection, * going from deepest child window back up to the root window. */ for (i = sprite->spriteTraceGood - 1; i >= 0; i--) { Bool delivered; win = sprite->spriteTrace[i]; delivered = TouchAddRegularListener(dev, ti, win, ev); if (delivered) return; } } /** * Remove the touch pointer grab from the device. Called from AllowSome() */ void TouchRemovePointerGrab(DeviceIntPtr dev) { TouchPointInfoPtr ti; GrabPtr grab; DeviceEvent *ev; if (!dev->touch) return; grab = dev->deviceGrab.grab; if (!grab) return; ev = dev->deviceGrab.sync.event; if (!IsTouchEvent((InternalEvent *) ev)) return; ti = TouchFindByClientID(dev, ev->touchid); if (!ti) return; } /* 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. */ void TouchListenerGone(XID resource) { TouchPointInfoPtr ti; DeviceIntPtr dev; InternalEvent *events = InitEventList(GetMaximumEventsNum()); int i, j, k, nev; if (!events) FatalError("TouchListenerGone: couldn't allocate 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]; if (!ti->active) continue; for (j = 0; j < ti->num_listeners; j++) { if (ti->listeners[j].listener != resource) continue; nev = GetTouchOwnershipEvents(events, dev, ti, XIRejectTouch, resource, 0); for (k = 0; k < nev; k++) mieqProcessDeviceEvent(dev, events + k, NULL); break; } } } FreeEventList(events, GetMaximumEventsNum()); } int TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener, int mode) { InternalEvent *events; int nev; int i; BUG_RETURN_VAL(listener < 0, BadMatch); BUG_RETURN_VAL(listener >= ti->num_listeners, BadMatch); if (listener > 0) { if (mode == XIRejectTouch) TouchRejected(dev, ti, ti->listeners[listener].listener, NULL); else ti->listeners[listener].state = LISTENER_EARLY_ACCEPT; return Success; } events = InitEventList(GetMaximumEventsNum()); BUG_RETURN_VAL_MSG(!events, BadAlloc, "Failed to allocate touch ownership events\n"); nev = GetTouchOwnershipEvents(events, dev, ti, mode, ti->listeners[0].listener, 0); BUG_WARN_MSG(nev == 0, "Failed to get touch ownership events\n"); for (i = 0; i < nev; i++) mieqProcessDeviceEvent(dev, events + i, NULL); FreeEventList(events, GetMaximumEventsNum()); return nev ? Success : BadMatch; } int TouchAcceptReject(ClientPtr client, DeviceIntPtr dev, int mode, uint32_t touchid, Window grab_window, XID *error) { TouchPointInfoPtr ti; int i; if (!dev->touch) { *error = dev->id; return BadDevice; } ti = TouchFindByClientID(dev, touchid); if (!ti) { *error = touchid; return BadValue; } for (i = 0; i < ti->num_listeners; i++) { if (CLIENT_ID(ti->listeners[i].listener) == client->index && ti->listeners[i].window->drawable.id == grab_window) break; } if (i == ti->num_listeners) return BadAccess; return TouchListenerAcceptReject(dev, ti, i, mode); } /** * End physically active touches for a device. */ void TouchEndPhysicallyActiveTouches(DeviceIntPtr dev) { InternalEvent *eventlist = InitEventList(GetMaximumEventsNum()); int i; OsBlockSignals(); mieqProcessInputEvents(); for (i = 0; i < dev->last.num_touches; i++) { DDXTouchPointInfoPtr ddxti = dev->last.touches + i; if (ddxti->active) { int j; int nevents = GetTouchEvents(eventlist, dev, ddxti->ddx_id, XI_TouchEnd, 0, NULL); for (j = 0; j < nevents; j++) mieqProcessDeviceEvent(dev, eventlist + j, NULL); } } OsReleaseSignals(); FreeEventList(eventlist, GetMaximumEventsNum()); }