touch.c 32.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 * 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"
33
#include "dixgrabs.h"
34

35 36
#include "eventstr.h"
#include "exevents.h"
37
#include "exglobals.h"
38 39 40
#include "inpututils.h"
#include "eventconvert.h"
#include "windowstr.h"
41
#include "mi.h"
42

43 44
#define TOUCH_HISTORY_SIZE 100

45
/* If a touch queue resize is needed, the device id's bit is set. */
46
static unsigned char resize_waiting[(MAXDEVICES + 7) / 8];
47

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
/**
 * 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.
 */

67 68 69 70 71 72 73 74 75 76 77
/**
 * 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
78
TouchResizeQueue(ClientPtr client, void *closure)
79 80 81 82 83 84
{
    int i;

    OsBlockSignals();

    /* first two ids are reserved */
85
    for (i = 2; i < MAXDEVICES; i++) {
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
        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 */
102
        size = dev->last.num_touches + dev->last.num_touches / 2 + 1;
103

104
        tmp = reallocarray(dev->last.touches, size, sizeof(*dev->last.touches));
105
        if (tmp) {
106
            int j;
107

108
            dev->last.touches = tmp;
109 110
            for (j = dev->last.num_touches; j < size; j++)
                TouchInitDDXTouchPoint(dev, &dev->last.touches[j]);
111 112 113 114 115 116 117 118 119
            dev->last.num_touches = size;
        }

    }
    OsReleaseSignals();

    return TRUE;
}

120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
/**
 * 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;

137
    for (i = 0; i < dev->last.num_touches; i++) {
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
        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;
163
    Bool emulate_pointer;
164 165 166 167

    if (!t)
        return NULL;

168 169
    emulate_pointer = (t->mode == XIDirectTouch);

170 171 172 173 174
    /* Look for another active touchpoint with the same DDX ID. DDX
     * touchpoints must be unique. */
    if (TouchFindByDDXID(dev, ddx_id, FALSE))
        return NULL;

175
    for (i = 0; i < dev->last.num_touches; i++) {
176 177 178
        /* Only emulate pointer events on the first touch */
        if (dev->last.touches[i].active)
            emulate_pointer = FALSE;
179
        else if (!ti)           /* ti is now first non-active touch rec */
180 181 182 183 184 185
            ti = &dev->last.touches[i];

        if (!emulate_pointer && ti)
            break;
    }

186
    if (ti) {
187
        int client_id;
188

189 190 191 192 193 194 195 196 197 198 199
        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;
    }

200 201 202
    /* 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. */
203 204 205
    ErrorFSigSafe("%s: not enough space for touch events (max %u touchpoints). "
                  "Dropping this event.\n", dev->name, dev->last.num_touches);

206 207 208 209 210
    if (!BitIsOn(resize_waiting, dev->id)) {
        SetBit(resize_waiting, dev->id);
        QueueWorkProc(TouchResizeQueue, serverClient, NULL);
    }

211 212 213 214 215 216 217 218 219 220 221 222 223
    return NULL;
}

void
TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti)
{
    TouchClassPtr t = dev->touch;

    if (!t)
        return;

    ti->active = FALSE;
}
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

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));
248
    if (!ti->sprite.spriteTrace) {
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
        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;
266
    int i;
267 268 269 270 271

    if (!device->touch || index >= device->touch->num_touches)
        return;
    ti = &device->touch->touches[index];

272 273 274
    if (ti->active)
        TouchEndTouch(device, ti);

275 276 277
    for (i = 0; i < ti->num_listeners; i++)
        TouchRemoveListener(ti, ti->listeners[0].listener);

278 279 280 281 282 283 284 285 286 287 288
    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;
}

289 290 291 292 293 294 295 296 297 298 299 300 301 302
/**
 * 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;

303
    for (i = 0; i < t->num_touches; i++) {
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
        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;

339 340
 try_find_touch:
    for (i = 0; i < t->num_touches; i++) {
341 342 343 344 345 346 347 348 349 350 351 352
        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. */
353
    tmp = reallocarray(t->touches, t->num_touches + 1, sizeof(*ti));
