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