summaryrefslogtreecommitdiff
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
parentd3c52df16105de5ac37e196a49b173e426caf417 (diff)
Implement gesture processing logic
-rw-r--r--Xi/exevents.c174
-rw-r--r--dix/devices.c1
-rw-r--r--dix/dispatch.c1
-rw-r--r--dix/events.c60
-rw-r--r--dix/gestures.c305
-rw-r--r--dix/inpututils.c18
-rw-r--r--include/dix.h4
-rw-r--r--include/input.h15
-rw-r--r--include/inpututils.h1
-rw-r--r--mi/mieq.c8
10 files changed, 587 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)
{
diff --git a/dix/devices.c b/dix/devices.c
index 29f3051aa..5bf956ead 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -458,6 +458,7 @@ DisableDevice(DeviceIntPtr dev, BOOL sendevent)
return FALSE;
TouchEndPhysicallyActiveTouches(dev);
+ GestureEndActiveGestures(dev);
ReleaseButtonsAndKeys(dev);
SyncRemoveDeviceIdleTime(dev->idle_counter);
dev->idle_counter = NULL;
diff --git a/dix/dispatch.c b/dix/dispatch.c
index ba01de6cf..083553610 100644
--- a/dix/dispatch.c
+++ b/dix/dispatch.c
@@ -3487,6 +3487,7 @@ CloseDownClient(ClientPtr client)
CallCallbacks((&ClientStateCallback), (void *) &clientinfo);
}
TouchListenerGone(client->clientAsMask);
+ GestureListenerGone(client->clientAsMask);
FreeClientResources(client);
/* Disable client ID tracking. This must be done after
* ClientStateCallback. */
diff --git a/dix/events.c b/dix/events.c
index e2455a4bb..d29868ef0 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -1328,6 +1328,15 @@ ComputeFreezes(void)
TouchListenerAcceptReject(replayDev, ti, 0, XIRejectTouch);
}
+ else if (IsGestureEvent(event)) {
+ GestureInfoPtr gi =
+ GestureFindActiveByEventType(replayDev, event->any.type);
+ if (gi) {
+ GestureEmitGestureEndToOwner(replayDev, gi);
+ GestureEndGesture(gi);
+ }
+ ProcessGestureEvent(event, replayDev);
+ }
else {
WindowPtr w = XYToWindow(replayDev->spriteInfo->sprite,
event->device_event.root_x,
@@ -1510,6 +1519,46 @@ UpdateTouchesForGrab(DeviceIntPtr mouse)
}
/**
+ * Update gesture records when an explicit grab is activated. Any gestures owned
+ * by the grabbing client are updated so the listener state reflects the new
+ * grab.
+ */
+static void
+UpdateGesturesForGrab(DeviceIntPtr mouse)
+{
+ if (!mouse->gesture || mouse->deviceGrab.fromPassiveGrab)
+ return;
+
+ GestureInfoPtr gi = &mouse->gesture->gesture;
+ GestureListener *listener = &gi->listener;
+ GrabPtr grab = mouse->deviceGrab.grab;
+
+ if (gi->active && CLIENT_BITS(listener->listener) == grab->resource) {
+ if (grab->grabtype == CORE || grab->grabtype == XI ||
+ !xi2mask_isset(grab->xi2mask, mouse, GetXI2Type(gi->type))) {
+
+ if (listener->type == GESTURE_LISTENER_REGULAR) {
+ /* if the listener already got any events relating to the gesture, we must send
+ a gesture end because the grab overrides the previous listener and won't
+ itself send any gesture events.
+ */
+ GestureEmitGestureEndToOwner(mouse, gi);
+ }
+ listener->type = GESTURE_LISTENER_NONGESTURE_GRAB;
+ } else {
+ listener->type = GESTURE_LISTENER_GRAB;
+ }
+
+ listener->listener = grab->resource;
+ listener->window = grab->window;
+
+ if (listener->grab)
+ FreeGrab(listener->grab);
+ listener->grab = AllocGrab(grab);
+ }
+}
+
+/**
* Activate a pointer grab on the given device. A pointer grab will cause all
* core pointer events of this device to be delivered to the grabbing client only.
* No other device will send core events to the grab client while the grab is
@@ -1559,6 +1608,7 @@ ActivatePointerGrab(DeviceIntPtr mouse, GrabPtr grab,
grabinfo->implicitGrab = autoGrab & ImplicitGrabMask;
PostNewCursor(mouse);
UpdateTouchesForGrab(mouse);
+ UpdateGesturesForGrab(mouse);
CheckGrabForSyncs(mouse, (Bool) grab->pointerMode,
(Bool) grab->keyboardMode);
if (oldgrab)
@@ -1614,6 +1664,16 @@ DeactivatePointerGrab(DeviceIntPtr mouse)
if (dev->deviceGrab.sync.other == grab)
dev->deviceGrab.sync.other = NullGrab;
}
+
+ /* in case of explicit gesture grab, send end event to the grab client */
+ if (!wasPassive && mouse->gesture) {
+ GestureInfoPtr gi = &mouse->gesture->gesture;
+ if (gi->active && GestureResourceIsOwner(gi, grab_resource)) {
+ GestureEmitGestureEndToOwner(mouse, gi);
+ GestureEndGesture(gi);
+ }
+ }
+
DoEnterLeaveEvents(mouse, mouse->id, grab->window,
mouse->spriteInfo->sprite->win, NotifyUngrab);
if (grab->confineTo)
diff --git a/dix/gestures.c b/dix/gestures.c
index 7e4057deb..593a4a67f 100644
--- a/dix/gestures.c
+++ b/dix/gestures.c
@@ -39,6 +39,8 @@
#include "windowstr.h"
#include "mi.h"
+#define GESTURE_HISTORY_SIZE 100
+
Bool
GestureInitGestureInfo(GestureInfoPtr gi)
{
@@ -55,3 +57,306 @@ GestureInitGestureInfo(GestureInfoPtr gi)
return TRUE;
}
+
+/**
+ * Given an event type returns the associated gesture event info.
+ */
+GestureInfoPtr
+GestureFindActiveByEventType(DeviceIntPtr dev, int type)
+{
+ GestureClassPtr g = dev->gesture;
+ enum EventType type_to_expect = GestureTypeToBegin(type);
+
+ if (!g || type_to_expect == 0 || !g->gesture.active ||
+ g->gesture.type != type_to_expect) {
+ return NULL;
+ }
+
+ return &g->gesture;
+}
+
+/**
+ * Sets up gesture info for a new gesture. Returns NULL on failure.
+ */
+GestureInfoPtr
+GestureBeginGesture(DeviceIntPtr dev, InternalEvent *ev)
+{
+ GestureClassPtr g = dev->gesture;
+ enum EventType gesture_type = GestureTypeToBegin(ev->any.type);
+
+ /* Note that we ignore begin events when an existing gesture is active */
+ if (!g || gesture_type == 0 || g->gesture.active)
+ return NULL;
+
+ g->gesture.type = gesture_type;
+
+ if (!GestureBuildSprite(dev, &g->gesture))
+ return NULL;
+
+ g->gesture.active = TRUE;
+ g->gesture.num_touches = ev->gesture_event.num_touches;
+ g->gesture.sourceid = ev->gesture_event.sourceid;
+ g->gesture.has_listener = FALSE;
+ return &g->gesture;
+}
+
+/**
+ * Releases a gesture: this must only be called after all events
+ * related to that gesture have been sent and finalised.
+ */
+void
+GestureEndGesture(GestureInfoPtr gi)
+{
+ if (gi->has_listener) {
+ if (gi->listener.grab) {
+ FreeGrab(gi->listener.grab);
+ gi->listener.grab = NULL;
+ }
+ gi->listener.listener = 0;
+ gi->has_listener = FALSE;
+ }
+
+ gi->active = FALSE;
+ gi->num_touches = 0;
+ gi->sprite.spriteTraceGood = 0;
+}
+
+/**
+ * Ensure a window trace is present in gi->sprite, constructing one for
+ * Gesture{Pinch,Swipe}Begin events.
+ */
+Bool
+GestureBuildSprite(DeviceIntPtr sourcedev, GestureInfoPtr gi)
+{
+ SpritePtr sprite = &gi->sprite;
+
+ if (!sourcedev->spriteInfo->sprite)
+ return FALSE;
+
+ if (!CopySprite(sourcedev->spriteInfo->sprite, sprite))
+ return FALSE;
+
+ if (sprite->spriteTraceGood <= 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+/**
+ * @returns TRUE if the specified grab or selection is the current owner of
+ * the gesture sequence.
+ */
+Bool
+GestureResourceIsOwner(GestureInfoPtr gi, XID resource)
+{
+ return (gi->listener.listener == resource);
+}
+
+void
+GestureAddListener(GestureInfoPtr gi, XID resource, int resource_type,
+ enum GestureListenerType type, WindowPtr window, const GrabPtr grab)
+{
+ GrabPtr g = NULL;
+
+ BUG_RETURN(gi->has_listener);
+
+ /* We need a copy of the grab, not the grab itself since that may be deleted by
+ * a UngrabButton request and leaves us with a dangling pointer */
+ if (grab)
+ g = AllocGrab(grab);
+
+ gi->listener.listener = resource;
+ gi->listener.resource_type = resource_type;
+ gi->listener.type = type;
+ gi->listener.window = window;
+ gi->listener.grab = g;
+ gi->has_listener = TRUE;
+}
+
+static void
+GestureAddGrabListener(DeviceIntPtr dev, GestureInfoPtr gi, GrabPtr grab)
+{
+ enum GestureListenerType type;
+
+ /* FIXME: owner_events */
+
+ if (grab->grabtype == XI2) {
+ if (xi2mask_isset(grab->xi2mask, dev, XI_GesturePinchBegin) ||
+ xi2mask_isset(grab->xi2mask, dev, XI_GestureSwipeBegin)) {
+ type = GESTURE_LISTENER_GRAB;
+ } else
+ type = GESTURE_LISTENER_NONGESTURE_GRAB;
+ }
+ else if (grab->grabtype == XI || grab->grabtype == CORE) {
+ type = GESTURE_LISTENER_NONGESTURE_GRAB;
+ }
+ else {
+ BUG_RETURN_MSG(1, "Unsupported grab type\n");
+ }
+
+ /* grab listeners are always RT_NONE since we keep the grab pointer */
+ GestureAddListener(gi, grab->resource, RT_NONE, type, grab->window, grab);
+}
+
+/**
+ * Add one listener if there is a grab on the given window.
+ */
+static void
+GestureAddPassiveGrabListener(DeviceIntPtr dev, GestureInfoPtr gi, WindowPtr win, InternalEvent *ev)
+{
+ Bool activate = FALSE;
+ Bool check_core = FALSE;
+
+ GrabPtr grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core,
+ activate);
+ if (!grab)
+ return;
+
+ /* We'll deliver later in gesture-specific code */
+ ActivateGrabNoDelivery(dev, grab, ev, ev);
+ GestureAddGrabListener(dev, gi, grab);
+}
+
+static void
+GestureAddRegularListener(DeviceIntPtr dev, GestureInfoPtr gi, WindowPtr win, InternalEvent *ev)
+{
+ InputClients *iclients = NULL;
+ OtherInputMasks *inputMasks = NULL;
+ uint16_t evtype = GetXI2Type(ev->any.type);
+ int mask;
+
+ mask = EventIsDeliverable(dev, ev->any.type, win);
+ if (!mask)
+ return;
+
+ inputMasks = wOtherInputMasks(win);
+
+ if (mask & EVENT_XI2_MASK) {
+ nt_list_for_each_entry(iclients, inputMasks->inputClients, next) {
+ if (!xi2mask_isset(iclients->xi2mask, dev, evtype))
+ continue;
+
+ GestureAddListener(gi, iclients->resource, RT_INPUTCLIENT,
+ GESTURE_LISTENER_REGULAR, win, NULL);
+ return;
+ }
+ }
+}
+
+void
+GestureSetupListener(DeviceIntPtr dev, GestureInfoPtr gi, InternalEvent *ev)
+{
+ int i;
+ SpritePtr sprite = &gi->sprite;
+ WindowPtr win;
+
+ /* Any current grab will consume all gesture events */
+ if (dev->deviceGrab.grab) {
+ GestureAddGrabListener(dev, gi, dev->deviceGrab.grab);
+ return;
+ }
+
+ /* Find passive grab that would be activated by this event, if any. If we're handling
+ * ReplayDevice then the search starts from the descendant of the grab window, otherwise
+ * the search starts at the root window. The search ends at deepest child window. */
+ i = 0;
+ if (syncEvents.playingEvents) {
+ while (i < dev->spriteInfo->sprite->spriteTraceGood) {
+ if (dev->spriteInfo->sprite->spriteTrace[i++] == syncEvents.replayWin)
+ break;
+ }
+ }
+
+ for (; i < sprite->spriteTraceGood; i++) {
+ win = sprite->spriteTrace[i];
+ GestureAddPassiveGrabListener(dev, gi, win, ev);
+ if (gi->has_listener)
+ return;
+ }
+
+ /* Find the first client with an applicable event selection,
+ * going from deepest child window back up to the root window. */
+ for (i = sprite->spriteTraceGood - 1; i >= 0; i--) {
+ win = sprite->spriteTrace[i];
+ GestureAddRegularListener(dev, gi, win, ev);
+ if (gi->has_listener)
+ return;
+ }
+}
+
+/* As gesture grabs don't turn into active grabs with their own resources, we
+ * need to walk all the gestures and remove this grab from listener */
+void
+GestureListenerGone(XID resource)
+{
+ GestureInfoPtr gi;
+ DeviceIntPtr dev;
+ InternalEvent *events = InitEventList(GetMaximumEventsNum());
+
+ if (!events)
+ FatalError("GestureListenerGone: couldn't allocate events\n");
+
+ for (dev = inputInfo.devices; dev; dev = dev->next) {
+ if (!dev->gesture)
+ continue;
+
+ gi = &dev->gesture->gesture;
+ if (!gi->active)
+ continue;
+
+ if (CLIENT_BITS(gi->listener.listener) == resource)
+ GestureEndGesture(gi);
+ }
+
+ FreeEventList(events, GetMaximumEventsNum());
+}
+
+/**
+ * End physically active gestures for a device.
+ */
+void
+GestureEndActiveGestures(DeviceIntPtr dev)
+{
+ GestureClassPtr g = dev->gesture;
+ InternalEvent *eventlist;
+
+ if (!g)
+ return;
+
+ eventlist = InitEventList(GetMaximumEventsNum());
+
+ input_lock();
+ mieqProcessInputEvents();
+ if (g->gesture.active) {
+ int j;
+ int type = GetXI2Type(GestureTypeToEnd(g->gesture.type));
+ int nevents = GetGestureEvents(eventlist, dev, type, g->gesture.num_touches,
+ 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+
+ for (j = 0; j < nevents; j++)
+ mieqProcessDeviceEvent(dev, eventlist + j, NULL);
+ }
+ input_unlock();
+
+ FreeEventList(eventlist, GetMaximumEventsNum());
+}
+
+/**
+ * Generate and deliver a Gesture{Pinch,Swipe}End event to the owner.
+ *
+ * @param dev The device to deliver the event for.
+ * @param gi The gesture record to deliver the event for.
+ */
+void
+GestureEmitGestureEndToOwner(DeviceIntPtr dev, GestureInfoPtr gi)
+{
+ InternalEvent event;
+ /* We're not processing a gesture end for a frozen device */
+ if (dev->deviceGrab.sync.frozen)
+ return;
+
+ DeliverDeviceClassesChangedEvent(gi->sourceid, GetTimeInMillis());
+ InitGestureEvent(&event, dev, GetTimeInMillis(), GestureTypeToEnd(gi->type),
+ 0, 0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+ DeliverGestureEventToOwner(dev, gi, &event);
+}
diff --git a/dix/inpututils.c b/dix/inpututils.c
index 6906f997b..9026f651b 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -809,6 +809,24 @@ event_set_state(DeviceIntPtr mouse, DeviceIntPtr kbd, DeviceEvent *event)
}
}
+void
+event_set_state_gesture(DeviceIntPtr kbd, GestureEvent *event)
+{
+ if (kbd && kbd->key) {
+ XkbStatePtr state= &kbd->key->xkbInfo->state;
+
+ event->mods.base = state->base_mods;
+ event->mods.latched = state->latched_mods;
+ event->mods.locked = state->locked_mods;
+ event->mods.effective = state->mods;
+
+ event->group.base = state->base_group;
+ event->group.latched = state->latched_group;
+ event->group.locked = state->locked_group;
+ event->group.effective = state->group;
+ }
+}
+
/**
* Return the event filter mask for the given device and the given core or
* XI1 protocol type.
diff --git a/include/dix.h b/include/dix.h
index 07d3607f2..432bdcc35 100644
--- a/include/dix.h
+++ b/include/dix.h
@@ -421,6 +421,10 @@ DeliverTouchEvents(DeviceIntPtr /* dev */ ,
InternalEvent * /* ev */ ,
XID /* resource */ );
+extern Bool
+DeliverGestureEventToOwner(DeviceIntPtr dev, GestureInfoPtr gi,
+ InternalEvent *ev);
+
extern void
InitializeSprite(DeviceIntPtr /* pDev */ ,
WindowPtr /* pWin */ );
diff --git a/include/input.h b/include/input.h
index c19e74969..b1aef3663 100644
--- a/include/input.h
+++ b/include/input.h
@@ -660,6 +660,21 @@ extern void TouchEmitTouchEnd(DeviceIntPtr dev, TouchPointInfoPtr ti, int flags,
extern void TouchAcceptAndEnd(DeviceIntPtr dev, int touchid);
extern Bool GestureInitGestureInfo(GestureInfoPtr gesture);
+extern GestureInfoPtr GestureBeginGesture(DeviceIntPtr dev, InternalEvent *ev);
+extern GestureInfoPtr GestureFindActiveByEventType(DeviceIntPtr dev, int type);
+extern void GestureEndGesture(GestureInfoPtr gi);
+extern Bool GestureResourceIsOwner(GestureInfoPtr gi, XID resource);
+extern void GestureAddListener(GestureInfoPtr gi, XID resource, int resource_type,
+ enum GestureListenerType type,
+ WindowPtr window, GrabPtr grab);
+extern void GestureSetupListener(DeviceIntPtr dev, GestureInfoPtr gi,
+ InternalEvent *ev);
+extern Bool GestureBuildSprite(DeviceIntPtr sourcedev, GestureInfoPtr gi);
+extern void GestureListenerGone(XID resource);
+extern void GestureEndActiveGestures(DeviceIntPtr dev);
+extern void GestureEmitGestureEndToOwner(DeviceIntPtr dev, GestureInfoPtr gi);
+extern void ProcessGestureEvent(InternalEvent *ev, DeviceIntPtr dev);
+
/* misc event helpers */
extern Mask GetEventMask(DeviceIntPtr dev, xEvent *ev, InputClientsPtr clients);
extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event);
diff --git a/include/inpututils.h b/include/inpututils.h
index 489e1d9b7..49b1c1074 100644
--- a/include/inpututils.h
+++ b/include/inpututils.h
@@ -50,6 +50,7 @@ extern void init_gesture_event(GestureEvent *event, DeviceIntPtr dev, Time ms);
extern int event_get_corestate(DeviceIntPtr mouse, DeviceIntPtr kbd);
extern void event_set_state(DeviceIntPtr mouse, DeviceIntPtr kbd,
DeviceEvent *event);
+extern void event_set_state_gesture(DeviceIntPtr kbd, GestureEvent *event);
extern Mask event_get_filter_from_type(DeviceIntPtr dev, int evtype);
extern Mask event_get_filter_from_xi2type(int evtype);
diff --git a/mi/mieq.c b/mi/mieq.c
index bcd62d572..c98d46862 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -342,6 +342,14 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent *event)
case ET_BarrierLeave:
event->barrier_event.deviceid = dev->id;
break;
+ case ET_GesturePinchBegin:
+ case ET_GesturePinchUpdate:
+ case ET_GesturePinchEnd:
+ case ET_GestureSwipeBegin:
+ case ET_GestureSwipeUpdate:
+ case ET_GestureSwipeEnd:
+ event->gesture_event.deviceid = dev->id;
+ break;
default:
ErrorF("[mi] Unknown event type (%d), cannot change id.\n",
event->any.type);