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