354
    if (tmp) {
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
        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)
{
372 373
    int i;

374
    if (ti->emulate_pointer) {
375 376
        GrabPtr grab;

377
        if ((grab = dev->deviceGrab.grab)) {
378 379
            if (dev->deviceGrab.fromPassiveGrab &&
                !dev->button->buttonsDown &&
380 381
                !dev->touch->buttonsDown && GrabIsPointerGrab(grab))
                (*dev->deviceGrab.DeactivateGrab) (dev);
382 383 384
        }
    }

385 386 387
    for (i = 0; i < ti->num_listeners; i++)
        TouchRemoveListener(ti, ti->listeners[0].listener);

388 389 390 391 392 393 394 395 396
    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;

397 398
    TouchEventHistoryFree(ti);

399 400
    valuator_mask_zero(ti->valuators);
}
401

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
/**
 * 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;

445 446 447 448
    switch (ev->type) {
    case ET_TouchBegin:
        /* don't store the same touchbegin twice */
        if (ti->history_elements > 0)
449
            return;
450 451 452 453 454 455 456
        break;
    case ET_TouchUpdate:
        break;
    case ET_TouchEnd:
        return;                 /* no TouchEnd events in the history */
    default:
        return;
457 458 459
    }

    /* We only store real events in the history */
460
    if (ev->flags & (TOUCH_CLIENT_ID | TOUCH_REPLAYING))
461 462 463 464
        return;

    ti->history[ti->history_elements++] = *ev;
    /* FIXME: proper overflow fixes */
465
    if (ti->history_elements > ti->history_size - 1) {
466
        ti->history_elements = ti->history_size - 1;
467
        DebugF("source device %d: history size %zu overflowing for touch %u\n",
468
               ti->sourceid, ti->history_size, ti->client_id);
469 470 471 472 473 474
    }
}

void
TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource)
{
475
    int i;
476 477 478 479

    if (!ti->history)
        return;

480
    TouchDeliverDeviceClassesChangedEvent(ti, ti->history[0].time, resource);
481

482
    for (i = 0; i < ti->history_elements; i++) {
483
        DeviceEvent *ev = &ti->history[i];
484

485
        ev->flags |= TOUCH_REPLAYING;
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
        ev->resource = resource;
        /* FIXME:
           We're replaying ti->history which contains the TouchBegin +
           all TouchUpdates for ti. This needs to be passed on to the next
           listener. If that is a touch listener, everything is dandy.
           If the TouchBegin however triggers a sync passive grab, the
           TouchUpdate events must be sent to EnqueueEvent so the events end
           up in syncEvents.pending to be forwarded correctly in a
           subsequent ComputeFreeze().

           However, if we just send them to EnqueueEvent the sync'ing device
           prevents handling of touch events for ownership listeners who
           want the events right here, right now.
         */
        dev->public.processInputProc((InternalEvent*)ev, dev);
501 502
    }
}
503

504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
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);
    }
}

528 529 530 531 532 533 534 535 536 537 538
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++)
539 540
        if (!t->touches[i].pending_finish &&
            t->touches[i].sprite.spriteTraceGood > 0)
541 542 543 544 545 546 547 548
            break;
    if (i < t->num_touches)
        srcsprite = &t->touches[i].sprite;
    else if (dev->spriteInfo->sprite)
        srcsprite = dev->spriteInfo->sprite;
    else
        return FALSE;

549
    if (srcsprite->spriteTraceGood > sprite->spriteTraceSize) {
550 551
        trace = reallocarray(sprite->spriteTrace,
                             srcsprite->spriteTraceSize, sizeof(*trace));
552
        if (!trace) {
553 554 555 556 557 558 559
            sprite->spriteTraceGood = 0;
            return FALSE;
        }
        sprite->spriteTrace = trace;
        sprite->spriteTraceSize = srcsprite->spriteTraceGood;
    }
    memcpy(sprite->spriteTrace, srcsprite->spriteTrace,
560
           srcsprite->spriteTraceGood * sizeof(*trace));
561 562 563 564 565 566 567 568 569 570
    sprite->spriteTraceGood = srcsprite->spriteTraceGood;

    return TRUE;
}

/**
 * Ensure a window trace is present in ti->sprite, constructing one for
 * TouchBegin events.
 */
