xserver-multidpi/dix/touch.c
Peter Hutterer f59499b5d0 dix: add resource type to touch listeners
Instead of guessing what resource type the listener is and what property to
retrieve, store the resource type in the listener directly.

Breaks XIT test cases:
TouchGrabTestMultipleTaps.PassiveGrabPointerEmulationMultipleTouchesFastSuccession

Fixes https://bugs.freedesktop.org/show_bug.cgi?id=56557

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Keith Packard <keithp@keithp.com>
Reviewed-by: Chase Douglas <chase.douglas@ubuntu.com>
2013-01-09 12:33:36 +10:00

1063 lines
30 KiB
C

/*
* 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 <daniel@fooishbar.org>
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#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)
{
int i;
if (!ti->history)
return;
TouchDeliverDeviceClassesChangedEvent(ti, ti->history[0].time, resource);
for (i = 0; i < ti->history_elements; i++) {
DeviceEvent *ev = &ti->history[i];
ev->flags |= TOUCH_REPLAYING;
DeliverTouchEvents(dev, ti, (InternalEvent *) ev, resource);
}
}
void
TouchDeliverDeviceClassesChangedEvent(TouchPointInfoPtr ti, Time time,
XID resource)
{
DeviceIntPtr dev;
int num_events = 0;
InternalEvent dcce;
dixLookupDevice(&dev, ti->sourceid, serverClient, DixWriteAccess);
if (!dev)
return;
/* UpdateFromMaster generates at most one event */
UpdateFromMaster(&dcce, dev, DEVCHANGE_POINTER_EVENT, &num_events);
BUG_WARN(num_events > 1);
if (num_events) {
dcce.any.time = time;
/* FIXME: This doesn't do anything */
dev->public.processInputProc(&dcce, dev);
}
}
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;
/* FIXME: missing a bit of code here... */
}
/* 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());
}