touch.c revision 35c4bbdf
1/*
2 * Copyright © 2011 Collabra Ltd.
3 * Copyright © 2011 Red Hat, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Author: Daniel Stone <daniel@fooishbar.org>
25 */
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30
31#include "inputstr.h"
32#include "scrnintstr.h"
33#include "dixgrabs.h"
34
35#include "eventstr.h"
36#include "exevents.h"
37#include "exglobals.h"
38#include "inpututils.h"
39#include "eventconvert.h"
40#include "windowstr.h"
41#include "mi.h"
42
43#define TOUCH_HISTORY_SIZE 100
44
45/* If a touch queue resize is needed, the device id's bit is set. */
46static unsigned char resize_waiting[(MAXDEVICES + 7) / 8];
47
48/**
49 * Some documentation about touch points:
50 * The driver submits touch events with it's own (unique) touch point ID.
51 * The driver may re-use those IDs, the DDX doesn't care. It just passes on
52 * the data to the DIX. In the server, the driver's ID is referred to as the
53 * DDX id anyway.
54 *
55 * On a TouchBegin, we create a DDXTouchPointInfo that contains the DDX id
56 * and the client ID that this touchpoint will have. The client ID is the
57 * one visible on the protocol.
58 *
59 * TouchUpdate and TouchEnd will only be processed if there is an active
60 * touchpoint with the same DDX id.
61 *
62 * The DDXTouchPointInfo struct is stored dev->last.touches. When the event
63 * being processed, it becomes a TouchPointInfo in dev->touch-touches which
64 * contains amongst other things the sprite trace and delivery information.
65 */
66
67/**
68 * Check which devices need a bigger touch event queue and grow their
69 * last.touches by half it's current size.
70 *
71 * @param client Always the serverClient
72 * @param closure Always NULL
73 *
74 * @return Always True. If we fail to grow we probably will topple over soon
75 * anyway and re-executing this won't help.
76 */
77static Bool
78TouchResizeQueue(ClientPtr client, void *closure)
79{
80    int i;
81
82    OsBlockSignals();
83
84    /* first two ids are reserved */
85    for (i = 2; i < MAXDEVICES; i++) {
86        DeviceIntPtr dev;
87        DDXTouchPointInfoPtr tmp;
88        size_t size;
89
90        if (!BitIsOn(resize_waiting, i))
91            continue;
92
93        ClearBit(resize_waiting, i);
94
95        /* device may have disappeared by now */
96        dixLookupDevice(&dev, i, serverClient, DixWriteAccess);
97        if (!dev)
98            continue;
99
100        /* Need to grow the queue means dropping events. Grow sufficiently so we
101         * 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            for (j = dev->last.num_touches; j < size; j++)
110                TouchInitDDXTouchPoint(dev, &dev->last.touches[j]);
111            dev->last.num_touches = size;
112        }
113
114    }
115    OsReleaseSignals();
116
117    return TRUE;
118}
119
120/**
121 * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the
122 * associated DDXTouchPointInfoRec.
123 *
124 * @param dev The device to create the touch point for
125 * @param ddx_id Touch id assigned by the driver/ddx
126 * @param create Create the touchpoint if it cannot be found
127 */
128DDXTouchPointInfoPtr
129TouchFindByDDXID(DeviceIntPtr dev, uint32_t ddx_id, Bool create)
130{
131    DDXTouchPointInfoPtr ti;
132    int i;
133
134    if (!dev->touch)
135        return NULL;
136
137    for (i = 0; i < dev->last.num_touches; i++) {
138        ti = &dev->last.touches[i];
139        if (ti->active && ti->ddx_id == ddx_id)
140            return ti;
141    }
142
143    return create ? TouchBeginDDXTouch(dev, ddx_id) : NULL;
144}
145
146/**
147 * Given a unique DDX ID for a touchpoint, create a touchpoint record and
148 * return it.
149 *
150 * If no other touch points are active, mark new touchpoint for pointer
151 * emulation.
152 *
153 * Returns NULL on failure (i.e. if another touch with that ID is already active,
154 * allocation failure).
155 */
156DDXTouchPointInfoPtr
157TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id)
158{
159    static int next_client_id = 1;
160    int i;
161    TouchClassPtr t = dev->touch;
162    DDXTouchPointInfoPtr ti = NULL;
163    Bool emulate_pointer;
164
165    if (!t)
166        return NULL;
167
168    emulate_pointer = (t->mode == XIDirectTouch);
169
170    /* Look for another active touchpoint with the same DDX ID. DDX
171     * touchpoints must be unique. */
172    if (TouchFindByDDXID(dev, ddx_id, FALSE))
173        return NULL;
174
175    for (i = 0; i < dev->last.num_touches; i++) {
176        /* Only emulate pointer events on the first touch */
177        if (dev->last.touches[i].active)
178            emulate_pointer = FALSE;
179        else if (!ti)           /* ti is now first non-active touch rec */
180            ti = &dev->last.touches[i];
181
182        if (!emulate_pointer && ti)
183            break;
184    }
185
186    if (ti) {
187        int client_id;
188
189        ti->active = TRUE;
190        ti->ddx_id = ddx_id;
191        client_id = next_client_id;
192        next_client_id++;
193        if (next_client_id == 0)
194            next_client_id = 1;
195        ti->client_id = client_id;
196        ti->emulate_pointer = emulate_pointer;
197        return ti;
198    }
199
200    /* If we get here, then we've run out of touches and we need to drop the
201     * event (we're inside the SIGIO handler here) schedule a WorkProc to
202     * grow the queue for us for next time. */
203    ErrorFSigSafe("%s: not enough space for touch events (max %u touchpoints). "
204                  "Dropping this event.\n", dev->name, dev->last.num_touches);
205
206    if (!BitIsOn(resize_waiting, dev->id)) {
207        SetBit(resize_waiting, dev->id);
208        QueueWorkProc(TouchResizeQueue, serverClient, NULL);
209    }
210
211    return NULL;
212}
213
214void
215TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti)
216{
217    TouchClassPtr t = dev->touch;
218
219    if (!t)
220        return;
221
222    ti->active = FALSE;
223}
224
225void
226TouchInitDDXTouchPoint(DeviceIntPtr dev, DDXTouchPointInfoPtr ddxtouch)
227{
228    memset(ddxtouch, 0, sizeof(*ddxtouch));
229    ddxtouch->valuators = valuator_mask_new(dev->valuator->numAxes);
230}
231
232Bool
233TouchInitTouchPoint(TouchClassPtr t, ValuatorClassPtr v, int index)
234{
235    TouchPointInfoPtr ti;
236
237    if (index >= t->num_touches)
238        return FALSE;
239    ti = &t->touches[index];
240
241    memset(ti, 0, sizeof(*ti));
242
243    ti->valuators = valuator_mask_new(v->numAxes);
244    if (!ti->valuators)
245        return FALSE;
246
247    ti->sprite.spriteTrace = calloc(32, sizeof(*ti->sprite.spriteTrace));
248    if (!ti->sprite.spriteTrace) {
249        valuator_mask_free(&ti->valuators);
250        return FALSE;
251    }
252    ti->sprite.spriteTraceSize = 32;
253    ti->sprite.spriteTrace[0] = screenInfo.screens[0]->root;
254    ti->sprite.hot.pScreen = screenInfo.screens[0];
255    ti->sprite.hotPhys.pScreen = screenInfo.screens[0];
256
257    ti->client_id = -1;
258
259    return TRUE;
260}
261
262void
263TouchFreeTouchPoint(DeviceIntPtr device, int index)
264{
265    TouchPointInfoPtr ti;
266    int i;
267
268    if (!device->touch || index >= device->touch->num_touches)
269        return;
270    ti = &device->touch->touches[index];
271
272    if (ti->active)
273        TouchEndTouch(device, ti);
274
275    for (i = 0; i < ti->num_listeners; i++)
276        TouchRemoveListener(ti, ti->listeners[0].listener);
277
278    valuator_mask_free(&ti->valuators);
279    free(ti->sprite.spriteTrace);
280    ti->sprite.spriteTrace = NULL;
281    free(ti->listeners);
282    ti->listeners = NULL;
283    free(ti->history);
284    ti->history = NULL;
285    ti->history_size = 0;
286    ti->history_elements = 0;
287}
288
289/**
290 * Given a client-facing ID (e.g. DeviceEvent::detail.touch), find the
291 * associated TouchPointInfoRec.
292 */
293TouchPointInfoPtr
294TouchFindByClientID(DeviceIntPtr dev, uint32_t client_id)
295{
296    TouchClassPtr t = dev->touch;
297    TouchPointInfoPtr ti;
298    int i;
299
300    if (!t)
301        return NULL;
302
303    for (i = 0; i < t->num_touches; i++) {
304        ti = &t->touches[i];
305        if (ti->active && ti->client_id == client_id)
306            return ti;
307    }
308
309    return NULL;
310}
311
312/**
313 * Given a unique ID for a touchpoint, create a touchpoint record in the
314 * server.
315 *
316 * Returns NULL on failure (i.e. if another touch with that ID is already active,
317 * allocation failure).
318 */
319TouchPointInfoPtr
320TouchBeginTouch(DeviceIntPtr dev, int sourceid, uint32_t touchid,
321                Bool emulate_pointer)
322{
323    int i;
324    TouchClassPtr t = dev->touch;
325    TouchPointInfoPtr ti;
326    void *tmp;
327
328    if (!t)
329        return NULL;
330
331    /* Look for another active touchpoint with the same client ID.  It's
332     * technically legitimate for a touchpoint to still exist with the same
333     * ID but only once the 32 bits wrap over and you've used up 4 billion
334     * touch ids without lifting that one finger off once. In which case
335     * you deserve a medal or something, but not error handling code. */
336    if (TouchFindByClientID(dev, touchid))
337        return NULL;
338
339 try_find_touch:
340    for (i = 0; i < t->num_touches; i++) {
341        ti = &t->touches[i];
342        if (!ti->active) {
343            ti->active = TRUE;
344            ti->client_id = touchid;
345            ti->sourceid = sourceid;
346            ti->emulate_pointer = emulate_pointer;
347            return ti;
348        }
349    }
350
351    /* If we get here, then we've run out of touches: enlarge dev->touch and
352     * try again. */
353    tmp = reallocarray(t->touches, t->num_touches + 1, sizeof(*ti));
354    if (tmp) {
355        t->touches = tmp;
356        t->num_touches++;
357        if (TouchInitTouchPoint(t, dev->valuator, t->num_touches - 1))
358            goto try_find_touch;
359    }
360
361    return NULL;
362}
363
364/**
365 * Releases a touchpoint for use: this must only be called after all events
366 * related to that touchpoint have been sent and finalised.  Called from
367 * ProcessTouchEvent and friends.  Not by you.
368 */
369void
370TouchEndTouch(DeviceIntPtr dev, TouchPointInfoPtr ti)
371{
372    int i;
373
374    if (ti->emulate_pointer) {
375        GrabPtr grab;
376
377        if ((grab = dev->deviceGrab.grab)) {
378            if (dev->deviceGrab.fromPassiveGrab &&
379                !dev->button->buttonsDown &&
380                !dev->touch->buttonsDown && GrabIsPointerGrab(grab))
381                (*dev->deviceGrab.DeactivateGrab) (dev);
382        }
383    }
384
385    for (i = 0; i < ti->num_listeners; i++)
386        TouchRemoveListener(ti, ti->listeners[0].listener);
387
388    ti->active = FALSE;
389    ti->pending_finish = FALSE;
390    ti->sprite.spriteTraceGood = 0;
391    free(ti->listeners);
392    ti->listeners = NULL;
393    ti->num_listeners = 0;
394    ti->num_grabs = 0;
395    ti->client_id = 0;
396
397    TouchEventHistoryFree(ti);
398
399    valuator_mask_zero(ti->valuators);
400}
401
402/**
403 * Allocate the event history for this touch pointer. Calling this on a
404 * touchpoint that already has an event history does nothing but counts as
405 * as success.
406 *
407 * @return TRUE on success, FALSE on allocation errors
408 */
409Bool
410TouchEventHistoryAllocate(TouchPointInfoPtr ti)
411{
412    if (ti->history)
413        return TRUE;
414
415    ti->history = calloc(TOUCH_HISTORY_SIZE, sizeof(*ti->history));
416    ti->history_elements = 0;
417    if (ti->history)
418        ti->history_size = TOUCH_HISTORY_SIZE;
419    return ti->history != NULL;
420}
421
422void
423TouchEventHistoryFree(TouchPointInfoPtr ti)
424{
425    free(ti->history);
426    ti->history = NULL;
427    ti->history_size = 0;
428    ti->history_elements = 0;
429}
430
431/**
432 * Store the given event on the event history (if one exists)
433 * A touch event history consists of one TouchBegin and several TouchUpdate
434 * events (if applicable) but no TouchEnd event.
435 * If more than one TouchBegin is pushed onto the stack, the push is
436 * ignored, calling this function multiple times for the TouchBegin is
437 * valid.
438 */
439void
440TouchEventHistoryPush(TouchPointInfoPtr ti, const DeviceEvent *ev)
441{
442    if (!ti->history)
443        return;
444
445    switch (ev->type) {
446    case ET_TouchBegin:
447        /* don't store the same touchbegin twice */
448        if (ti->history_elements > 0)
449            return;
450        break;
451    case ET_TouchUpdate:
452        break;
453    case ET_TouchEnd:
454        return;                 /* no TouchEnd events in the history */
455    default:
456        return;
457    }
458
459    /* We only store real events in the history */
460    if (ev->flags & (TOUCH_CLIENT_ID | TOUCH_REPLAYING))
461        return;
462
463    ti->history[ti->history_elements++] = *ev;
464    /* 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
472void
473TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource)
474{
475    int i;
476
477    if (!ti->history)
478        return;
479
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        ev->resource = resource;
487        /* FIXME:
488           We're replaying ti->history which contains the TouchBegin +
489           all TouchUpdates for ti. This needs to be passed on to the next
490           listener. If that is a touch listener, everything is dandy.
491           If the TouchBegin however triggers a sync passive grab, the
492           TouchUpdate events must be sent to EnqueueEvent so the events end
493           up in syncEvents.pending to be forwarded correctly in a
494           subsequent ComputeFreeze().
495
496           However, if we just send them to EnqueueEvent the sync'ing device
497           prevents handling of touch events for ownership listeners who
498           want the events right here, right now.
499         */
500        dev->public.processInputProc((InternalEvent*)ev, dev);
501    }
502}
503
504void
505TouchDeliverDeviceClassesChangedEvent(TouchPointInfoPtr ti, Time time,
506                                      XID resource)
507{
508    DeviceIntPtr dev;
509    int num_events = 0;
510    InternalEvent dcce;
511
512    dixLookupDevice(&dev, ti->sourceid, serverClient, DixWriteAccess);
513
514    if (!dev)
515        return;
516
517    /* UpdateFromMaster generates at most one event */
518    UpdateFromMaster(&dcce, dev, DEVCHANGE_POINTER_EVENT, &num_events);
519    BUG_WARN(num_events > 1);
520
521    if (num_events) {
522        dcce.any.time = time;
523        /* FIXME: This doesn't do anything */
524        dev->public.processInputProc(&dcce, dev);
525    }
526}
527
528Bool
529TouchBuildDependentSpriteTrace(DeviceIntPtr dev, SpritePtr sprite)
530{
531    int i;
532    TouchClassPtr t = dev->touch;
533    WindowPtr *trace;
534    SpritePtr srcsprite;
535
536    /* All touches should have the same sprite trace, so find and reuse an
537     * existing touch's sprite if possible, else use the device's sprite. */
538    for (i = 0; i < t->num_touches; i++)
539        if (!t->touches[i].pending_finish &&
540            t->touches[i].sprite.spriteTraceGood > 0)
541            break;
542    if (i < t->num_touches)
543        srcsprite = &t->touches[i].sprite;
544    else if (dev->spriteInfo->sprite)
545        srcsprite = dev->spriteInfo->sprite;
546    else
547        return FALSE;
548
549    if (srcsprite->spriteTraceGood > sprite->spriteTraceSize) {
550        trace = reallocarray(sprite->spriteTrace,
551                             srcsprite->spriteTraceSize, sizeof(*trace));
552        if (!trace) {
553            sprite->spriteTraceGood = 0;
554            return FALSE;
555        }
556        sprite->spriteTrace = trace;
557        sprite->spriteTraceSize = srcsprite->spriteTraceGood;
558    }
559    memcpy(sprite->spriteTrace, srcsprite->spriteTrace,
560           srcsprite->spriteTraceGood * sizeof(*trace));
561    sprite->spriteTraceGood = srcsprite->spriteTraceGood;
562
563    return TRUE;
564}
565
566/**
567 * Ensure a window trace is present in ti->sprite, constructing one for
568 * TouchBegin events.
569 */
570Bool
571TouchBuildSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti,
572                 InternalEvent *ev)
573{
574    TouchClassPtr t = sourcedev->touch;
575    SpritePtr sprite = &ti->sprite;
576
577    if (t->mode == XIDirectTouch) {
578        /* Focus immediately under the touchpoint in direct touch mode.
579         * XXX: Do we need to handle crossing screens here? */
580        sprite->spriteTrace[0] =
581            sourcedev->spriteInfo->sprite->hotPhys.pScreen->root;
582        XYToWindow(sprite, ev->device_event.root_x, ev->device_event.root_y);
583    }
584    else if (!TouchBuildDependentSpriteTrace(sourcedev, sprite))
585        return FALSE;
586
587    if (sprite->spriteTraceGood <= 0)
588        return FALSE;
589
590    /* Mark which grabs/event selections we're delivering to: max one grab per
591     * window plus the bottom-most event selection, plus any active grab. */
592    ti->listeners = calloc(sprite->spriteTraceGood + 2, sizeof(*ti->listeners));
593    if (!ti->listeners) {
594        sprite->spriteTraceGood = 0;
595        return FALSE;
596    }
597    ti->num_listeners = 0;
598
599    return TRUE;
600}
601
602/**
603 * Copy the touch event into the pointer_event, switching the required
604 * fields to make it a correct pointer event.
605 *
606 * @param event The original touch event
607 * @param[in] motion_event The respective motion event
608 * @param[in] button_event The respective button event (if any)
609 *
610 * @returns The number of converted events.
611 * @retval 0 An error occured
612 * @retval 1 only the motion event is valid
613 * @retval 2 motion and button event are valid
614 */
615int
616TouchConvertToPointerEvent(const InternalEvent *event,
617                           InternalEvent *motion_event,
618                           InternalEvent *button_event)
619{
620    int ptrtype;
621    int nevents = 0;
622
623    BUG_RETURN_VAL(!event, 0);
624    BUG_RETURN_VAL(!motion_event, 0);
625
626    switch (event->any.type) {
627    case ET_TouchUpdate:
628        nevents = 1;
629        break;
630    case ET_TouchBegin:
631        nevents = 2;            /* motion + press */
632        ptrtype = ET_ButtonPress;
633        break;
634    case ET_TouchEnd:
635        nevents = 2;            /* motion + release */
636        ptrtype = ET_ButtonRelease;
637        break;
638    default:
639        BUG_WARN_MSG(1, "Invalid event type %d\n", event->any.type);
640        return 0;
641    }
642
643    BUG_WARN_MSG(!(event->device_event.flags & TOUCH_POINTER_EMULATED),
644                 "Non-emulating touch event\n");
645
646    motion_event->device_event = event->device_event;
647    motion_event->any.type = ET_Motion;
648    motion_event->device_event.detail.button = 0;
649    motion_event->device_event.flags = XIPointerEmulated;
650
651    if (nevents > 1) {
652        BUG_RETURN_VAL(!button_event, 0);
653        button_event->device_event = event->device_event;
654        button_event->any.type = ptrtype;
655        button_event->device_event.flags = XIPointerEmulated;
656        /* detail is already correct */
657    }
658
659    return nevents;
660}
661
662/**
663 * Return the corresponding pointer emulation internal event type for the given
664 * touch event or 0 if no such event type exists.
665 */
666int
667TouchGetPointerEventType(const InternalEvent *event)
668{
669    int type = 0;
670
671    switch (event->any.type) {
672    case ET_TouchBegin:
673        type = ET_ButtonPress;
674        break;
675    case ET_TouchUpdate:
676        type = ET_Motion;
677        break;
678    case ET_TouchEnd:
679        type = ET_ButtonRelease;
680        break;
681    default:
682        break;
683    }
684    return type;
685}
686
687/**
688 * @returns TRUE if the specified grab or selection is the current owner of
689 * the touch sequence.
690 */
691Bool
692TouchResourceIsOwner(TouchPointInfoPtr ti, XID resource)
693{
694    return (ti->listeners[0].listener == resource);
695}
696
697/**
698 * Add the resource to this touch's listeners.
699 */
700void
701TouchAddListener(TouchPointInfoPtr ti, XID resource, int resource_type,
702                 enum InputLevel level, enum TouchListenerType type,
703                 enum TouchListenerState state, WindowPtr window,
704                 const GrabPtr grab)
705{
706    GrabPtr g = NULL;
707
708    /* We need a copy of the grab, not the grab itself since that may be
709     * deleted by a UngrabButton request and leaves us with a dangling
710     * pointer */
711    if (grab)
712        g = AllocGrab(grab);
713
714    ti->listeners[ti->num_listeners].listener = resource;
715    ti->listeners[ti->num_listeners].resource_type = resource_type;
716    ti->listeners[ti->num_listeners].level = level;
717    ti->listeners[ti->num_listeners].state = state;
718    ti->listeners[ti->num_listeners].type = type;
719    ti->listeners[ti->num_listeners].window = window;
720    ti->listeners[ti->num_listeners].grab = g;
721    if (grab)
722        ti->num_grabs++;
723    ti->num_listeners++;
724}
725
726/**
727 * Remove the resource from this touch's listeners.
728 *
729 * @return TRUE if the resource was removed, FALSE if the resource was not
730 * in the list
731 */
732Bool
733TouchRemoveListener(TouchPointInfoPtr ti, XID resource)
734{
735    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        for (j = i; j < ti->num_listeners - 1; j++)
751            ti->listeners[j] = ti->listeners[j + 1];
752        ti->num_listeners--;
753        ti->listeners[ti->num_listeners].listener = 0;
754        ti->listeners[ti->num_listeners].state = LISTENER_AWAITING_BEGIN;
755
756        return TRUE;
757    }
758    return FALSE;
759}
760
761static void
762TouchAddGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
763                     InternalEvent *ev, GrabPtr grab)
764{
765    enum TouchListenerType type = LISTENER_GRAB;
766
767    /* FIXME: owner_events */
768
769    if (grab->grabtype == XI2) {
770        if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchOwnership))
771            TouchEventHistoryAllocate(ti);
772        if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin))
773            type = LISTENER_POINTER_GRAB;
774    }
775    else if (grab->grabtype == XI || grab->grabtype == CORE) {
776        TouchEventHistoryAllocate(ti);
777        type = LISTENER_POINTER_GRAB;
778    }
779
780    /* grab listeners are always RT_NONE since we keep the grab pointer */
781    TouchAddListener(ti, grab->resource, RT_NONE, grab->grabtype,
782                     type, LISTENER_AWAITING_BEGIN, grab->window, grab);
783}
784
785/**
786 * Add one listener if there is a grab on the given window.
787 */
788static void
789TouchAddPassiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
790                            WindowPtr win, InternalEvent *ev)
791{
792    GrabPtr grab;
793    Bool check_core = IsMaster(dev) && ti->emulate_pointer;
794
795    /* FIXME: make CheckPassiveGrabsOnWindow only trigger on TouchBegin */
796    grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core, FALSE);
797    if (!grab)
798        return;
799
800    TouchAddGrabListener(dev, ti, ev, grab);
801}
802
803static Bool
804TouchAddRegularListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
805                        WindowPtr win, InternalEvent *ev)
806{
807    InputClients *iclients = NULL;
808    OtherInputMasks *inputMasks = NULL;
809    uint16_t evtype = 0;        /* may be event type or emulated event type */
810    enum TouchListenerType type = LISTENER_REGULAR;
811    int mask;
812
813    evtype = GetXI2Type(ev->any.type);
814    mask = EventIsDeliverable(dev, ev->any.type, win);
815    if (!mask && !ti->emulate_pointer)
816        return FALSE;
817    else if (!mask) {           /* now try for pointer event */
818        mask = EventIsDeliverable(dev, TouchGetPointerEventType(ev), win);
819        if (mask) {
820            evtype = GetXI2Type(TouchGetPointerEventType(ev));
821            type = LISTENER_POINTER_REGULAR;
822        }
823    }
824    if (!mask)
825        return FALSE;
826
827    inputMasks = wOtherInputMasks(win);
828
829    if (mask & EVENT_XI2_MASK) {
830        nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
831            if (!xi2mask_isset(iclients->xi2mask, dev, evtype))
832                continue;
833
834            if (!xi2mask_isset(iclients->xi2mask, dev, XI_TouchOwnership))
835                TouchEventHistoryAllocate(ti);
836
837            TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI2,
838                             type, LISTENER_AWAITING_BEGIN, win, NULL);
839            return TRUE;
840        }
841    }
842
843    if (mask & EVENT_XI1_MASK) {
844        int xitype = GetXIType(TouchGetPointerEventType(ev));
845        Mask xi_filter = event_get_filter_from_type(dev, xitype);
846
847        nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
848            if (!(iclients->mask[dev->id] & xi_filter))
849                continue;
850
851            TouchEventHistoryAllocate(ti);
852            TouchAddListener(ti, iclients->resource, RT_INPUTCLIENT, XI,
853                             LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN,
854                             win, NULL);
855            return TRUE;
856        }
857    }
858
859    if (mask & EVENT_CORE_MASK) {
860        int coretype = GetCoreType(TouchGetPointerEventType(ev));
861        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            return TRUE;
871        }
872
873        /* all others */
874        nt_list_for_each_entry(oclients, wOtherClients(win), next) {
875            if (!(oclients->mask & core_filter))
876                continue;
877
878            TouchEventHistoryAllocate(ti);
879            TouchAddListener(ti, oclients->resource, RT_OTHERCLIENT, CORE,
880                             type, LISTENER_AWAITING_BEGIN, win, NULL);
881            return TRUE;
882        }
883    }
884
885    return FALSE;
886}
887
888static void
889TouchAddActiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti,
890                           InternalEvent *ev, GrabPtr grab)
891{
892    if (!ti->emulate_pointer &&
893        (grab->grabtype == CORE || grab->grabtype == XI))
894        return;
895
896    if (!ti->emulate_pointer &&
897        grab->grabtype == XI2 &&
898        !xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin))
899        return;
900
901    TouchAddGrabListener(dev, ti, ev, grab);
902}
903
904void
905TouchSetupListeners(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev)
906{
907    int i;
908    SpritePtr sprite = &ti->sprite;
909    WindowPtr win;
910
911    if (dev->deviceGrab.grab && !dev->deviceGrab.fromPassiveGrab)
912        TouchAddActiveGrabListener(dev, ti, ev, dev->deviceGrab.grab);
913
914    /* We set up an active touch listener for existing touches, but not any
915     * passive grab or regular listeners. */
916    if (ev->any.type != ET_TouchBegin)
917        return;
918
919    /* First, find all grabbing clients from the root window down
920     * to the deepest child window. */
921    for (i = 0; i < sprite->spriteTraceGood; i++) {
922        win = sprite->spriteTrace[i];
923        TouchAddPassiveGrabListener(dev, ti, win, ev);
924    }
925
926    /* Find the first client with an applicable event selection,
927     * going from deepest child window back up to the root window. */
928    for (i = sprite->spriteTraceGood - 1; i >= 0; i--) {
929        Bool delivered;
930
931        win = sprite->spriteTrace[i];
932        delivered = TouchAddRegularListener(dev, ti, win, ev);
933        if (delivered)
934            return;
935    }
936}
937
938/**
939 * Remove the touch pointer grab from the device. Called from
940 * DeactivatePointerGrab()
941 */
942void
943TouchRemovePointerGrab(DeviceIntPtr dev)
944{
945    TouchPointInfoPtr ti;
946    GrabPtr grab;
947    DeviceEvent *ev;
948
949    if (!dev->touch)
950        return;
951
952    grab = dev->deviceGrab.grab;
953    if (!grab)
954        return;
955
956    ev = dev->deviceGrab.sync.event;
957    if (!IsTouchEvent((InternalEvent *) ev))
958        return;
959
960    ti = TouchFindByClientID(dev, ev->touchid);
961    if (!ti)
962        return;
963
964    /* FIXME: missing a bit of code here... */
965}
966
967/* As touch grabs don't turn into active grabs with their own resources, we
968 * need to walk all the touches and remove this grab from any delivery
969 * lists. */
970void
971TouchListenerGone(XID resource)
972{
973    TouchPointInfoPtr ti;
974    DeviceIntPtr dev;
975    InternalEvent *events = InitEventList(GetMaximumEventsNum());
976    int i, j, k, nev;
977
978    if (!events)
979        FatalError("TouchListenerGone: couldn't allocate events\n");
980
981    for (dev = inputInfo.devices; dev; dev = dev->next) {
982        if (!dev->touch)
983            continue;
984
985        for (i = 0; i < dev->touch->num_touches; i++) {
986            ti = &dev->touch->touches[i];
987            if (!ti->active)
988                continue;
989
990            for (j = 0; j < ti->num_listeners; j++) {
991                if (CLIENT_BITS(ti->listeners[j].listener) != resource)
992                    continue;
993
994                nev = GetTouchOwnershipEvents(events, dev, ti, XIRejectTouch,
995                                              ti->listeners[j].listener, 0);
996                for (k = 0; k < nev; k++)
997                    mieqProcessDeviceEvent(dev, events + k, NULL);
998
999                break;
1000            }
1001        }
1002    }
1003
1004    FreeEventList(events, GetMaximumEventsNum());
1005}
1006
1007int
1008TouchListenerAcceptReject(DeviceIntPtr dev, TouchPointInfoPtr ti, int listener,
1009                          int mode)
1010{
1011    InternalEvent *events;
1012    int nev;
1013    int i;
1014
1015    BUG_RETURN_VAL(listener < 0, BadMatch);
1016    BUG_RETURN_VAL(listener >= ti->num_listeners, BadMatch);
1017
1018    if (listener > 0) {
1019        if (mode == XIRejectTouch)
1020            TouchRejected(dev, ti, ti->listeners[listener].listener, NULL);
1021        else
1022            ti->listeners[listener].state = LISTENER_EARLY_ACCEPT;
1023
1024        return Success;
1025    }
1026
1027    events = InitEventList(GetMaximumEventsNum());
1028    BUG_RETURN_VAL_MSG(!events, BadAlloc, "Failed to allocate touch ownership events\n");
1029
1030    nev = GetTouchOwnershipEvents(events, dev, ti, mode,
1031                                  ti->listeners[0].listener, 0);
1032    BUG_WARN_MSG(nev == 0, "Failed to get touch ownership events\n");
1033
1034    for (i = 0; i < nev; i++)
1035        mieqProcessDeviceEvent(dev, events + i, NULL);
1036
1037    FreeEventList(events, GetMaximumEventsNum());
1038
1039    return nev ? Success : BadMatch;
1040}
1041
1042int
1043TouchAcceptReject(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        *error = dev->id;
1051        return BadDevice;
1052    }
1053
1054    ti = TouchFindByClientID(dev, touchid);
1055    if (!ti) {
1056        *error = touchid;
1057        return BadValue;
1058    }
1059
1060    for (i = 0; i < ti->num_listeners; i++) {
1061        if (CLIENT_ID(ti->listeners[i].listener) == client->index &&
1062            ti->listeners[i].window->drawable.id == grab_window)
1063            break;
1064    }
1065    if (i == ti->num_listeners)
1066        return BadAccess;
1067
1068    return TouchListenerAcceptReject(dev, ti, i, mode);
1069}
1070
1071/**
1072 * End physically active touches for a device.
1073 */
1074void
1075TouchEndPhysicallyActiveTouches(DeviceIntPtr dev)
1076{
1077    InternalEvent *eventlist = InitEventList(GetMaximumEventsNum());
1078    int i;
1079
1080    OsBlockSignals();
1081    mieqProcessInputEvents();
1082    for (i = 0; i < dev->last.num_touches; i++) {
1083        DDXTouchPointInfoPtr ddxti = dev->last.touches + i;
1084
1085        if (ddxti->active) {
1086            int j;
1087            int nevents = GetTouchEvents(eventlist, dev, ddxti->ddx_id,
1088                                         XI_TouchEnd, 0, NULL);
1089
1090            for (j = 0; j < nevents; j++)
1091                mieqProcessDeviceEvent(dev, eventlist + j, NULL);
1092        }
1093    }
1094    OsReleaseSignals();
1095
1096    FreeEventList(eventlist, GetMaximumEventsNum());
1097}
1098
1099/**
1100 * Generate and deliver a TouchEnd event.
1101 *
1102 * @param dev The device to deliver the event for.
1103 * @param ti The touch point record to deliver the event for.
1104 * @param flags Internal event flags. The called does not need to provide
1105 *        TOUCH_CLIENT_ID and TOUCH_POINTER_EMULATED, this function will ensure
1106 *        they are set appropriately.
1107 * @param resource The client resource to deliver to, or 0 for all clients.
1108 */
1109void
1110TouchEmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags, XID resource)
1111{
1112    InternalEvent event;
1113
1114    /* We're not processing a touch end for a frozen device */
1115    if (dev->deviceGrab.sync.frozen)
1116        return;
1117
1118    flags |= TOUCH_CLIENT_ID;
1119    if (ti->emulate_pointer)
1120        flags |= TOUCH_POINTER_EMULATED;
1121    TouchDeliverDeviceClassesChangedEvent(ti, GetTimeInMillis(), resource);
1122    GetDixTouchEnd(&event, dev, ti, flags);
1123    DeliverTouchEvents(dev, ti, &event, resource);
1124    if (ti->num_grabs == 0)
1125        UpdateDeviceState(dev, &event.device_event);
1126}
1127
1128void
1129TouchAcceptAndEnd(DeviceIntPtr dev, int touchid)
1130{
1131    TouchPointInfoPtr ti = TouchFindByClientID(dev, touchid);
1132    if (!ti)
1133        return;
1134
1135    TouchListenerAcceptReject(dev, ti, 0, XIAcceptTouch);
1136    if (ti->pending_finish)
1137        TouchEmitTouchEnd(dev, ti, 0, 0);
1138    if (ti->num_listeners <= 1)
1139        TouchEndTouch(dev, ti);
1140}
1141