Bool
571 572
TouchBuildSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
                 InternalEvent *ev)
573 574 575 576
{
    TouchClassPtr t = sourcedev->touch;
    SpritePtr sprite = &ti->sprite;

577
    if (t->mode == XIDirectTouch) {
578 579 580 581 582 583 584 585 586 587 588 589 590
        /* 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
591 592
     * window plus the bottom-most event selection, plus any active grab. */
    ti->listeners = calloc(sprite->spriteTraceGood + 2, sizeof(*ti->listeners));
593
    if (!ti->listeners) {
594 595 596 597 598 599 600
        sprite->spriteTraceGood = 0;
        return FALSE;
    }
    ti->num_listeners = 0;

    return TRUE;
}
601

602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
/**
 * 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;

623 624
    BUG_RETURN_VAL(!event, 0);
    BUG_RETURN_VAL(!motion_event, 0);
625

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
    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;
641 642 643 644 645
    }

    BUG_WARN_MSG(!(event->device_event.flags & TOUCH_POINTER_EMULATED),
                 "Non-emulating touch event\n");

646
    motion_event->device_event = event->device_event;
647 648 649 650
    motion_event->any.type = ET_Motion;
    motion_event->device_event.detail.button = 0;
    motion_event->device_event.flags = XIPointerEmulated;

651
    if (nevents > 1) {
652
        BUG_RETURN_VAL(!button_event, 0);
653
        button_event->device_event = event->device_event;
654 655 656 657 658 659 660 661
        button_event->any.type = ptrtype;
        button_event->device_event.flags = XIPointerEmulated;
        /* detail is already correct */
    }

    return nevents;
}

662 663 664 665 666 667 668 669 670
/**
 * 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;

671 672 673 674 675 676 677 678 679 680 681 682
    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;
683 684 685 686
    }
    return type;
}

687 688 689 690 691 692 693 694 695 696
/**
 * @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);
}

697 698 699 700
/**
 * Add the resource to this touch's listeners.
 */
void
701 702 703
TouchAddListener(TouchPointInfoPtr ti, XID resource, int resource_type,
                 enum InputLevel level, enum TouchListenerType type,
                 enum TouchListenerState state, WindowPtr window,
704
                 const GrabPtr grab)
705
{
706 707 708 709 710 711 712 713
    GrabPtr g = NULL;

    /* We need a copy of the grab, not the grab itself since that may be
     * deleted by a UngrabButton request and leaves us with a dangling
     * pointer */
    if (grab)
        g = AllocGrab(grab);

714
    ti->listeners[ti->num_listeners].listener = resource;
715
    ti->listeners[ti->num_listeners].resource_type = resource_type;
716 717 718
    ti->listeners[ti->num_listeners].level = level;
    ti->listeners[ti->num_listeners].state = state;
    ti->listeners[ti->num_listeners].type = type;
719
    ti->listeners[ti->num_listeners].window = window;
720
    ti->listeners[ti->num_listeners].grab = g;
721 722
    if (grab)
        ti->num_grabs++;
723 724 725 726 727 728 729 730 731 732 733 734 735
    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;
736 737

    for (i = 0; i < ti->num_listeners; i++) {
738
        int j;
739
        TouchListener *listener = &ti->listeners[i];
740

741
        if (listener->listener != resource)
742
            continue;
743

744
        if (listener->grab) {
745
            FreeGrab(listener->grab);
746
            listener->grab = NULL;
747
            ti->num_grabs--;
748
        }
749 750 751 752 753 754

        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;
755

756
        return TRUE;
757 758 759 760 761 762 763 764 765 766 767 768
    }
    return FALSE;
}

static void
TouchAddGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
                     InternalEvent *ev, GrabPtr grab)
{
    enum TouchListenerType type = LISTENER_GRAB;

    /* FIXME: owner_events */

769
    if (grab->grabtype == XI2) {
770 771 772 773
        if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchOwnership))
            TouchEventHistoryAllocate(ti);
        if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin))
            type = LISTENER_POINTER_GRAB;
