summaryrefslogtreecommitdiff
path: root/Xi
diff options
context:
space:
mode:
authorPovilas Kanapickas <povilas@radix.lt>2021-05-30 13:26:42 +0300
committerPovilas Kanapickas <povilas@radix.lt>2021-05-30 13:26:42 +0300
commit5163fc8bc28ce8bc2703cddcd9f2775ebc311766 (patch)
treea2d05dcb160f9f6955751e008863c818f7698cdb /Xi
parentd3c52df16105de5ac37e196a49b173e426caf417 (diff)
Implement gesture processing logic
Diffstat (limited to 'Xi')
-rw-r--r--Xi/exevents.c174
1 files changed, 174 insertions, 0 deletions
diff --git a/Xi/exevents.c b/Xi/exevents.c
index 16acd5814..f878dd212 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -1743,6 +1743,67 @@ ProcessBarrierEvent(InternalEvent *e, DeviceIntPtr dev)
free(ev);
}
+static BOOL
+IsAnotherGestureActiveOnMaster(DeviceIntPtr dev, InternalEvent* ev)
+{
+ GestureClassPtr g = dev->gesture;
+ if (g->gesture.active && g->gesture.sourceid != ev->gesture_event.sourceid) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Processes and delivers a Gesture{Pinch,Swipe}{Begin,Update,End}.
+ *
+ * Due to having rather different delivery semantics (see the Xi 2.4 protocol
+ * spec for more information), this implements its own grab and event-selection
+ * delivery logic.
+ */
+void
+ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev)
+{
+ GestureInfoPtr gi;
+ DeviceIntPtr kbd;
+ Bool deactivateGestureGrab = FALSE;
+
+ if (!dev->gesture)
+ return;
+
+ if (IsMaster(dev) && IsAnotherGestureActiveOnMaster(dev, ev))
+ return;
+
+ if (IsGestureBeginEvent(ev))
+ gi = GestureBeginGesture(dev, ev);
+ else
+ gi = GestureFindActiveByEventType(dev, ev->any.type);
+
+ if (!gi) {
+ /* This may happen if gesture is no longer active or was never started. */
+ return;
+ }
+
+ kbd = GetMaster(dev, KEYBOARD_OR_FLOAT);
+ event_set_state_gesture(kbd, &ev->gesture_event);
+
+ if (IsGestureBeginEvent(ev))
+ GestureSetupListener(dev, gi, ev);
+
+ if (IsGestureEndEvent(ev) &&
+ dev->deviceGrab.grab &&
+ dev->deviceGrab.fromPassiveGrab &&
+ GrabIsGestureGrab(dev->deviceGrab.grab))
+ deactivateGestureGrab = TRUE;
+
+ DeliverGestureEventToOwner(dev, gi, ev);
+
+ if (IsGestureEndEvent(ev))
+ GestureEndGesture(gi);
+
+ if (deactivateGestureGrab)
+ (*dev->deviceGrab.DeactivateGrab) (dev);
+}
+
/**
* Process DeviceEvents and DeviceChangedEvents.
*/
@@ -1937,6 +1998,14 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
case ET_BarrierLeave:
ProcessBarrierEvent(ev, device);
break;
+ case ET_GesturePinchBegin:
+ case ET_GesturePinchUpdate:
+ case ET_GesturePinchEnd:
+ case ET_GestureSwipeBegin:
+ case ET_GestureSwipeUpdate:
+ case ET_GestureSwipeEnd:
+ ProcessGestureEvent(ev, device);
+ break;
default:
ProcessDeviceEvent(ev, device);
break;
@@ -2141,6 +2210,111 @@ DeliverTouchEvents(DeviceIntPtr dev, TouchPointInfoPtr ti,
}
}
+/**
+ * Attempts to deliver a gesture event to the given client.
+ */
+static Bool
+DeliverOneGestureEvent(ClientPtr client, DeviceIntPtr dev, GestureInfoPtr gi,
+ GrabPtr grab, WindowPtr win, InternalEvent *ev)
+{
+ int err;
+ xEvent *xi2;
+ Mask filter;
+ Window child = DeepestSpriteWin(&gi->sprite)->drawable.id;
+
+ /* If we fail here, we're going to leave a client hanging. */
+ err = EventToXI2(ev, &xi2);
+ if (err != Success)
+ FatalError("[Xi] %s: XI2 conversion failed in %s"
+ " (%d)\n", dev->name, __func__, err);
+
+ FixUpEventFromWindow(&gi->sprite, xi2, win, child, FALSE);
+ filter = GetEventFilter(dev, xi2);
+ if (XaceHook(XACE_RECEIVE_ACCESS, client, win, xi2, 1) != Success)
+ return FALSE;
+ err = TryClientEvents(client, dev, xi2, 1, filter, filter, NullGrab);
+ free(xi2);
+
+ /* Returning the value from TryClientEvents isn't useful, since all our
+ * resource-gone cleanups will update the delivery list anyway. */
+ return TRUE;
+}
+
+/**
+ * Given a gesture event and a potential listener, retrieve info needed for processing the event.
+ *
+ * @param dev The device generating the gesture event.
+ * @param ev The gesture event to process.
+ * @param listener The gesture event listener that may receive the gesture event.
+ * @param[out] client The client that should receive the gesture event.
+ * @param[out] win The window to deliver the event on.
+ * @param[out] grab The grab to deliver the event through, if any.
+ * @return TRUE if an event should be delivered to the listener, FALSE
+ * otherwise.
+ */
+static Bool
+RetrieveGestureDeliveryData(DeviceIntPtr dev, InternalEvent *ev, GestureListener* listener,
+ ClientPtr *client, WindowPtr *win, GrabPtr *grab)
+{
+ int rc;
+ int evtype;
+ InputClients *iclients = NULL;
+ *grab = NULL;
+
+ if (listener->type == GESTURE_LISTENER_GRAB ||
+ listener->type == GESTURE_LISTENER_NONGESTURE_GRAB) {
+ *grab = listener->grab;
+
+ BUG_RETURN_VAL(!*grab, FALSE);
+
+ *client = rClient(*grab);
+ *win = (*grab)->window;
+ }
+ else {
+ rc = dixLookupResourceByType((void **) win, listener->listener, listener->resource_type,
+ serverClient, DixSendAccess);
+ if (rc != Success)
+ return FALSE;
+
+ /* note that we only will have XI2 listeners as
+ listener->type == GESTURE_LISTENER_REGULAR */
+ evtype = GetXI2Type(ev->any.type);
+
+ nt_list_for_each_entry(iclients, wOtherInputMasks(*win)->inputClients, next)
+ if (xi2mask_isset(iclients->xi2mask, dev, evtype))
+ break;
+
+ BUG_RETURN_VAL(!iclients, FALSE);
+
+ *client = rClient(iclients);
+ }
+
+ return TRUE;
+}
+
+/**
+ * Delivers a gesture to the owner, if possible and needed. Returns whether
+ * an event was delivered.
+ */
+Bool
+DeliverGestureEventToOwner(DeviceIntPtr dev, GestureInfoPtr gi, InternalEvent *ev)
+{
+ GrabPtr grab = NULL;
+ ClientPtr client;
+ WindowPtr win;
+
+ if (!gi->has_listener || gi->listener.type == GESTURE_LISTENER_NONGESTURE_GRAB) {
+ return 0;
+ }
+
+ if (!RetrieveGestureDeliveryData(dev, ev, &gi->listener, &client, &win, &grab))
+ return 0;
+
+ ev->gesture_event.deviceid = dev->id;
+
+ return DeliverOneGestureEvent(client, dev, gi, grab, win, ev);
+}
+
int
InitProximityClassDeviceStruct(DeviceIntPtr dev)
{