1/*
2 * Copyright © 2011 Collabra Ltd.
3 * Copyright © 2011 Red Hat, Inc.
4 * Copyright © 2020 Povilas Kanapickas  <povilas@radix.lt>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include "inputstr.h"
31#include "scrnintstr.h"
32#include "dixgrabs.h"
33
34#include "eventstr.h"
35#include "exevents.h"
36#include "exglobals.h"
37#include "inpututils.h"
38#include "eventconvert.h"
39#include "windowstr.h"
40#include "mi.h"
41
42#define GESTURE_HISTORY_SIZE 100
43
44Bool
45GestureInitGestureInfo(GestureInfoPtr gi)
46{
47    memset(gi, 0, sizeof(*gi));
48
49    gi->sprite.spriteTrace = calloc(32, sizeof(*gi->sprite.spriteTrace));
50    if (!gi->sprite.spriteTrace) {
51        return FALSE;
52    }
53    gi->sprite.spriteTraceSize = 32;
54    gi->sprite.spriteTrace[0] = screenInfo.screens[0]->root;
55    gi->sprite.hot.pScreen = screenInfo.screens[0];
56    gi->sprite.hotPhys.pScreen = screenInfo.screens[0];
57
58    return TRUE;
59}
60
61/**
62 * Given an event type returns the associated gesture event info.
63 */
64GestureInfoPtr
65GestureFindActiveByEventType(DeviceIntPtr dev, int type)
66{
67    GestureClassPtr g = dev->gesture;
68    enum EventType type_to_expect = GestureTypeToBegin(type);
69
70    if (!g || type_to_expect == 0 || !g->gesture.active ||
71        g->gesture.type != type_to_expect) {
72        return NULL;
73    }
74
75    return &g->gesture;
76}
77
78/**
79 * Sets up gesture info for a new gesture. Returns NULL on failure.
80 */
81GestureInfoPtr
82GestureBeginGesture(DeviceIntPtr dev, InternalEvent *ev)
83{
84    GestureClassPtr g = dev->gesture;
85    enum EventType gesture_type = GestureTypeToBegin(ev->any.type);
86
87    /* Note that we ignore begin events when an existing gesture is active */
88    if (!g || gesture_type == 0 || g->gesture.active)
89        return NULL;
90
91    g->gesture.type = gesture_type;
92
93    if (!GestureBuildSprite(dev, &g->gesture))
94        return NULL;
95
96    g->gesture.active = TRUE;
97    g->gesture.num_touches = ev->gesture_event.num_touches;
98    g->gesture.sourceid = ev->gesture_event.sourceid;
99    g->gesture.has_listener = FALSE;
100    return &g->gesture;
101}
102
103/**
104 * Releases a gesture: this must only be called after all events
105 * related to that gesture have been sent and finalised.
106 */
107void
108GestureEndGesture(GestureInfoPtr gi)
109{
110    if (gi->has_listener) {
111        if (gi->listener.grab) {
112            FreeGrab(gi->listener.grab);
113            gi->listener.grab = NULL;
114        }
115        gi->listener.listener = 0;
116        gi->has_listener = FALSE;
117    }
118
119    gi->active = FALSE;
120    gi->num_touches = 0;
121    gi->sprite.spriteTraceGood = 0;
122}
123
124/**
125 * Ensure a window trace is present in gi->sprite, constructing one for
126 * Gesture{Pinch,Swipe}Begin events.
127 */
128Bool
129GestureBuildSprite(DeviceIntPtr sourcedev, GestureInfoPtr gi)
130{
131    SpritePtr sprite = &gi->sprite;
132
133    if (!sourcedev->spriteInfo->sprite)
134        return FALSE;
135
136    if (!CopySprite(sourcedev->spriteInfo->sprite, sprite))
137        return FALSE;
138
139    if (sprite->spriteTraceGood <= 0)
140        return FALSE;
141
142    return TRUE;
143}
144
145/**
146 * @returns TRUE if the specified grab or selection is the current owner of
147 * the gesture sequence.
148 */
149Bool
150GestureResourceIsOwner(GestureInfoPtr gi, XID resource)
151{
152    return (gi->listener.listener == resource);
153}
154
155void
156GestureAddListener(GestureInfoPtr gi, XID resource, int resource_type,
157                   enum GestureListenerType type, WindowPtr window, const GrabPtr grab)
158{
159    GrabPtr g = NULL;
160
161    BUG_RETURN(gi->has_listener);
162
163    /* We need a copy of the grab, not the grab itself since that may be deleted by
164     * a UngrabButton request and leaves us with a dangling pointer */
165    if (grab)
166        g = AllocGrab(grab);
167
168    gi->listener.listener = resource;
169    gi->listener.resource_type = resource_type;
170    gi->listener.type = type;
171    gi->listener.window = window;
172    gi->listener.grab = g;
173    gi->has_listener = TRUE;
174}
175
176static void
177GestureAddGrabListener(DeviceIntPtr dev, GestureInfoPtr gi, GrabPtr grab)
178{
179    enum GestureListenerType type;
180
181    /* FIXME: owner_events */
182
183    if (grab->grabtype == XI2) {
184        if (xi2mask_isset(grab->xi2mask, dev, XI_GesturePinchBegin) ||
185            xi2mask_isset(grab->xi2mask, dev, XI_GestureSwipeBegin)) {
186            type = GESTURE_LISTENER_GRAB;
187        } else
188            type = GESTURE_LISTENER_NONGESTURE_GRAB;
189    }
190    else if (grab->grabtype == XI || grab->grabtype == CORE) {
191        type = GESTURE_LISTENER_NONGESTURE_GRAB;
192    }
193    else {
194        BUG_RETURN_MSG(1, "Unsupported grab type\n");
195    }
196
197    /* grab listeners are always RT_NONE since we keep the grab pointer */
198    GestureAddListener(gi, grab->resource, RT_NONE, type, grab->window, grab);
199}
200
201/**
202 * Add one listener if there is a grab on the given window.
203 */
204static void
205GestureAddPassiveGrabListener(DeviceIntPtr dev, GestureInfoPtr gi, WindowPtr win, InternalEvent *ev)
206{
207    Bool activate = FALSE;
208    Bool check_core = FALSE;
209
210    GrabPtr grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core,
211                                             activate);
212    if (!grab)
213        return;
214
215    /* We'll deliver later in gesture-specific code */
216    ActivateGrabNoDelivery(dev, grab, ev, ev);
217    GestureAddGrabListener(dev, gi, grab);
218}
219
220static void
221GestureAddRegularListener(DeviceIntPtr dev, GestureInfoPtr gi, WindowPtr win, InternalEvent *ev)
222{
223    InputClients *iclients = NULL;
224    OtherInputMasks *inputMasks = NULL;
225    uint16_t evtype = GetXI2Type(ev->any.type);
226    int mask;
227
228    mask = EventIsDeliverable(dev, ev->any.type, win);
229    if (!mask)
230        return;
231
232    inputMasks = wOtherInputMasks(win);
233
234    if (mask & EVENT_XI2_MASK) {
235        nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
236            if (!xi2mask_isset(iclients->xi2mask, dev, evtype))
237                continue;
238
239            GestureAddListener(gi, iclients->resource, RT_INPUTCLIENT,
240                               GESTURE_LISTENER_REGULAR, win, NULL);
241            return;
242        }
243    }
244}
245
246void
247GestureSetupListener(DeviceIntPtr dev, GestureInfoPtr gi, InternalEvent *ev)
248{
249    int i;
250    SpritePtr sprite = &gi->sprite;
251    WindowPtr win;
252
253    /* Any current grab will consume all gesture events */
254    if (dev->deviceGrab.grab) {
255        GestureAddGrabListener(dev, gi, dev->deviceGrab.grab);
256        return;
257    }
258
259    /* Find passive grab that would be activated by this event, if any. If we're handling
260     * ReplayDevice then the search starts from the descendant of the grab window, otherwise
261     * the search starts at the root window. The search ends at deepest child window. */
262    i = 0;
263    if (syncEvents.playingEvents) {
264        while (i < dev->spriteInfo->sprite->spriteTraceGood) {
265            if (dev->spriteInfo->sprite->spriteTrace[i++] == syncEvents.replayWin)
266                break;
267        }
268    }
269
270    for (; i < sprite->spriteTraceGood; i++) {
271        win = sprite->spriteTrace[i];
272        GestureAddPassiveGrabListener(dev, gi, win, ev);
273        if (gi->has_listener)
274            return;
275    }
276
277    /* Find the first client with an applicable event selection,
278     * going from deepest child window back up to the root window. */
279    for (i = sprite->spriteTraceGood - 1; i >= 0; i--) {
280        win = sprite->spriteTrace[i];
281        GestureAddRegularListener(dev, gi, win, ev);
282        if (gi->has_listener)
283            return;
284    }
285}
286
287/* As gesture grabs don't turn into active grabs with their own resources, we
288 * need to walk all the gestures and remove this grab from listener */
289void
290GestureListenerGone(XID resource)
291{
292    GestureInfoPtr gi;
293    DeviceIntPtr dev;
294    InternalEvent *events = InitEventList(GetMaximumEventsNum());
295
296    if (!events)
297        FatalError("GestureListenerGone: couldn't allocate events\n");
298
299    for (dev = inputInfo.devices; dev; dev = dev->next) {
300        if (!dev->gesture)
301            continue;
302
303        gi = &dev->gesture->gesture;
304        if (!gi->active)
305            continue;
306
307        if (CLIENT_BITS(gi->listener.listener) == resource)
308            GestureEndGesture(gi);
309    }
310
311    FreeEventList(events, GetMaximumEventsNum());
312}
313
314/**
315 * End physically active gestures for a device.
316 */
317void
318GestureEndActiveGestures(DeviceIntPtr dev)
319{
320    GestureClassPtr g = dev->gesture;
321    InternalEvent *eventlist;
322
323    if (!g)
324        return;
325
326    eventlist = InitEventList(GetMaximumEventsNum());
327
328    input_lock();
329    mieqProcessInputEvents();
330    if (g->gesture.active) {
331        int j;
332        int type = GetXI2Type(GestureTypeToEnd(g->gesture.type));
333        int nevents = GetGestureEvents(eventlist, dev, type, g->gesture.num_touches,
334                                       0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
335
336        for (j = 0; j < nevents; j++)
337            mieqProcessDeviceEvent(dev, eventlist + j, NULL);
338    }
339    input_unlock();
340
341    FreeEventList(eventlist, GetMaximumEventsNum());
342}
343
344/**
345 * Generate and deliver a Gesture{Pinch,Swipe}End event to the owner.
346 *
347 * @param dev The device to deliver the event for.
348 * @param gi The gesture record to deliver the event for.
349 */
350void
351GestureEmitGestureEndToOwner(DeviceIntPtr dev, GestureInfoPtr gi)
352{
353    InternalEvent event;
354    /* We're not processing a gesture end for a frozen device */
355    if (dev->deviceGrab.sync.frozen)
356        return;
357
358    DeliverDeviceClassesChangedEvent(gi->sourceid, GetTimeInMillis());
359    InitGestureEvent(&event, dev, GetTimeInMillis(), GestureTypeToEnd(gi->type),
360                     0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
361    DeliverGestureEventToOwner(dev, gi, &event);
362}
363