774 775
    }
    else if (grab->grabtype == XI || grab->grabtype == CORE) {
776 777 778 779
        TouchEventHistoryAllocate(ti);
        type = LISTENER_POINTER_GRAB;
    }

780 781
    /* grab listeners are always RT_NONE since we keep the grab pointer */
    TouchAddListener(ti, grab->resource, RT_NONE, grab->grabtype,
782
                     type, LISTENER_AWAITING_BEGIN, grab->window, grab);
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
}

/**
 * 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;
809
    uint16_t evtype = 0;        /* may be event type or emulated event type */
810 811 812 813 814 815 816
    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;
817
    else if (!mask) {           /* now try for pointer event */
818
        mask = EventIsDeliverable(dev, TouchGetPointerEventType(ev), win);
819
        if (mask) {
820 821 822 823 824 825 826 827 828
            evtype = GetXI2Type(TouchGetPointerEventType(ev));
            type = LISTENER_POINTER_REGULAR;
        }
    }
    if (!mask)
        return FALSE;

    inputMasks = wOtherInputMasks(win);

829 830
    if (mask & EVENT_XI2_MASK) {
        nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
831 832 833 834 835 836
            if (!xi2mask_isset(iclients->xi2mask, dev, evtype))
                continue;

            if (!xi2mask_isset(iclients->xi2mask, dev, XI_TouchOwnership))
                TouchEventHistoryAllocate(ti);

837
            TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI2,
838
                             type, LISTENER_AWAITING_BEGIN, win, NULL);
839 840 841 842
            return TRUE;
        }
    }

843
    if (mask & EVENT_XI1_MASK) {
844 845
        int xitype = GetXIType(TouchGetPointerEventType(ev));
        Mask xi_filter = event_get_filter_from_type(dev, xitype);
846 847

        nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
848 849 850 851
            if (!(iclients->mask[dev->id] & xi_filter))
                continue;

            TouchEventHistoryAllocate(ti);
852
            TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI,
853
                             LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN,
854
                             win, NULL);
855 856 857 858
            return TRUE;
        }
    }

859
    if (mask & EVENT_CORE_MASK) {
860 861
        int coretype = GetCoreType(TouchGetPointerEventType(ev));
        Mask core_filter = event_get_filter_from_type(dev, coretype);
862
        OtherClients *oclients;
863 864

        /* window owner */
865
        if (IsMaster(dev) && (win->eventMask & core_filter)) {
866
            TouchEventHistoryAllocate(ti);
867
            TouchAddListener(ti, win->drawable.id, RT_WINDOW, CORE,
868
                             LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN,
869
                             win, NULL);
870 871 872 873
            return TRUE;
        }

        /* all others */
874 875
        nt_list_for_each_entry(oclients, wOtherClients(win), next) {
            if (!(oclients->mask & core_filter))
876 877 878
                continue;

            TouchEventHistoryAllocate(ti);
879
            TouchAddListener(ti, oclients->resource, RT_OTHERCLIENT, CORE,
880
                             type, LISTENER_AWAITING_BEGIN, win, NULL);
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
            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 &&
898
        !xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin))
899 900 901 902 903 904 905 906 907 908 909 910
        return;

    TouchAddGrabListener(dev, ti, ev, grab);
}

void
TouchSetupListeners(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev)
{
    int i;
    SpritePtr sprite = &ti->sprite;
    WindowPtr win;

911
    if (dev->deviceGrab.grab && !dev->deviceGrab.fromPassiveGrab)
912 913
        TouchAddActiveGrabListener(dev, ti, ev, dev->deviceGrab.grab);

914 915 916 917 918
    /* 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;

919 920
    /* First, find all grabbing clients from the root window down
     * to the deepest child window. */
921
    for (i = 0; i < sprite->spriteTraceGood; i++) {
922 923 924 925 926 927
        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. */
928
    for (i = sprite->spriteTraceGood - 1; i >= 0; i--) {
929 930 931 932 933 934 935 936
        Bool delivered;

        win = sprite->spriteTrace[i];
        delivered = TouchAddRegularListener(dev, ti, win, ev);
        if (delivered)
            return;
    }
}
937 938

/**
Peter Hutterer's avatar
Peter Hutterer committed
939 940
 * Remove the touch pointer grab from the device. Called from
 * DeactivatePointerGrab()
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
 */
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;
957
    if (!IsTouchEvent((InternalEvent *) ev))
958 959 960 961 962
        return;

    ti = TouchFindByClientID(dev, ev->touchid);
    if (!ti)
        return;
963 964

    /* FIXME: missing a bit of code here... */
965
}
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980

/* 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");

981
    for (dev = inputInfo.devices; dev; dev = dev->next) {
982 983 984
        if (!dev->touch)
            continue;

985
        for (i = 0; i < dev->touch->num_touches; i++) {
986 987 988 989
            ti = &dev->touch->touches[i];
            if (!ti->active)
                continue;

990
            for (j = 0; j < ti->num_listeners; j++) {
991
                if (CLIENT_BITS(ti->listeners[j].listener) != resource)
992 993 994
                    continue;

                nev = GetTouchOwnershipEvents(events, dev, ti, XIRejectTouch,
995
                                              ti->listeners[j].listener, 0);
996 997 998 999 1000 1001 1002 1003 1004 1005
                for (k = 0; k < nev; k++)
                    mieqProcessDeviceEvent(dev, events + k, NULL);

                break;
            }
        }
    }

    FreeEventList(events, GetMaximumEventsNum());
}
1006

1007 1008 1009 1010 1011 1012 1013 1014
int
TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener,
                          int mode)
{
    InternalEvent *events;
    int nev;
    int i;

1015 1016
    BUG_RETURN_VAL(listener < 0, BadMatch);
    BUG_RETURN_VAL(listener >= ti->num_listeners, BadMatch);
1017

1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
    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());
1028
    BUG_RETURN_VAL_MSG(!events, BadAlloc, "Failed to allocate touch ownership events\n");
1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041

    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;
}

1042 1043
int
TouchAcceptReject(ClientPtr client, DeviceIntPtr dev, int mode,
1044
                  uint32_t touchid, Window grab_window, XID *error)
1045 1046
{
    TouchPointInfoPtr ti;
1047
    int i;
1048

1049
    if (!dev->touch) {
1050 1051 1052 1053 1054
        *error = dev->id;
        return BadDevice;
    }

    ti = TouchFindByClientID(dev, touchid);
1055
    if (!ti) {
1056 1057 1058 1059
        *error = touchid;
        return BadValue;
    }

1060
    for (i = 0; i < ti->num_listeners; i++) {
1061 1062 1063 1064 1065 1066 1067
        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;

1068
    return TouchListenerAcceptReject(dev, ti, i, mode);
1069
}
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097

/**
 * 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());
}
1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123

/**
 * Generate and deliver a TouchEnd event.
 *
 * @param dev The device to deliver the event for.
 * @param ti The touch point record to deliver the event for.
 * @param flags Internal event flags. The called does not need to provide
 *        TOUCH_CLIENT_ID and TOUCH_POINTER_EMULATED, this function will ensure
 *        they are set appropriately.
 * @param resource The client resource to deliver to, or 0 for all clients.
 */
void
TouchEmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource)
{
    InternalEvent event;

    /* We're not processing a touch end for a frozen device */
    if (dev->deviceGrab.sync.frozen)
        return;

    flags |= TOUCH_CLIENT_ID;
    if (ti->emulate_pointer)
        flags |= TOUCH_POINTER_EMULATED;
    TouchDeliverDeviceClassesChangedEvent(ti, GetTimeInMillis(), resource);
    GetDixTouchEnd(&event, dev, ti, flags);
    DeliverTouchEvents(dev, ti, &event, resource);
1124 1125
    if (ti->num_grabs == 0)
        UpdateDeviceState(dev, &event.device_event);
1126
}
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140

void
TouchAcceptAndEnd(DeviceIntPtr dev, int touchid)
{
    TouchPointInfoPtr ti = TouchFindByClientID(dev, touchid);
    if (!ti)
        return;

    TouchListenerAcceptReject(dev, ti, 0, XIAcceptTouch);
    if (ti->pending_finish)
        TouchEmitTouchEnd(dev, ti, 0, 0);
    if (ti->num_listeners <= 1)
        TouchEndTouch(dev, ti);
}